about summary refs log tree commit diff
path: root/src/tools
diff options
context:
space:
mode:
authorThe rustc-josh-sync Cronjob Bot <github-actions@github.com>2025-09-25 04:16:26 +0000
committerThe rustc-josh-sync Cronjob Bot <github-actions@github.com>2025-09-25 04:16:26 +0000
commitff7e7319837d5029ed4f522601debc2a7bc33274 (patch)
tree42dbcb2d8c347a2e7c97c7e4361b1c361143da0b /src/tools
parent3280c210dd6e5f26595ff4857572f8feba678992 (diff)
parentd226e7aa93425ba2090f423642341a99ab047838 (diff)
downloadrust-ff7e7319837d5029ed4f522601debc2a7bc33274.tar.gz
rust-ff7e7319837d5029ed4f522601debc2a7bc33274.zip
Merge ref 'caccb4d0368b' from rust-lang/rust
Pull recent changes from https://github.com/rust-lang/rust via Josh.

Upstream ref: caccb4d0368bd918ef6668af8e13834d07040417
Filtered ref: 0f345ed05d559bbfb754f1403b16199366cda2e0
Upstream diff: https://github.com/rust-lang/rust/compare/21a19c297d4f5a03501d92ca251bd7a17073c08a...caccb4d0368bd918ef6668af8e13834d07040417

This merge was created using https://github.com/rust-lang/josh-sync.
Diffstat (limited to 'src/tools')
-rw-r--r--src/tools/build-manifest/Cargo.toml2
-rw-r--r--src/tools/build-manifest/src/main.rs5
-rw-r--r--src/tools/bump-stage0/Cargo.toml2
-rw-r--r--src/tools/bump-stage0/src/main.rs6
m---------src/tools/cargo0
-rw-r--r--src/tools/clippy/.cargo/config.toml7
-rw-r--r--src/tools/clippy/.github/ISSUE_TEMPLATE/new_lint.yml4
-rw-r--r--src/tools/clippy/.github/PULL_REQUEST_TEMPLATE.md4
-rw-r--r--src/tools/clippy/.github/workflows/clippy_dev.yml2
-rw-r--r--src/tools/clippy/.github/workflows/clippy_mq.yml8
-rw-r--r--src/tools/clippy/.github/workflows/clippy_pr.yml2
-rw-r--r--src/tools/clippy/.github/workflows/deploy.yml4
-rw-r--r--src/tools/clippy/.github/workflows/feature_freeze.yml45
-rw-r--r--src/tools/clippy/.github/workflows/lintcheck.yml6
-rw-r--r--src/tools/clippy/.github/workflows/remark.yml2
-rw-r--r--src/tools/clippy/.gitignore3
-rw-r--r--src/tools/clippy/CHANGELOG.md154
-rw-r--r--src/tools/clippy/CONTRIBUTING.md12
-rw-r--r--src/tools/clippy/Cargo.toml6
-rw-r--r--src/tools/clippy/book/src/README.md4
-rw-r--r--src/tools/clippy/book/src/SUMMARY.md1
-rw-r--r--src/tools/clippy/book/src/continuous_integration/github_actions.md2
-rw-r--r--src/tools/clippy/book/src/development/adding_lints.md3
-rw-r--r--src/tools/clippy/book/src/development/basics.md4
-rw-r--r--src/tools/clippy/book/src/development/common_tools_writing_lints.md2
-rw-r--r--src/tools/clippy/book/src/development/feature_freeze.md55
-rw-r--r--src/tools/clippy/book/src/lint_configuration.md14
-rw-r--r--src/tools/clippy/clippy.toml6
-rw-r--r--src/tools/clippy/clippy_config/Cargo.toml2
-rw-r--r--src/tools/clippy/clippy_config/src/conf.rs8
-rw-r--r--src/tools/clippy/clippy_dev/src/dogfood.rs49
-rw-r--r--src/tools/clippy/clippy_dev/src/lib.rs7
-rw-r--r--src/tools/clippy/clippy_dev/src/lint.rs56
-rw-r--r--src/tools/clippy/clippy_dev/src/main.rs11
-rw-r--r--src/tools/clippy/clippy_dev/src/serve.rs107
-rw-r--r--src/tools/clippy/clippy_dev/src/setup/git_hook.rs6
-rw-r--r--src/tools/clippy/clippy_dev/src/setup/mod.rs20
-rw-r--r--src/tools/clippy/clippy_dev/src/setup/toolchain.rs19
-rw-r--r--src/tools/clippy/clippy_dev/src/setup/vscode.rs6
-rw-r--r--src/tools/clippy/clippy_dev/src/utils.rs62
-rw-r--r--src/tools/clippy/clippy_lints/Cargo.toml6
-rw-r--r--src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs27
-rw-r--r--src/tools/clippy/clippy_lints/src/async_yields_async.rs60
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs24
-rw-r--r--src/tools/clippy/clippy_lints/src/bool_comparison.rs179
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/as_underscore.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs30
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs30
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs29
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs43
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/char_lit_as_u8.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/mod.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs39
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs34
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/utils.rs15
-rw-r--r--src/tools/clippy/clippy_lints/src/cognitive_complexity.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/collapsible_if.rs22
-rw-r--r--src/tools/clippy/clippy_lints/src/copy_iterator.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/declared_lints.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/dereference.rs18
-rw-r--r--src/tools/clippy/clippy_lints/src/derivable_impls.rs84
-rw-r--r--src/tools/clippy/clippy_lints/src/derive.rs526
-rw-r--r--src/tools/clippy/clippy_lints/src/derive/derive_ord_xor_partial_ord.rs50
-rw-r--r--src/tools/clippy/clippy_lints/src/derive/derive_partial_eq_without_eq.rs87
-rw-r--r--src/tools/clippy/clippy_lints/src/derive/derived_hash_with_manual_eq.rs49
-rw-r--r--src/tools/clippy/clippy_lints/src/derive/expl_impl_clone_on_copy.rs65
-rw-r--r--src/tools/clippy/clippy_lints/src/derive/mod.rs215
-rw-r--r--src/tools/clippy/clippy_lints/src/derive/unsafe_derive_deserialize.rs93
-rw-r--r--src/tools/clippy/clippy_lints/src/disallowed_macros.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/doc/mod.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/duplicate_mod.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/empty_drop.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/empty_line_after.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/entry.rs67
-rw-r--r--src/tools/clippy/clippy_lints/src/equatable_if_let.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/error_impl_error.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/eta_reduction.rs54
-rw-r--r--src/tools/clippy/clippy_lints/src/excessive_nesting.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/float_literal.rs45
-rw-r--r--src/tools/clippy/clippy_lints/src/format_impl.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/from_over_into.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/from_str_radix_10.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/duplicate_underscore_argument.rs34
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/mod.rs41
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/ref_option.rs48
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs35
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/result.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/too_many_arguments.rs37
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/future_not_send.rs109
-rw-r--r--src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs32
-rw-r--r--src/tools/clippy/clippy_lints/src/impl_hash_with_borrow_str_and_bytes.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/infallible_try_from.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/instant_subtraction.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/item_name_repetitions.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/large_include_file.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/len_zero.rs63
-rw-r--r--src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.rs18
-rw-r--r--src/tools/clippy/clippy_lints/src/lifetimes.rs115
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs53
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/missing_spin_loop.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs33
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/never_loop.rs59
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_abs_diff.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_async_fn.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs26
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_let_else.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_retain.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_strip.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/map_unit_fn.rs21
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs49
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/manual_utils.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/match_bool.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/match_ref_pats.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/mod.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/needless_match.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/single_match.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/try_err.rs40
-rw-r--r--src/tools/clippy/clippy_lints/src/mem_replace.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/double_ended_iterator_last.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs75
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/filter_map.rs18
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/filter_next.rs18
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/iter_out_of_bounds.rs45
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/map_flatten.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/map_identity.rs96
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/mod.rs75
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs28
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/or_then_unwrap.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/ptr_offset_with_cast.rs82
-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/single_char_add_str.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_min_or_max.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/useless_asref.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/min_ident_chars.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/misc.rs118
-rw-r--r--src/tools/clippy/clippy_lints/src/misc_early/mod.rs57
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs18
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_doc.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_inline.rs45
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_trait_methods.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/mut_reference.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_bool.rs232
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_for_each.rs36
-rw-r--r--src/tools/clippy/clippy_lints/src/no_effect.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/non_canonical_impls.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/non_copy_const.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/non_expressive_names.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs8
-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.rs28
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/erasing_op.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/op_ref.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/panic_unimplemented.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs6
-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.rs61
-rw-r--r--src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs151
-rw-r--r--src/tools/clippy/clippy_lints/src/question_mark.rs21
-rw-r--r--src/tools/clippy/clippy_lints/src/raw_strings.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs27
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_clone.rs20
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_slicing.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/reference.rs141
-rw-r--r--src/tools/clippy/clippy_lints/src/returns.rs513
-rw-r--r--src/tools/clippy/clippy_lints/src/returns/let_and_return.rs86
-rw-r--r--src/tools/clippy/clippy_lints/src/returns/mod.rs140
-rw-r--r--src/tools/clippy/clippy_lints/src/returns/needless_return.rs269
-rw-r--r--src/tools/clippy/clippy_lints/src/returns/needless_return_with_question_mark.rs60
-rw-r--r--src/tools/clippy/clippy_lints/src/same_name_method.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/semicolon_block.rs19
-rw-r--r--src/tools/clippy/clippy_lints/src/serde_api.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/size_of_ref.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/std_instead_of_core.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/strings.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/swap.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/to_string_trait_impl.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/toplevel_ref_arg.rs119
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/eager_transmute.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs55
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/mod.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs53
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs44
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/transmute_ref_to_ref.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/useless_transmute.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/types/rc_buffer.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/unconditional_recursion.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs196
-rw-r--r--src/tools/clippy/clippy_lints/src/uninit_vec.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/unit_types/unit_cmp.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/unnecessary_semicolon.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_io_amount.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_peekable.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_unit.rs58
-rw-r--r--src/tools/clippy/clippy_lints/src/unwrap.rs73
-rw-r--r--src/tools/clippy/clippy_lints/src/unwrap_in_result.rs206
-rw-r--r--src/tools/clippy/clippy_lints/src/use_self.rs84
-rw-r--r--src/tools/clippy/clippy_lints/src/useless_conversion.rs23
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/author.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/vec.rs232
-rw-r--r--src/tools/clippy/clippy_lints/src/write.rs17
-rw-r--r--src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/zombie_processes.rs20
-rw-r--r--src/tools/clippy/clippy_lints_internal/src/derive_deserialize_allowing_unknown.rs15
-rw-r--r--src/tools/clippy/clippy_test_deps/Cargo.lock4
-rw-r--r--src/tools/clippy/clippy_utils/Cargo.toml2
-rw-r--r--src/tools/clippy/clippy_utils/README.md2
-rw-r--r--src/tools/clippy/clippy_utils/src/ast_utils/mod.rs85
-rw-r--r--src/tools/clippy/clippy_utils/src/check_proc_macro.rs173
-rw-r--r--src/tools/clippy/clippy_utils/src/diagnostics.rs15
-rw-r--r--src/tools/clippy/clippy_utils/src/higher.rs27
-rw-r--r--src/tools/clippy/clippy_utils/src/hir_utils.rs4
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs120
-rw-r--r--src/tools/clippy/clippy_utils/src/macros.rs7
-rw-r--r--src/tools/clippy/clippy_utils/src/msrvs.rs40
-rw-r--r--src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs4
-rw-r--r--src/tools/clippy/clippy_utils/src/source.rs5
-rw-r--r--src/tools/clippy/clippy_utils/src/sym.rs5
-rw-r--r--src/tools/clippy/clippy_utils/src/ty/mod.rs115
-rw-r--r--src/tools/clippy/clippy_utils/src/usage.rs8
-rw-r--r--src/tools/clippy/clippy_utils/src/visitors.rs3
-rw-r--r--src/tools/clippy/declare_clippy_lint/Cargo.toml2
-rw-r--r--src/tools/clippy/rust-toolchain.toml2
-rw-r--r--src/tools/clippy/src/driver.rs3
-rw-r--r--src/tools/clippy/tests/config-consistency.rs30
-rw-r--r--src/tools/clippy/tests/missing-test-files.rs2
-rw-r--r--src/tools/clippy/tests/no-profile-in-cargo-toml.rs37
-rw-r--r--src/tools/clippy/tests/ui-cargo/undocumented_unsafe_blocks/fail/Cargo.stderr26
-rw-r--r--src/tools/clippy/tests/ui-cargo/undocumented_unsafe_blocks/fail/Cargo.toml12
-rw-r--r--src/tools/clippy/tests/ui-cargo/undocumented_unsafe_blocks/fail/src/main.rs7
-rw-r--r--src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr4
-rw-r--r--src/tools/clippy/tests/ui-toml/excessive_precision/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/excessive_precision/excessive_precision.fixed38
-rw-r--r--src/tools/clippy/tests/ui-toml/excessive_precision/excessive_precision.rs38
-rw-r--r--src/tools/clippy/tests/ui-toml/excessive_precision/excessive_precision.stderr38
-rw-r--r--src/tools/clippy/tests/ui-toml/functions_maxlines/test.stderr35
-rw-r--r--src/tools/clippy/tests/ui-toml/ref_option/all/clippy.toml (renamed from src/tools/clippy/tests/ui/ref_option/all/clippy.toml)0
-rw-r--r--src/tools/clippy/tests/ui-toml/ref_option/private/clippy.toml (renamed from src/tools/clippy/tests/ui/ref_option/private/clippy.toml)0
-rw-r--r--src/tools/clippy/tests/ui-toml/ref_option/ref_option.all.fixed114
-rw-r--r--src/tools/clippy/tests/ui-toml/ref_option/ref_option.all.stderr (renamed from src/tools/clippy/tests/ui/ref_option/ref_option.all.stderr)68
-rw-r--r--src/tools/clippy/tests/ui-toml/ref_option/ref_option.private.fixed114
-rw-r--r--src/tools/clippy/tests/ui-toml/ref_option/ref_option.private.stderr (renamed from src/tools/clippy/tests/ui/ref_option/ref_option.private.stderr)44
-rw-r--r--src/tools/clippy/tests/ui-toml/ref_option/ref_option.rs114
-rw-r--r--src/tools/clippy/tests/ui-toml/ref_option/ref_option_traits.all.stderr (renamed from src/tools/clippy/tests/ui/ref_option/ref_option_traits.all.stderr)8
-rw-r--r--src/tools/clippy/tests/ui-toml/ref_option/ref_option_traits.private.stderr (renamed from src/tools/clippy/tests/ui/ref_option/ref_option_traits.private.stderr)4
-rw-r--r--src/tools/clippy/tests/ui-toml/ref_option/ref_option_traits.rs71
-rw-r--r--src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr3
-rw-r--r--src/tools/clippy/tests/ui/as_ptr_cast_mut.fixed38
-rw-r--r--src/tools/clippy/tests/ui/as_ptr_cast_mut.rs4
-rw-r--r--src/tools/clippy/tests/ui/as_ptr_cast_mut.stderr10
-rw-r--r--src/tools/clippy/tests/ui/as_ptr_cast_mut_unfixable.rs16
-rw-r--r--src/tools/clippy/tests/ui/as_ptr_cast_mut_unfixable.stderr11
-rw-r--r--src/tools/clippy/tests/ui/as_underscore_unfixable.rs14
-rw-r--r--src/tools/clippy/tests/ui/as_underscore_unfixable.stderr20
-rw-r--r--src/tools/clippy/tests/ui/assertions_on_result_states.fixed11
-rw-r--r--src/tools/clippy/tests/ui/assertions_on_result_states.rs11
-rw-r--r--src/tools/clippy/tests/ui/assertions_on_result_states.stderr10
-rw-r--r--src/tools/clippy/tests/ui/assign_ops.fixed39
-rw-r--r--src/tools/clippy/tests/ui/assign_ops.rs41
-rw-r--r--src/tools/clippy/tests/ui/assign_ops.stderr50
-rw-r--r--src/tools/clippy/tests/ui/async_yields_async.fixed39
-rw-r--r--src/tools/clippy/tests/ui/async_yields_async.rs39
-rw-r--r--src/tools/clippy/tests/ui/async_yields_async.stderr47
-rw-r--r--src/tools/clippy/tests/ui/bool_assert_comparison.fixed24
-rw-r--r--src/tools/clippy/tests/ui/bool_assert_comparison.rs18
-rw-r--r--src/tools/clippy/tests/ui/bool_assert_comparison.stderr122
-rw-r--r--src/tools/clippy/tests/ui/bool_comparison.fixed167
-rw-r--r--src/tools/clippy/tests/ui/bool_comparison.rs167
-rw-r--r--src/tools/clippy/tests/ui/bool_comparison.stderr154
-rw-r--r--src/tools/clippy/tests/ui/borrow_as_ptr.fixed12
-rw-r--r--src/tools/clippy/tests/ui/borrow_as_ptr.rs12
-rw-r--r--src/tools/clippy/tests/ui/borrow_as_ptr.stderr14
-rw-r--r--src/tools/clippy/tests/ui/cast.rs13
-rw-r--r--src/tools/clippy/tests/ui/cast.stderr96
-rw-r--r--src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.fixed39
-rw-r--r--src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.rs25
-rw-r--r--src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.stderr66
-rw-r--r--src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast_no_std.fixed55
-rw-r--r--src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast_no_std.rs55
-rw-r--r--src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast_no_std.stderr83
-rw-r--r--src/tools/clippy/tests/ui/char_indices_as_byte_indices.fixed1
-rw-r--r--src/tools/clippy/tests/ui/char_indices_as_byte_indices.rs1
-rw-r--r--src/tools/clippy/tests/ui/char_indices_as_byte_indices.stderr28
-rw-r--r--src/tools/clippy/tests/ui/char_lit_as_u8.fixed (renamed from src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.fixed)0
-rw-r--r--src/tools/clippy/tests/ui/char_lit_as_u8.rs9
-rw-r--r--src/tools/clippy/tests/ui/char_lit_as_u8.stderr32
-rw-r--r--src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.rs12
-rw-r--r--src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.stderr36
-rw-r--r--src/tools/clippy/tests/ui/char_lit_as_u8_unfixable.rs8
-rw-r--r--src/tools/clippy/tests/ui/char_lit_as_u8_unfixable.stderr12
-rw-r--r--src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs22
-rw-r--r--src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr38
-rw-r--r--src/tools/clippy/tests/ui/collapsible_match.rs41
-rw-r--r--src/tools/clippy/tests/ui/collapsible_match.stderr71
-rw-r--r--src/tools/clippy/tests/ui/collapsible_match2.stderr2
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-15657.rs11
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-15666.fixed6
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-15666.rs6
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-15666.stderr16
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-6255.rs2
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-6255.stderr22
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-96721.stderr2
-rw-r--r--src/tools/clippy/tests/ui/deref_addrof.fixed88
-rw-r--r--src/tools/clippy/tests/ui/deref_addrof.rs88
-rw-r--r--src/tools/clippy/tests/ui/deref_addrof.stderr56
-rw-r--r--src/tools/clippy/tests/ui/derivable_impls.fixed38
-rw-r--r--src/tools/clippy/tests/ui/derivable_impls.rs41
-rw-r--r--src/tools/clippy/tests/ui/derivable_impls.stderr47
-rw-r--r--src/tools/clippy/tests/ui/derivable_impls_derive_const.fixed25
-rw-r--r--src/tools/clippy/tests/ui/derivable_impls_derive_const.rs32
-rw-r--r--src/tools/clippy/tests/ui/derivable_impls_derive_const.stderr46
-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/double_ended_iterator_last.fixed11
-rw-r--r--src/tools/clippy/tests/ui/double_ended_iterator_last.rs11
-rw-r--r--src/tools/clippy/tests/ui/double_ended_iterator_last.stderr17
-rw-r--r--src/tools/clippy/tests/ui/double_ended_iterator_last_unfixable.rs39
-rw-r--r--src/tools/clippy/tests/ui/double_ended_iterator_last_unfixable.stderr19
-rw-r--r--src/tools/clippy/tests/ui/elidable_lifetime_names.fixed82
-rw-r--r--src/tools/clippy/tests/ui/elidable_lifetime_names.rs82
-rw-r--r--src/tools/clippy/tests/ui/elidable_lifetime_names.stderr146
-rw-r--r--src/tools/clippy/tests/ui/entry_unfixable.stderr5
-rw-r--r--src/tools/clippy/tests/ui/eta.fixed82
-rw-r--r--src/tools/clippy/tests/ui/eta.rs82
-rw-r--r--src/tools/clippy/tests/ui/eta.stderr114
-rw-r--r--src/tools/clippy/tests/ui/excessive_precision.fixed45
-rw-r--r--src/tools/clippy/tests/ui/excessive_precision.rs45
-rw-r--r--src/tools/clippy/tests/ui/excessive_precision.stderr85
-rw-r--r--src/tools/clippy/tests/ui/float_equality_without_abs.rs14
-rw-r--r--src/tools/clippy/tests/ui/float_equality_without_abs.stderr18
-rw-r--r--src/tools/clippy/tests/ui/from_str_radix_10.fixed10
-rw-r--r--src/tools/clippy/tests/ui/from_str_radix_10.rs10
-rw-r--r--src/tools/clippy/tests/ui/from_str_radix_10.stderr14
-rw-r--r--src/tools/clippy/tests/ui/functions_maxlines.rs115
-rw-r--r--src/tools/clippy/tests/ui/functions_maxlines.stderr22
-rw-r--r--src/tools/clippy/tests/ui/if_then_some_else_none.fixed41
-rw-r--r--src/tools/clippy/tests/ui/if_then_some_else_none.rs51
-rw-r--r--src/tools/clippy/tests/ui/if_then_some_else_none.stderr25
-rw-r--r--src/tools/clippy/tests/ui/infinite_loops.rs86
-rw-r--r--src/tools/clippy/tests/ui/infinite_loops.stderr34
-rw-r--r--src/tools/clippy/tests/ui/invalid_upcast_comparisons.rs12
-rw-r--r--src/tools/clippy/tests/ui/invalid_upcast_comparisons.stderr8
-rw-r--r--src/tools/clippy/tests/ui/iter_overeager_cloned.fixed12
-rw-r--r--src/tools/clippy/tests/ui/iter_overeager_cloned.rs12
-rw-r--r--src/tools/clippy/tests/ui/iter_overeager_cloned.stderr4
-rw-r--r--src/tools/clippy/tests/ui/manual_is_ascii_check.fixed4
-rw-r--r--src/tools/clippy/tests/ui/manual_is_ascii_check.rs4
-rw-r--r--src/tools/clippy/tests/ui/manual_is_ascii_check.stderr30
-rw-r--r--src/tools/clippy/tests/ui/map_identity.fixed20
-rw-r--r--src/tools/clippy/tests/ui/map_identity.rs16
-rw-r--r--src/tools/clippy/tests/ui/map_identity.stderr66
-rw-r--r--src/tools/clippy/tests/ui/match_as_ref.fixed30
-rw-r--r--src/tools/clippy/tests/ui/match_as_ref.rs30
-rw-r--r--src/tools/clippy/tests/ui/match_bool.fixed13
-rw-r--r--src/tools/clippy/tests/ui/match_bool.rs13
-rw-r--r--src/tools/clippy/tests/ui/match_ref_pats.fixed36
-rw-r--r--src/tools/clippy/tests/ui/match_ref_pats.rs36
-rw-r--r--src/tools/clippy/tests/ui/match_ref_pats.stderr10
-rw-r--r--src/tools/clippy/tests/ui/missing_inline.rs7
-rw-r--r--src/tools/clippy/tests/ui/missing_transmute_annotations_unfixable.rs29
-rw-r--r--src/tools/clippy/tests/ui/missing_transmute_annotations_unfixable.stderr36
-rw-r--r--src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs119
-rw-r--r--src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr143
-rw-r--r--src/tools/clippy/tests/ui/mut_reference.fixed170
-rw-r--r--src/tools/clippy/tests/ui/mut_reference.rs152
-rw-r--r--src/tools/clippy/tests/ui/mut_reference.stderr76
-rw-r--r--src/tools/clippy/tests/ui/needless_bool/fixable.stderr8
-rw-r--r--src/tools/clippy/tests/ui/needless_for_each_unfixable.rs19
-rw-r--r--src/tools/clippy/tests/ui/needless_for_each_unfixable.stderr38
-rw-r--r--src/tools/clippy/tests/ui/needless_range_loop.rs32
-rw-r--r--src/tools/clippy/tests/ui/needless_range_loop.stderr26
-rw-r--r--src/tools/clippy/tests/ui/needless_return.fixed7
-rw-r--r--src/tools/clippy/tests/ui/needless_return.rs7
-rw-r--r--src/tools/clippy/tests/ui/never_loop.rs24
-rw-r--r--src/tools/clippy/tests/ui/never_loop.stderr97
-rw-r--r--src/tools/clippy/tests/ui/nonminimal_bool.rs2
-rw-r--r--src/tools/clippy/tests/ui/nonminimal_bool.stderr50
-rw-r--r--src/tools/clippy/tests/ui/option_if_let_else.fixed10
-rw-r--r--src/tools/clippy/tests/ui/option_if_let_else.rs16
-rw-r--r--src/tools/clippy/tests/ui/option_if_let_else.stderr50
-rw-r--r--src/tools/clippy/tests/ui/option_map_unit_fn_fixable.fixed9
-rw-r--r--src/tools/clippy/tests/ui/option_map_unit_fn_fixable.rs9
-rw-r--r--src/tools/clippy/tests/ui/option_map_unit_fn_fixable.stderr18
-rw-r--r--src/tools/clippy/tests/ui/or_fun_call.fixed16
-rw-r--r--src/tools/clippy/tests/ui/or_fun_call.rs22
-rw-r--r--src/tools/clippy/tests/ui/or_fun_call.stderr88
-rw-r--r--src/tools/clippy/tests/ui/or_then_unwrap.fixed6
-rw-r--r--src/tools/clippy/tests/ui/or_then_unwrap.rs6
-rw-r--r--src/tools/clippy/tests/ui/or_then_unwrap.stderr10
-rw-r--r--src/tools/clippy/tests/ui/panicking_macros.rs15
-rw-r--r--src/tools/clippy/tests/ui/panicking_macros.stderr46
-rw-r--r--src/tools/clippy/tests/ui/print_literal.fixed13
-rw-r--r--src/tools/clippy/tests/ui/print_literal.rs14
-rw-r--r--src/tools/clippy/tests/ui/print_literal.stderr38
-rw-r--r--src/tools/clippy/tests/ui/ptr_arg.stderr4
-rw-r--r--src/tools/clippy/tests/ui/ptr_as_ptr.fixed14
-rw-r--r--src/tools/clippy/tests/ui/ptr_as_ptr.rs12
-rw-r--r--src/tools/clippy/tests/ui/ptr_as_ptr.stderr68
-rw-r--r--src/tools/clippy/tests/ui/ptr_cast_constness.fixed6
-rw-r--r--src/tools/clippy/tests/ui/ptr_cast_constness.rs6
-rw-r--r--src/tools/clippy/tests/ui/ptr_cast_constness.stderr8
-rw-r--r--src/tools/clippy/tests/ui/ptr_offset_with_cast.fixed22
-rw-r--r--src/tools/clippy/tests/ui/ptr_offset_with_cast.rs22
-rw-r--r--src/tools/clippy/tests/ui/ptr_offset_with_cast.stderr41
-rw-r--r--src/tools/clippy/tests/ui/question_mark.fixed14
-rw-r--r--src/tools/clippy/tests/ui/question_mark.rs26
-rw-r--r--src/tools/clippy/tests/ui/question_mark.stderr92
-rw-r--r--src/tools/clippy/tests/ui/read_zero_byte_vec.rs27
-rw-r--r--src/tools/clippy/tests/ui/read_zero_byte_vec.stderr65
-rw-r--r--src/tools/clippy/tests/ui/ref_option/ref_option.all.fixed79
-rw-r--r--src/tools/clippy/tests/ui/ref_option/ref_option.private.fixed79
-rw-r--r--src/tools/clippy/tests/ui/ref_option/ref_option.rs79
-rw-r--r--src/tools/clippy/tests/ui/ref_option/ref_option_traits.rs40
-rw-r--r--src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.fixed61
-rw-r--r--src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.stderr18
-rw-r--r--src/tools/clippy/tests/ui/semicolon_inside_block.fixed22
-rw-r--r--src/tools/clippy/tests/ui/semicolon_inside_block.rs22
-rw-r--r--src/tools/clippy/tests/ui/semicolon_inside_block.stderr8
-rw-r--r--src/tools/clippy/tests/ui/semicolon_inside_block_stmt_expr_attrs.fixed11
-rw-r--r--src/tools/clippy/tests/ui/semicolon_inside_block_stmt_expr_attrs.rs11
-rw-r--r--src/tools/clippy/tests/ui/semicolon_inside_block_stmt_expr_attrs.stderr16
-rw-r--r--src/tools/clippy/tests/ui/similar_names.rs4
-rw-r--r--src/tools/clippy/tests/ui/similar_names.stderr4
-rw-r--r--src/tools/clippy/tests/ui/std_instead_of_core.fixed7
-rw-r--r--src/tools/clippy/tests/ui/std_instead_of_core.rs7
-rw-r--r--src/tools/clippy/tests/ui/track-diagnostics-clippy.stderr2
-rw-r--r--src/tools/clippy/tests/ui/transmute.rs3
-rw-r--r--src/tools/clippy/tests/ui/transmute.stderr46
-rw-r--r--src/tools/clippy/tests/ui/transmute_ptr_to_ref.fixed39
-rw-r--r--src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs39
-rw-r--r--src/tools/clippy/tests/ui/transmute_ptr_to_ref.stderr54
-rw-r--r--src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed3
-rw-r--r--src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs3
-rw-r--r--src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr32
-rw-r--r--src/tools/clippy/tests/ui/unit_cmp.rs17
-rw-r--r--src/tools/clippy/tests/ui/unit_cmp.stderr20
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_clone.stderr12
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_operation.fixed10
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_operation.stderr10
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_semicolon.edition2021.fixed9
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_semicolon.edition2024.fixed9
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_semicolon.rs9
-rw-r--r--src/tools/clippy/tests/ui/unnested_or_patterns.fixed25
-rw-r--r--src/tools/clippy/tests/ui/unnested_or_patterns.rs25
-rw-r--r--src/tools/clippy/tests/ui/unnested_or_patterns.stderr54
-rw-r--r--src/tools/clippy/tests/ui/unused_unit.edition2021.fixed27
-rw-r--r--src/tools/clippy/tests/ui/unused_unit.edition2021.stderr6
-rw-r--r--src/tools/clippy/tests/ui/unused_unit.edition2024.fixed29
-rw-r--r--src/tools/clippy/tests/ui/unused_unit.rs29
-rw-r--r--src/tools/clippy/tests/ui/unwrap_in_result.rs70
-rw-r--r--src/tools/clippy/tests/ui/unwrap_in_result.stderr132
-rw-r--r--src/tools/clippy/tests/ui/use_self.fixed18
-rw-r--r--src/tools/clippy/tests/ui/use_self.rs16
-rw-r--r--src/tools/clippy/tests/ui/use_self.stderr8
-rw-r--r--src/tools/clippy/tests/ui/useless_attribute.fixed10
-rw-r--r--src/tools/clippy/tests/ui/useless_attribute.rs10
-rw-r--r--src/tools/clippy/tests/ui/vec.fixed9
-rw-r--r--src/tools/clippy/tests/ui/vec.rs9
-rw-r--r--src/tools/clippy/tests/ui/vec.stderr8
-rw-r--r--src/tools/clippy/tests/ui/zero_sized_hashmap_values.rs8
-rw-r--r--src/tools/clippy/tests/ui/zero_sized_hashmap_values.stderr6
-rw-r--r--src/tools/clippy/triagebot.toml7
-rw-r--r--src/tools/compiletest/Cargo.toml2
-rw-r--r--src/tools/compiletest/src/bin/main.rs3
-rw-r--r--src/tools/compiletest/src/common.rs38
-rw-r--r--src/tools/compiletest/src/directives.rs45
-rw-r--r--src/tools/compiletest/src/directives/directive_names.rs3
-rw-r--r--src/tools/compiletest/src/directives/needs.rs13
-rw-r--r--src/tools/compiletest/src/directives/tests.rs8
-rw-r--r--src/tools/compiletest/src/executor.rs127
-rw-r--r--src/tools/compiletest/src/lib.rs240
-rw-r--r--src/tools/compiletest/src/output_capture.rs52
-rw-r--r--src/tools/compiletest/src/panic_hook.rs136
-rw-r--r--src/tools/compiletest/src/runtest.rs177
-rw-r--r--src/tools/compiletest/src/runtest/codegen_units.rs30
-rw-r--r--src/tools/compiletest/src/runtest/compute_diff.rs (renamed from src/tools/compiletest/src/compute_diff.rs)7
-rw-r--r--src/tools/compiletest/src/runtest/crashes.rs8
-rw-r--r--src/tools/compiletest/src/runtest/debuginfo.rs52
-rw-r--r--src/tools/compiletest/src/runtest/incremental.rs2
-rw-r--r--src/tools/compiletest/src/runtest/mir_opt.rs4
-rw-r--r--src/tools/compiletest/src/runtest/pretty.rs10
-rw-r--r--src/tools/compiletest/src/runtest/run_make.rs13
-rw-r--r--src/tools/compiletest/src/runtest/rustdoc_json.rs4
-rw-r--r--src/tools/compiletest/src/runtest/ui.rs26
-rw-r--r--src/tools/compiletest/src/util.rs13
m---------src/tools/enzyme0
-rw-r--r--src/tools/features-status-dump/src/main.rs3
-rw-r--r--src/tools/html-checker/main.rs2
-rw-r--r--src/tools/jsondoclint/src/item_kind.rs4
-rw-r--r--src/tools/linkchecker/Cargo.toml1
-rw-r--r--src/tools/linkchecker/main.rs13
-rw-r--r--src/tools/linkchecker/tests/valid/inner/bar.html3
-rw-r--r--src/tools/linkchecker/tests/valid/inner/foo.html8
-rw-r--r--src/tools/linkchecker/tests/valid/inner/redir-target.html3
-rw-r--r--src/tools/lint-docs/src/lib.rs42
-rw-r--r--src/tools/lint-docs/src/main.rs13
-rw-r--r--src/tools/miri/.github/workflows/ci.yml85
-rw-r--r--src/tools/miri/CONTRIBUTING.md6
-rw-r--r--src/tools/miri/Cargo.lock28
-rw-r--r--src/tools/miri/Cargo.toml5
-rw-r--r--src/tools/miri/README.md9
-rw-r--r--src/tools/miri/cargo-miri/Cargo.lock4
-rw-r--r--src/tools/miri/cargo-miri/Cargo.toml2
-rw-r--r--src/tools/miri/cargo-miri/src/phases.rs39
-rwxr-xr-xsrc/tools/miri/ci/ci.sh21
-rw-r--r--src/tools/miri/doc/genmc.md38
-rw-r--r--src/tools/miri/doc/img/perfetto_aggregate_statistics.pngbin0 -> 77886 bytes
-rw-r--r--src/tools/miri/doc/img/perfetto_aggregate_statistics_sql.pngbin0 -> 181156 bytes
-rw-r--r--src/tools/miri/doc/img/perfetto_span.pngbin0 -> 165716 bytes
-rw-r--r--src/tools/miri/doc/img/perfetto_subname_statistics.pngbin0 -> 102585 bytes
-rw-r--r--src/tools/miri/doc/img/perfetto_timeline.pngbin0 -> 613708 bytes
-rw-r--r--src/tools/miri/doc/img/perfetto_visualize_argument_values.pngbin0 -> 68014 bytes
-rw-r--r--src/tools/miri/doc/img/perfetto_visualize_argument_values_misbehaving.pngbin0 -> 68856 bytes
-rw-r--r--src/tools/miri/doc/img/perfetto_visualize_argument_values_sql.pngbin0 -> 261657 bytes
-rw-r--r--src/tools/miri/doc/tracing.md292
-rw-r--r--src/tools/miri/etc/rust_analyzer_zed.json41
-rw-r--r--src/tools/miri/genmc-sys/.clang-format52
-rw-r--r--src/tools/miri/genmc-sys/Cargo.toml8
-rw-r--r--src/tools/miri/genmc-sys/build.rs75
-rw-r--r--src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp345
-rw-r--r--src/tools/miri/genmc-sys/cpp/include/ResultHandling.hpp21
-rw-r--r--src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp251
-rw-r--r--src/tools/miri/genmc-sys/cpp/src/MiriInterface/Exploration.cpp56
-rw-r--r--src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp197
-rw-r--r--src/tools/miri/genmc-sys/cpp/src/MiriInterface/ThreadManagement.cpp54
-rw-r--r--src/tools/miri/genmc-sys/src/lib.rs436
-rw-r--r--src/tools/miri/genmc-sys/src_cpp/MiriInterface.cpp50
-rw-r--r--src/tools/miri/genmc-sys/src_cpp/MiriInterface.hpp44
-rw-r--r--src/tools/miri/miri-script/src/commands.rs8
-rw-r--r--src/tools/miri/rust-version2
-rw-r--r--src/tools/miri/src/alloc_addresses/address_generator.rs78
-rw-r--r--src/tools/miri/src/alloc_addresses/mod.rs86
-rw-r--r--src/tools/miri/src/bin/log/mod.rs1
-rw-r--r--src/tools/miri/src/bin/log/tracing_chrome.rs177
-rw-r--r--src/tools/miri/src/bin/log/tracing_chrome_instant.rs183
-rw-r--r--src/tools/miri/src/bin/miri.rs42
-rw-r--r--src/tools/miri/src/borrow_tracker/mod.rs24
-rw-r--r--src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs1
-rw-r--r--src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs6
-rw-r--r--src/tools/miri/src/borrow_tracker/tree_borrows/foreign_access_skipping.rs2
-rw-r--r--src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs225
-rw-r--r--src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs119
-rw-r--r--src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs101
-rw-r--r--src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs12
-rw-r--r--src/tools/miri/src/clock.rs23
-rw-r--r--src/tools/miri/src/concurrency/data_race.rs209
-rw-r--r--src/tools/miri/src/concurrency/genmc/config.rs92
-rw-r--r--src/tools/miri/src/concurrency/genmc/dummy.rs123
-rw-r--r--src/tools/miri/src/concurrency/genmc/global_allocations.rs118
-rw-r--r--src/tools/miri/src/concurrency/genmc/helper.rs225
-rw-r--r--src/tools/miri/src/concurrency/genmc/mod.rs870
-rw-r--r--src/tools/miri/src/concurrency/genmc/run.rs166
-rw-r--r--src/tools/miri/src/concurrency/genmc/scheduling.rs69
-rw-r--r--src/tools/miri/src/concurrency/genmc/thread_id_map.rs63
-rw-r--r--src/tools/miri/src/concurrency/init_once.rs14
-rw-r--r--src/tools/miri/src/concurrency/mod.rs2
-rw-r--r--src/tools/miri/src/concurrency/sync.rs72
-rw-r--r--src/tools/miri/src/concurrency/thread.rs53
-rw-r--r--src/tools/miri/src/concurrency/weak_memory.rs95
-rw-r--r--src/tools/miri/src/diagnostics.rs46
-rw-r--r--src/tools/miri/src/eval.rs96
-rw-r--r--src/tools/miri/src/helpers.rs3
-rw-r--r--src/tools/miri/src/intrinsics/atomic.rs77
-rw-r--r--src/tools/miri/src/intrinsics/math.rs313
-rw-r--r--src/tools/miri/src/intrinsics/mod.rs432
-rw-r--r--src/tools/miri/src/lib.rs14
-rw-r--r--src/tools/miri/src/machine.rs113
-rw-r--r--src/tools/miri/src/math.rs340
-rw-r--r--src/tools/miri/src/operator.rs2
-rw-r--r--src/tools/miri/src/shims/extern_static.rs2
-rw-r--r--src/tools/miri/src/shims/files.rs12
-rw-r--r--src/tools/miri/src/shims/foreign_items.rs338
-rw-r--r--src/tools/miri/src/shims/math.rs247
-rw-r--r--src/tools/miri/src/shims/mod.rs1
-rw-r--r--src/tools/miri/src/shims/native_lib/ffi.rs46
-rw-r--r--src/tools/miri/src/shims/native_lib/mod.rs317
-rw-r--r--src/tools/miri/src/shims/native_lib/trace/child.rs30
-rw-r--r--src/tools/miri/src/shims/native_lib/trace/parent.rs82
-rw-r--r--src/tools/miri/src/shims/time.rs4
-rw-r--r--src/tools/miri/src/shims/unix/android/foreign_items.rs11
-rw-r--r--src/tools/miri/src/shims/unix/fd.rs56
-rw-r--r--src/tools/miri/src/shims/unix/freebsd/foreign_items.rs6
-rw-r--r--src/tools/miri/src/shims/unix/fs.rs60
-rw-r--r--src/tools/miri/src/shims/unix/linux/foreign_items.rs2
-rw-r--r--src/tools/miri/src/shims/unix/linux_like/epoll.rs4
-rw-r--r--src/tools/miri/src/shims/unix/linux_like/eventfd.rs9
-rw-r--r--src/tools/miri/src/shims/unix/macos/sync.rs4
-rw-r--r--src/tools/miri/src/shims/unix/solarish/foreign_items.rs2
-rw-r--r--src/tools/miri/src/shims/unix/sync.rs16
-rw-r--r--src/tools/miri/src/shims/unix/unnamed_socket.rs12
-rw-r--r--src/tools/miri/src/shims/wasi/foreign_items.rs68
-rw-r--r--src/tools/miri/src/shims/windows/foreign_items.rs32
-rw-r--r--src/tools/miri/src/shims/windows/handle.rs8
-rw-r--r--src/tools/miri/src/shims/windows/sync.rs2
-rwxr-xr-xsrc/tools/miri/test-cargo-miri/run-test.py2
-rw-r--r--src/tools/miri/test-cargo-miri/test.default.stdout.ref1
-rw-r--r--src/tools/miri/test-cargo-miri/test.filter.stdout.ref1
-rw-r--r--src/tools/miri/test-cargo-miri/test.multiple_targets.stdout.ref2
-rw-r--r--src/tools/miri/tests/deps/Cargo.lock4
-rw-r--r--src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.stderr4
-rw-r--r--src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.stderr4
-rw-r--r--src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.stderr4
-rw-r--r--src/tools/miri/tests/fail-dep/concurrency/windows_join_detached.stderr4
-rw-r--r--src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr4
-rw-r--r--src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr4
-rw-r--r--src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.stderr4
-rw-r--r--src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.stderr4
-rw-r--r--src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.stderr4
-rw-r--r--src/tools/miri/tests/fail-dep/libc/socketpair-close-while-blocked.stderr4
-rw-r--r--src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.stderr4
-rw-r--r--src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.stderr4
-rw-r--r--src/tools/miri/tests/fail/alloc/alloc_error_handler.rs2
-rw-r--r--src/tools/miri/tests/fail/alloc/alloc_error_handler.stderr10
-rw-r--r--src/tools/miri/tests/fail/async-shared-mutable.tree.stderr3
-rw-r--r--src/tools/miri/tests/fail/both_borrows/alias_through_mutation.tree.stderr1
-rw-r--r--src/tools/miri/tests/fail/both_borrows/aliasing_mut1.tree.stderr1
-rw-r--r--src/tools/miri/tests/fail/both_borrows/aliasing_mut2.tree.stderr1
-rw-r--r--src/tools/miri/tests/fail/both_borrows/aliasing_mut3.tree.stderr1
-rw-r--r--src/tools/miri/tests/fail/both_borrows/aliasing_mut4.tree.stderr1
-rw-r--r--src/tools/miri/tests/fail/both_borrows/box_exclusive_violation1.tree.stderr1
-rw-r--r--src/tools/miri/tests/fail/both_borrows/box_noalias_violation.tree.stderr5
-rw-r--r--src/tools/miri/tests/fail/both_borrows/buggy_as_mut_slice.tree.stderr1
-rw-r--r--src/tools/miri/tests/fail/both_borrows/buggy_split_at_mut.tree.stderr1
-rw-r--r--src/tools/miri/tests/fail/both_borrows/illegal_write1.tree.stderr1
-rw-r--r--src/tools/miri/tests/fail/both_borrows/illegal_write5.tree.stderr1
-rw-r--r--src/tools/miri/tests/fail/both_borrows/illegal_write6.tree.stderr5
-rw-r--r--src/tools/miri/tests/fail/both_borrows/invalidate_against_protector2.tree.stderr1
-rw-r--r--src/tools/miri/tests/fail/both_borrows/invalidate_against_protector3.tree.stderr1
-rw-r--r--src/tools/miri/tests/fail/both_borrows/issue-miri-1050-1.rs2
-rw-r--r--src/tools/miri/tests/fail/both_borrows/issue-miri-1050-2.rs2
-rw-r--r--src/tools/miri/tests/fail/both_borrows/load_invalid_shr.tree.stderr1
-rw-r--r--src/tools/miri/tests/fail/both_borrows/mut_exclusive_violation1.tree.stderr1
-rw-r--r--src/tools/miri/tests/fail/both_borrows/mut_exclusive_violation2.tree.stderr1
-rw-r--r--src/tools/miri/tests/fail/both_borrows/newtype_pair_retagging.tree.stderr1
-rw-r--r--src/tools/miri/tests/fail/both_borrows/newtype_retagging.tree.stderr1
-rw-r--r--src/tools/miri/tests/fail/both_borrows/outdated_local.tree.stderr1
-rw-r--r--src/tools/miri/tests/fail/both_borrows/pass_invalid_shr.tree.stderr1
-rw-r--r--src/tools/miri/tests/fail/both_borrows/pass_invalid_shr_option.tree.stderr1
-rw-r--r--src/tools/miri/tests/fail/both_borrows/pass_invalid_shr_tuple.tree.stderr1
-rw-r--r--src/tools/miri/tests/fail/both_borrows/return_invalid_shr.stack.stderr2
-rw-r--r--src/tools/miri/tests/fail/both_borrows/return_invalid_shr.tree.stderr3
-rw-r--r--src/tools/miri/tests/fail/both_borrows/return_invalid_shr_option.stack.stderr2
-rw-r--r--src/tools/miri/tests/fail/both_borrows/return_invalid_shr_option.tree.stderr3
-rw-r--r--src/tools/miri/tests/fail/both_borrows/return_invalid_shr_tuple.stack.stderr2
-rw-r--r--src/tools/miri/tests/fail/both_borrows/return_invalid_shr_tuple.tree.stderr3
-rw-r--r--src/tools/miri/tests/fail/both_borrows/shr_frozen_violation1.tree.stderr1
-rw-r--r--src/tools/miri/tests/fail/both_borrows/shr_frozen_violation2.tree.stderr1
-rw-r--r--src/tools/miri/tests/fail/both_borrows/zero-sized-protected.rs18
-rw-r--r--src/tools/miri/tests/fail/both_borrows/zero-sized-protected.tree.stderr31
-rw-r--r--src/tools/miri/tests/fail/branchless-select-i128-pointer.rs2
-rw-r--r--src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias.rs36
-rw-r--r--src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias.stack.stderr25
-rw-r--r--src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias.tree.stderr34
-rw-r--r--src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias_ret.rs34
-rw-r--r--src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias_ret.stack.stderr25
-rw-r--r--src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias_ret.tree.stderr34
-rw-r--r--src/tools/miri/tests/fail/function_calls/arg_inplace_mutate.tree.stderr5
-rw-r--r--src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.tree.stderr5
-rw-r--r--src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr9
-rw-r--r--src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr9
-rw-r--r--src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.extern_block.stderr2
-rw-r--r--src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.rs2
-rw-r--r--src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_read.none.stderr4
-rw-r--r--src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_read.rs8
-rw-r--r--src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_read.stack.stderr14
-rw-r--r--src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_read.tree.stderr13
-rw-r--r--src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write.rs4
-rw-r--r--src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write.stack.stderr10
-rw-r--r--src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write.tree.stderr9
-rw-r--r--src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write_tail_call.rs4
-rw-r--r--src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write_tail_call.stack.stderr14
-rw-r--r--src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write_tail_call.tree.stderr9
-rw-r--r--src/tools/miri/tests/fail/intrinsics/funnel_shl.rs7
-rw-r--r--src/tools/miri/tests/fail/intrinsics/funnel_shl.stderr15
-rw-r--r--src/tools/miri/tests/fail/intrinsics/funnel_shr.rs7
-rw-r--r--src/tools/miri/tests/fail/intrinsics/funnel_shr.stderr15
-rw-r--r--src/tools/miri/tests/fail/overlapping_assignment_aggregate.rs18
-rw-r--r--src/tools/miri/tests/fail/overlapping_assignment_aggregate.stderr (renamed from src/tools/miri/tests/fail/both_borrows/zero-sized-protected.stack.stderr)10
-rw-r--r--src/tools/miri/tests/fail/panic/abort_unwind.rs2
-rw-r--r--src/tools/miri/tests/fail/panic/abort_unwind.stderr9
-rw-r--r--src/tools/miri/tests/fail/panic/double_panic.rs2
-rw-r--r--src/tools/miri/tests/fail/panic/double_panic.stderr7
-rw-r--r--src/tools/miri/tests/fail/panic/panic_abort1.rs2
-rw-r--r--src/tools/miri/tests/fail/panic/panic_abort1.stderr10
-rw-r--r--src/tools/miri/tests/fail/panic/panic_abort2.rs2
-rw-r--r--src/tools/miri/tests/fail/panic/panic_abort2.stderr10
-rw-r--r--src/tools/miri/tests/fail/panic/panic_abort3.rs2
-rw-r--r--src/tools/miri/tests/fail/panic/panic_abort3.stderr10
-rw-r--r--src/tools/miri/tests/fail/panic/panic_abort4.rs2
-rw-r--r--src/tools/miri/tests/fail/panic/panic_abort4.stderr10
-rw-r--r--src/tools/miri/tests/fail/provenance/mix-ptrs1.rs21
-rw-r--r--src/tools/miri/tests/fail/provenance/mix-ptrs1.stderr16
-rw-r--r--src/tools/miri/tests/fail/provenance/mix-ptrs2.rs37
-rw-r--r--src/tools/miri/tests/fail/provenance/mix-ptrs2.stderr16
-rw-r--r--src/tools/miri/tests/fail/provenance/provenance_transmute.rs2
-rw-r--r--src/tools/miri/tests/fail/ptr_swap_nonoverlapping.rs2
-rw-r--r--src/tools/miri/tests/fail/ptr_swap_nonoverlapping.stderr9
-rw-r--r--src/tools/miri/tests/fail/terminate-terminator.rs2
-rw-r--r--src/tools/miri/tests/fail/terminate-terminator.stderr11
-rw-r--r--src/tools/miri/tests/fail/tree_borrows/alternate-read-write.stderr3
-rw-r--r--src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.stderr1
-rw-r--r--src/tools/miri/tests/fail/tree_borrows/error-range.stderr1
-rw-r--r--src/tools/miri/tests/fail/tree_borrows/fnentry_invalidation.stderr3
-rw-r--r--src/tools/miri/tests/fail/tree_borrows/frozen-lazy-write-to-surrounding.rs9
-rw-r--r--src/tools/miri/tests/fail/tree_borrows/frozen-lazy-write-to-surrounding.stderr21
-rw-r--r--src/tools/miri/tests/fail/tree_borrows/outside-range.stderr1
-rw-r--r--src/tools/miri/tests/fail/tree_borrows/parent_read_freezes_raw_mut.stderr3
-rw-r--r--src/tools/miri/tests/fail/tree_borrows/pass_invalid_mut.stderr3
-rw-r--r--src/tools/miri/tests/fail/tree_borrows/protector-write-lazy.stderr1
-rw-r--r--src/tools/miri/tests/fail/tree_borrows/repeated_foreign_read_lazy_conflicted.stderr1
-rw-r--r--src/tools/miri/tests/fail/tree_borrows/reserved/cell-protected-write.stderr1
-rw-r--r--src/tools/miri/tests/fail/tree_borrows/reserved/int-protected-write.stderr1
-rw-r--r--src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.rs3
-rw-r--r--src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.with.stderr1
-rw-r--r--src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.without.stderr1
-rw-r--r--src/tools/miri/tests/fail/tree_borrows/return_invalid_mut.stderr3
-rw-r--r--src/tools/miri/tests/fail/tree_borrows/spurious_read.stderr1
-rw-r--r--src/tools/miri/tests/fail/tree_borrows/strongly-protected.stderr1
-rw-r--r--src/tools/miri/tests/fail/tree_borrows/subtree_traversal_skipping_diagnostics.rs2
-rw-r--r--src/tools/miri/tests/fail/tree_borrows/subtree_traversal_skipping_diagnostics.stderr1
-rw-r--r--src/tools/miri/tests/fail/tree_borrows/unique.default.stderr32
-rw-r--r--src/tools/miri/tests/fail/tree_borrows/write-during-2phase.stderr1
-rw-r--r--src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.rs11
-rw-r--r--src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.stderr2
-rw-r--r--src/tools/miri/tests/fail/unwind-action-terminate.rs2
-rw-r--r--src/tools/miri/tests/fail/unwind-action-terminate.stderr9
-rw-r--r--src/tools/miri/tests/fail/validity/dangling_ref1.rs3
-rw-r--r--src/tools/miri/tests/fail/validity/nonzero.stderr2
-rw-r--r--src/tools/miri/tests/genmc/fail/data_race/mpu2_rels_rlx.rs45
-rw-r--r--src/tools/miri/tests/genmc/fail/data_race/mpu2_rels_rlx.stderr22
-rw-r--r--src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rel_rlx.stderr22
-rw-r--r--src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_acq.stderr22
-rw-r--r--src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_rlx.stderr22
-rw-r--r--src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rs40
-rw-r--r--src/tools/miri/tests/genmc/fail/loom/buggy_inc.rs66
-rw-r--r--src/tools/miri/tests/genmc/fail/loom/buggy_inc.stderr16
-rw-r--r--src/tools/miri/tests/genmc/fail/loom/store_buffering.genmc.stderr16
-rw-r--r--src/tools/miri/tests/genmc/fail/loom/store_buffering.non_genmc.stderr13
-rw-r--r--src/tools/miri/tests/genmc/fail/loom/store_buffering.rs57
-rw-r--r--src/tools/miri/tests/genmc/fail/simple/2w2w_weak.relaxed4.stderr16
-rw-r--r--src/tools/miri/tests/genmc/fail/simple/2w2w_weak.release4.stderr16
-rw-r--r--src/tools/miri/tests/genmc/fail/simple/2w2w_weak.rs72
-rw-r--r--src/tools/miri/tests/genmc/fail/simple/2w2w_weak.sc3_rel1.stderr16
-rw-r--r--src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.rs70
-rw-r--r--src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.stderr22
-rw-r--r--src/tools/miri/tests/genmc/pass/atomics/cas_simple.rs34
-rw-r--r--src/tools/miri/tests/genmc/pass/atomics/cas_simple.stderr2
-rw-r--r--src/tools/miri/tests/genmc/pass/atomics/rmw_ops.rs91
-rw-r--r--src/tools/miri/tests/genmc/pass/atomics/rmw_ops.stderr2
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/2cowr.rs51
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/2cowr.stderr2
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/2w2w_2sc_scf.rs33
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/2w2w_2sc_scf.stderr2
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.release1.stderr2
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.release2.stderr2
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.rs54
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.rs49
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.sc.stderr2
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.weak.stderr2
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/2w2w_4sc.rs45
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/2w2w_4sc.stderr2
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.rs64
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.stderr2
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/IRIWish.rs64
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/IRIWish.stderr30
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/LB.rs47
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/LB.stderr2
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/LB_incMPs.rs41
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/LB_incMPs.stderr2
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/MP.rs47
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/MP.stderr2
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.rs67
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.stderr38
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/MPU_rels_acq.rs42
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/MPU_rels_acq.stderr2
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/MP_incMPs.rs36
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/MP_incMPs.stderr2
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/MP_rels_acqf.rs40
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/MP_rels_acqf.stderr2
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/SB.rs47
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/SB.stderr2
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/SB_2sc_scf.rs32
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/SB_2sc_scf.stderr2
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/Z6_U.rs62
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/Z6_U.sc.stderr20
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/Z6_U.weak.stderr24
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/Z6_acq.rs38
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/Z6_acq.stderr2
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/atomicpo.rs32
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/atomicpo.stderr2
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/casdep.rs37
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/casdep.stderr2
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/ccr.rs34
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/ccr.stderr2
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/cii.rs35
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/cii.stderr2
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/corr.rs45
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/corr.stderr2
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/corr0.rs46
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/corr0.stderr2
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/corr1.rs58
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/corr1.stderr2
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/corr2.rs70
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/corr2.stderr2
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/corw.rs43
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/corw.stderr2
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/cowr.rs40
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/cowr.stderr2
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/cumul-release.rs58
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/cumul-release.stderr2
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/default.rs47
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/default.stderr2
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/detour.join.stderr2
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/detour.no_join.stderr2
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/detour.rs68
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.rs53
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.stderr2
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/inc2w.rs45
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/inc2w.stderr2
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/inc_inc_RR_W_RR.rs63
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/inc_inc_RR_W_RR.stderr2
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/riwi.rs31
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/riwi.stderr2
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/viktor-relseq.rs38
-rw-r--r--src/tools/miri/tests/genmc/pass/litmus/viktor-relseq.stderr4
-rw-r--r--src/tools/miri/tests/genmc/pass/test_cxx_build.rs8
-rw-r--r--src/tools/miri/tests/genmc/pass/test_cxx_build.stderr5
-rw-r--r--src/tools/miri/tests/native-lib/aggregate_arguments.c52
-rw-r--r--src/tools/miri/tests/native-lib/fail/pass_struct_expose_only_range.rs23
-rw-r--r--src/tools/miri/tests/native-lib/fail/pass_struct_expose_only_range.stderr28
-rw-r--r--src/tools/miri/tests/native-lib/fail/struct_not_extern_c.rs19
-rw-r--r--src/tools/miri/tests/native-lib/fail/struct_not_extern_c.stderr14
-rw-r--r--src/tools/miri/tests/native-lib/fail/tracing/partial_init.stderr2
-rw-r--r--src/tools/miri/tests/native-lib/fail/uninit_struct.rs27
-rw-r--r--src/tools/miri/tests/native-lib/fail/uninit_struct.stderr15
-rw-r--r--src/tools/miri/tests/native-lib/pass/aggregate_arguments.rs52
-rw-r--r--src/tools/miri/tests/native-lib/pass/ptr_read_access.rs1
-rw-r--r--src/tools/miri/tests/panic/oob_subslice.stderr2
-rw-r--r--src/tools/miri/tests/panic/transmute_fat2.rs2
-rw-r--r--src/tools/miri/tests/pass-dep/shims/windows-fs.rs47
-rw-r--r--src/tools/miri/tests/pass/0weak_memory/consistency.rs (renamed from src/tools/miri/tests/pass/0weak_memory_consistency.rs)29
-rw-r--r--src/tools/miri/tests/pass/0weak_memory/consistency_sc.rs (renamed from src/tools/miri/tests/pass/0weak_memory_consistency_sc.rs)5
-rw-r--r--src/tools/miri/tests/pass/0weak_memory/extra_cpp.rs (renamed from src/tools/miri/tests/pass/weak_memory/extra_cpp.rs)0
-rw-r--r--src/tools/miri/tests/pass/0weak_memory/weak.rs238
-rw-r--r--src/tools/miri/tests/pass/atomic.rs1
-rw-r--r--src/tools/miri/tests/pass/binops.rs1
-rw-r--r--src/tools/miri/tests/pass/both_borrows/basic_aliasing_model.rs32
-rw-r--r--src/tools/miri/tests/pass/btreemap.rs1
-rw-r--r--src/tools/miri/tests/pass/concurrency/sync.rs3
-rw-r--r--src/tools/miri/tests/pass/float.rs336
-rw-r--r--src/tools/miri/tests/pass/float_extra_rounding_error.rs31
-rw-r--r--src/tools/miri/tests/pass/float_nan.rs332
-rw-r--r--src/tools/miri/tests/pass/function_calls/exported_symbol_weak.rs39
-rw-r--r--src/tools/miri/tests/pass/intrinsics/fmuladd_nondeterministic.rs103
-rw-r--r--src/tools/miri/tests/pass/intrinsics/integer.rs8
-rw-r--r--src/tools/miri/tests/pass/overlapping_assignment_aggregate_scalar.rs19
-rw-r--r--src/tools/miri/tests/pass/prefetch.rs23
-rw-r--r--src/tools/miri/tests/pass/provenance.rs35
-rw-r--r--src/tools/miri/tests/pass/shims/fs.rs4
-rw-r--r--src/tools/miri/tests/pass/shims/x86/rounding-error.rs37
-rw-r--r--src/tools/miri/tests/pass/static_align.rs14
-rw-r--r--src/tools/miri/tests/pass/too-large-primval-write-problem.rs2
-rw-r--r--src/tools/miri/tests/pass/transmute_ptr.rs21
-rw-r--r--src/tools/miri/tests/pass/tree_borrows/cell-inside-box.rs2
-rw-r--r--src/tools/miri/tests/pass/tree_borrows/cell-lazy-write-to-surrounding.rs10
-rw-r--r--src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.rs4
-rw-r--r--src/tools/miri/tests/pass/tree_borrows/reserved.rs4
-rw-r--r--src/tools/miri/tests/pass/weak_memory/weak.rs151
-rw-r--r--src/tools/miri/tests/ui.rs3
-rw-r--r--src/tools/miri/tests/utils/genmc.rs64
-rw-r--r--src/tools/miri/tests/utils/libc.rs5
-rw-r--r--src/tools/miri/tests/utils/mod.rs50
-rw-r--r--src/tools/miri/tests/x86_64-unknown-kernel.json2
-rw-r--r--src/tools/miri/triagebot.toml13
-rw-r--r--src/tools/nix-dev-shell/shell.nix4
-rw-r--r--src/tools/opt-dist/Cargo.toml2
-rw-r--r--src/tools/opt-dist/src/bolt.rs5
-rw-r--r--src/tools/opt-dist/src/environment.rs8
-rw-r--r--src/tools/opt-dist/src/exec.rs6
-rw-r--r--src/tools/opt-dist/src/main.rs14
-rw-r--r--src/tools/opt-dist/src/tests.rs10
-rw-r--r--src/tools/opt-dist/src/training.rs4
-rw-r--r--src/tools/remote-test-server/src/main.rs19
-rw-r--r--src/tools/run-make-support/Cargo.toml2
-rw-r--r--src/tools/run-make-support/src/external_deps/cargo.rs14
-rw-r--r--src/tools/run-make-support/src/external_deps/rustc.rs6
-rw-r--r--src/tools/run-make-support/src/lib.rs5
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/lib.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lib.rs31
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/lib.rs4
-rw-r--r--src/tools/rustbook/Cargo.lock97
-rw-r--r--src/tools/rustbook/Cargo.toml3
-rw-r--r--src/tools/rustdoc-gui-test/src/main.rs20
-rw-r--r--src/tools/rustdoc-gui/.eslintrc.js96
-rw-r--r--src/tools/rustdoc-gui/tester.js309
-rw-r--r--src/tools/rustdoc-js/tester.js207
-rw-r--r--src/tools/rustfmt/Cargo.toml2
-rw-r--r--src/tools/rustfmt/src/items.rs55
-rw-r--r--src/tools/rustfmt/src/modules.rs7
-rw-r--r--src/tools/rustfmt/src/parse/parser.rs14
-rw-r--r--src/tools/rustfmt/src/patterns.rs2
-rw-r--r--src/tools/rustfmt/src/visitor.rs2
-rw-r--r--src/tools/rustfmt/tests/source/frontmatter_compact.rs8
-rw-r--r--src/tools/rustfmt/tests/source/frontmatter_escaped.rs13
-rw-r--r--src/tools/rustfmt/tests/source/frontmatter_spaced.rs16
-rw-r--r--src/tools/rustfmt/tests/source/negative-impl.rs4
-rw-r--r--src/tools/rustfmt/tests/target/frontmatter_compact.rs8
-rw-r--r--src/tools/rustfmt/tests/target/frontmatter_escaped.rs13
-rw-r--r--src/tools/rustfmt/tests/target/frontmatter_spaced.rs16
-rw-r--r--src/tools/rustfmt/tests/target/negative-impl.rs8
-rw-r--r--src/tools/tidy/Readme.md112
-rw-r--r--src/tools/tidy/src/alphabetical.rs31
-rw-r--r--src/tools/tidy/src/alphabetical/tests.rs27
-rw-r--r--src/tools/tidy/src/bins.rs14
-rw-r--r--src/tools/tidy/src/debug_artifacts.rs15
-rw-r--r--src/tools/tidy/src/deps.rs324
-rw-r--r--src/tools/tidy/src/diagnostics.rs243
-rw-r--r--src/tools/tidy/src/edition.rs9
-rw-r--r--src/tools/tidy/src/error_codes.rs124
-rw-r--r--src/tools/tidy/src/extdeps.rs19
-rw-r--r--src/tools/tidy/src/extra_checks/mod.rs124
-rw-r--r--src/tools/tidy/src/extra_checks/rustdoc_js.rs34
-rw-r--r--src/tools/tidy/src/features.rs132
-rw-r--r--src/tools/tidy/src/filenames.rs20
-rw-r--r--src/tools/tidy/src/fluent_alphabetical.rs36
-rw-r--r--src/tools/tidy/src/fluent_lowercase.rs65
-rw-r--r--src/tools/tidy/src/fluent_period.rs13
-rw-r--r--src/tools/tidy/src/fluent_used.rs7
-rw-r--r--src/tools/tidy/src/gcc_submodule.rs17
-rw-r--r--src/tools/tidy/src/issues.txt1
-rw-r--r--src/tools/tidy/src/known_bug.rs9
-rw-r--r--src/tools/tidy/src/lib.rs118
-rw-r--r--src/tools/tidy/src/main.rs51
-rw-r--r--src/tools/tidy/src/mir_opt_tests.rs23
-rw-r--r--src/tools/tidy/src/pal.rs12
-rw-r--r--src/tools/tidy/src/rustdoc_css_themes.rs37
-rw-r--r--src/tools/tidy/src/rustdoc_gui_tests.rs11
-rw-r--r--src/tools/tidy/src/rustdoc_json.rs46
-rw-r--r--src/tools/tidy/src/rustdoc_templates.rs15
-rw-r--r--src/tools/tidy/src/style.rs37
-rw-r--r--src/tools/tidy/src/target_policy.rs7
-rw-r--r--src/tools/tidy/src/target_specific_tests.rs23
-rw-r--r--src/tools/tidy/src/tests_placement.rs17
-rw-r--r--src/tools/tidy/src/tests_revision_unpaired_stdout_stderr.rs16
-rw-r--r--src/tools/tidy/src/triagebot.rs45
-rw-r--r--src/tools/tidy/src/ui_tests.rs53
-rw-r--r--src/tools/tidy/src/unit_tests.rs15
-rw-r--r--src/tools/tidy/src/unknown_revision.rs17
-rw-r--r--src/tools/tidy/src/unstable_book.rs40
-rw-r--r--src/tools/tidy/src/x_version.rs17
-rw-r--r--src/tools/unicode-table-generator/src/cascading_map.rs1
-rw-r--r--src/tools/unicode-table-generator/src/case_mapping.rs43
-rw-r--r--src/tools/unicode-table-generator/src/main.rs251
-rw-r--r--src/tools/unicode-table-generator/src/raw_emitter.rs3
-rw-r--r--src/tools/unicode-table-generator/src/skiplist.rs2
-rw-r--r--src/tools/unstable-book-gen/src/main.rs3
-rw-r--r--src/tools/wasm-component-ld/Cargo.toml2
1004 files changed, 24181 insertions, 10361 deletions
diff --git a/src/tools/build-manifest/Cargo.toml b/src/tools/build-manifest/Cargo.toml
index 7e0c4bee2b3..efa99f181b3 100644
--- a/src/tools/build-manifest/Cargo.toml
+++ b/src/tools/build-manifest/Cargo.toml
@@ -4,7 +4,7 @@ version = "0.1.0"
 edition = "2021"
 
 [dependencies]
-toml = "0.5"
+toml = "0.7"
 serde = { version = "1.0", features = ["derive"] }
 serde_json = "1.0"
 anyhow = "1.0.32"
diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs
index 0520eff0fa2..2890d26d3f4 100644
--- a/src/tools/build-manifest/src/main.rs
+++ b/src/tools/build-manifest/src/main.rs
@@ -71,6 +71,7 @@ static TARGETS: &[&str] = &[
     "aarch64-unknown-none-softfloat",
     "aarch64-unknown-redox",
     "aarch64-unknown-uefi",
+    "aarch64-unknown-managarm-mlibc",
     "amdgcn-amd-amdhsa",
     "arm64e-apple-darwin",
     "arm64e-apple-ios",
@@ -155,6 +156,7 @@ static TARGETS: &[&str] = &[
     "riscv64gc-unknown-none-elf",
     "riscv64gc-unknown-linux-gnu",
     "riscv64gc-unknown-linux-musl",
+    "riscv64gc-unknown-managarm-mlibc",
     "s390x-unknown-linux-gnu",
     "sparc64-unknown-linux-gnu",
     "sparcv9-sun-solaris",
@@ -194,6 +196,7 @@ static TARGETS: &[&str] = &[
     "x86_64-unknown-redox",
     "x86_64-unknown-hermit",
     "x86_64-unknown-uefi",
+    "x86_64-unknown-managarm-mlibc",
 ];
 
 /// This allows the manifest to contain rust-docs for hosts that don't build
@@ -205,7 +208,7 @@ static TARGETS: &[&str] = &[
 ///
 /// The order here matters, more specific entries should be first.
 static DOCS_FALLBACK: &[(&str, &str)] = &[
-    ("-apple-", "x86_64-apple-darwin"),
+    ("-apple-", "aarch64-apple-darwin"),
     ("aarch64", "aarch64-unknown-linux-gnu"),
     ("arm-", "aarch64-unknown-linux-gnu"),
     ("", "x86_64-unknown-linux-gnu"),
diff --git a/src/tools/bump-stage0/Cargo.toml b/src/tools/bump-stage0/Cargo.toml
index 6ee7a831839..79097f2c189 100644
--- a/src/tools/bump-stage0/Cargo.toml
+++ b/src/tools/bump-stage0/Cargo.toml
@@ -11,4 +11,4 @@ build_helper = { path = "../../build_helper" }
 curl = "0.4.38"
 indexmap = { version = "2.0.0", features = ["serde"] }
 serde = { version = "1.0.125", features = ["derive"] }
-toml = "0.5.7"
+toml = "0.8.23"
diff --git a/src/tools/bump-stage0/src/main.rs b/src/tools/bump-stage0/src/main.rs
index 680437cce4f..faed748785f 100644
--- a/src/tools/bump-stage0/src/main.rs
+++ b/src/tools/bump-stage0/src/main.rs
@@ -185,7 +185,11 @@ fn fetch_manifest(
         format!("{}/dist/channel-rust-{}.toml", config.dist_server, channel)
     };
 
-    Ok(toml::from_slice(&http_get(&url)?)?)
+    // FIXME: on newer `toml` (>= `0.9.*`), use `toml::from_slice`. For now, we use the most recent
+    // `toml` available in-tree which is `0.8.*`, so we have to do an additional dance here.
+    let response = http_get(&url)?;
+    let response = String::from_utf8(response)?;
+    Ok(toml::from_str(&response)?)
 }
 
 fn http_get(url: &str) -> Result<Vec<u8>, Error> {
diff --git a/src/tools/cargo b/src/tools/cargo
-Subproject 840b83a10fb0e039a83f4d70ad032892c287570
+Subproject 966f94733bbc94ca51ff9f1e4c49ad250ebbdc5
diff --git a/src/tools/clippy/.cargo/config.toml b/src/tools/clippy/.cargo/config.toml
index d9c635df5dc..a09bf95e87b 100644
--- a/src/tools/clippy/.cargo/config.toml
+++ b/src/tools/clippy/.cargo/config.toml
@@ -23,3 +23,10 @@ split-debuginfo = "unpacked"
 rustflags = ["--remap-path-prefix", "=clippy_dev"]
 [profile.dev.package.lintcheck]
 rustflags = ["--remap-path-prefix", "=lintcheck"]
+
+# quine-mc_cluskey makes up a significant part of the runtime in dogfood
+# due to the number of conditions in the clippy_lints crate
+# and enabling optimizations for that specific dependency helps a bit
+# without increasing total build times.
+[profile.dev.package.quine-mc_cluskey]
+opt-level = 3
diff --git a/src/tools/clippy/.github/ISSUE_TEMPLATE/new_lint.yml b/src/tools/clippy/.github/ISSUE_TEMPLATE/new_lint.yml
index a8202f6378f..6ad16aead60 100644
--- a/src/tools/clippy/.github/ISSUE_TEMPLATE/new_lint.yml
+++ b/src/tools/clippy/.github/ISSUE_TEMPLATE/new_lint.yml
@@ -1,7 +1,5 @@
 name: New lint suggestion
-description: |
-  Suggest a new Clippy lint (currently not accepting new lints)
-  Check out the Clippy book for more information about the feature freeze.
+description: Suggest a new Clippy lint.
 labels: ["A-lint"]
 body:
   - type: markdown
diff --git a/src/tools/clippy/.github/PULL_REQUEST_TEMPLATE.md b/src/tools/clippy/.github/PULL_REQUEST_TEMPLATE.md
index 83bfd8e9c68..9e49f60892d 100644
--- a/src/tools/clippy/.github/PULL_REQUEST_TEMPLATE.md
+++ b/src/tools/clippy/.github/PULL_REQUEST_TEMPLATE.md
@@ -32,10 +32,6 @@ order to get feedback.
 
 Delete this line and everything above before opening your PR.
 
-Note that we are currently not taking in new PRs that add new lints. We are in a
-feature freeze. Check out the book for more information. If you open a
-feature-adding pull request, its review will be delayed.
-
 ---
 
 *Please write a short comment explaining your change (or "none" for internal only changes)*
diff --git a/src/tools/clippy/.github/workflows/clippy_dev.yml b/src/tools/clippy/.github/workflows/clippy_dev.yml
index d6534fbaff9..d530eb6c73a 100644
--- a/src/tools/clippy/.github/workflows/clippy_dev.yml
+++ b/src/tools/clippy/.github/workflows/clippy_dev.yml
@@ -16,7 +16,7 @@ jobs:
     steps:
     # Setup
     - name: Checkout
-      uses: actions/checkout@v4
+      uses: actions/checkout@v5
       with:
         # Unsetting this would make so that any malicious package could get our Github Token
         persist-credentials: false
diff --git a/src/tools/clippy/.github/workflows/clippy_mq.yml b/src/tools/clippy/.github/workflows/clippy_mq.yml
index 07d5a08304e..0bcb7135935 100644
--- a/src/tools/clippy/.github/workflows/clippy_mq.yml
+++ b/src/tools/clippy/.github/workflows/clippy_mq.yml
@@ -36,7 +36,7 @@ jobs:
     steps:
     # Setup
     - name: Checkout
-      uses: actions/checkout@v4
+      uses: actions/checkout@v5
       with:
         persist-credentials: false
 
@@ -96,7 +96,7 @@ jobs:
     steps:
      # Setup
     - name: Checkout
-      uses: actions/checkout@v4
+      uses: actions/checkout@v5
       with:
         persist-credentials: false
 
@@ -114,7 +114,7 @@ jobs:
     steps:
     # Setup
     - name: Checkout
-      uses: actions/checkout@v4
+      uses: actions/checkout@v5
       with:
         persist-credentials: false
 
@@ -170,7 +170,7 @@ jobs:
     steps:
     # Setup
     - name: Checkout
-      uses: actions/checkout@v4
+      uses: actions/checkout@v5
       with:
         persist-credentials: false
 
diff --git a/src/tools/clippy/.github/workflows/clippy_pr.yml b/src/tools/clippy/.github/workflows/clippy_pr.yml
index 880ebd6e5d5..d91c638a8fb 100644
--- a/src/tools/clippy/.github/workflows/clippy_pr.yml
+++ b/src/tools/clippy/.github/workflows/clippy_pr.yml
@@ -24,7 +24,7 @@ jobs:
     steps:
     # Setup
     - name: Checkout
-      uses: actions/checkout@v4
+      uses: actions/checkout@v5
       with:
         # Unsetting this would make so that any malicious package could get our Github Token
         persist-credentials: false
diff --git a/src/tools/clippy/.github/workflows/deploy.yml b/src/tools/clippy/.github/workflows/deploy.yml
index ede19c11257..48c5bd36dbc 100644
--- a/src/tools/clippy/.github/workflows/deploy.yml
+++ b/src/tools/clippy/.github/workflows/deploy.yml
@@ -25,13 +25,13 @@ jobs:
     steps:
     # Setup
     - name: Checkout
-      uses: actions/checkout@v4
+      uses: actions/checkout@v5
       with:
         # Unsetting this would make so that any malicious package could get our Github Token
         persist-credentials: false
 
     - name: Checkout
-      uses: actions/checkout@v4
+      uses: actions/checkout@v5
       with:
         ref: ${{ env.TARGET_BRANCH }}
         path: 'out'
diff --git a/src/tools/clippy/.github/workflows/feature_freeze.yml b/src/tools/clippy/.github/workflows/feature_freeze.yml
deleted file mode 100644
index ec59be3e7f6..00000000000
--- a/src/tools/clippy/.github/workflows/feature_freeze.yml
+++ /dev/null
@@ -1,45 +0,0 @@
-name: Feature freeze check
-
-on:
-  pull_request_target:
-    types:
-      - opened
-    branches:
-      - master
-    paths:
-      - 'clippy_lints/src/declared_lints.rs'
-
-jobs:
-  auto-comment:
-    runs-on: ubuntu-latest
-
-    permissions:
-      pull-requests: write
-
-    # Do not in any case add code that runs anything coming from the  the content
-    # of the pull request, as malicious code would be able to access the private
-    # GitHub token.
-    steps:
-      - name: Add freeze warning comment
-        env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-          GITHUB_REPOSITORY: ${{ github.repository }}
-          PR_NUMBER: ${{ github.event.pull_request.number }}
-        run: |
-          COMMENT=$(echo "**Seems that you are trying to add a new lint!**\n\
-          \n\
-          We are currently in a [feature freeze](https://doc.rust-lang.org/nightly/clippy/development/feature_freeze.html), so we are delaying all lint-adding PRs to September 18 and [focusing on bugfixes](https://github.com/rust-lang/rust-clippy/issues/15086).\n\
-          \n\
-          Thanks a lot for your contribution, and sorry for the inconvenience.\n\
-          \n\
-          With ❤ from the Clippy team.\n\
-          \n\
-          @rustbot note Feature-freeze\n\
-          @rustbot blocked\n\
-          @rustbot label +A-lint"
-          )
-          curl -s -H "Authorization: Bearer $GITHUB_TOKEN" \
-             -H "Content-Type: application/vnd.github.raw+json" \
-             -X POST \
-             --data "{\"body\":\"${COMMENT}\"}" \
-             "https://api.github.com/repos/${GITHUB_REPOSITORY}/issues/${PR_NUMBER}/comments"
diff --git a/src/tools/clippy/.github/workflows/lintcheck.yml b/src/tools/clippy/.github/workflows/lintcheck.yml
index 003d0395739..390d6a0f747 100644
--- a/src/tools/clippy/.github/workflows/lintcheck.yml
+++ b/src/tools/clippy/.github/workflows/lintcheck.yml
@@ -24,7 +24,7 @@ jobs:
 
     steps:
     - name: Checkout
-      uses: actions/checkout@v4
+      uses: actions/checkout@v5
       with:
         fetch-depth: 2
         # Unsetting this would make so that any malicious package could get our Github Token
@@ -80,7 +80,7 @@ jobs:
 
     steps:
     - name: Checkout
-      uses: actions/checkout@v4
+      uses: actions/checkout@v5
       with:
         # Unsetting this would make so that any malicious package could get our Github Token
         persist-credentials: false
@@ -113,7 +113,7 @@ jobs:
 
     steps:
     - name: Checkout
-      uses: actions/checkout@v4
+      uses: actions/checkout@v5
       with:
         # Unsetting this would make so that any malicious package could get our Github Token
         persist-credentials: false
diff --git a/src/tools/clippy/.github/workflows/remark.yml b/src/tools/clippy/.github/workflows/remark.yml
index 7e7e26818c0..c9d350ee0b3 100644
--- a/src/tools/clippy/.github/workflows/remark.yml
+++ b/src/tools/clippy/.github/workflows/remark.yml
@@ -11,7 +11,7 @@ jobs:
     steps:
     # Setup
     - name: Checkout
-      uses: actions/checkout@v4
+      uses: actions/checkout@v5
       with:
         # Unsetting this would make so that any malicious package could get our Github Token
         persist-credentials: false
diff --git a/src/tools/clippy/.gitignore b/src/tools/clippy/.gitignore
index 36a4cdc1c35..666c4ceac4d 100644
--- a/src/tools/clippy/.gitignore
+++ b/src/tools/clippy/.gitignore
@@ -48,3 +48,6 @@ helper.txt
 
 # mdbook generated output
 /book/book
+
+# Remove jujutsu directory from search tools
+.jj
diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md
index bc60b1c57f7..3f26b9470e8 100644
--- a/src/tools/clippy/CHANGELOG.md
+++ b/src/tools/clippy/CHANGELOG.md
@@ -6,7 +6,152 @@ document.
 
 ## Unreleased / Beta / In Rust Nightly
 
-[4ef75291...master](https://github.com/rust-lang/rust-clippy/compare/4ef75291...master)
+[e9b7045...master](https://github.com/rust-lang/rust-clippy/compare/e9b7045...master)
+
+## Rust 1.90
+
+Current stable, released 2025-09-18
+
+[View all 118 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2025-06-13T15%3A55%3A04Z..2025-07-25T13%3A24%3A00Z+base%3Amaster)
+
+Note: This Clippy release does not introduce many new lints and is focused entirely on bug fixes — see
+[#15086](https://github.com/rust-lang/rust-clippy/issues/15086) for more details.
+
+## New Lints
+
+* Added [`manual_is_multiple_of`] to `complexity` [#14292](https://github.com/rust-lang/rust-clippy/pull/14292)
+* Added [`doc_broken_link`] to `pedantic` [#13696](https://github.com/rust-lang/rust-clippy/pull/13696)
+
+### Moves and Deprecations
+
+* Move [`uninlined_format_args`] to `pedantic` (from `style`, now allow-by-default) [#15287](https://github.com/rust-lang/rust-clippy/pull/15287)
+
+### Enhancements
+
+* [`or_fun_call`] now lints `Option::get_or_insert`, `Result::map_or`, `Option/Result::and` methods 
+  [#15071](https://github.com/rust-lang/rust-clippy/pull/15071) 
+  [#15073](https://github.com/rust-lang/rust-clippy/pull/15073) 
+  [#15074](https://github.com/rust-lang/rust-clippy/pull/15074)
+* [`incompatible_msrv`] now recognizes types exceeding MSRV 
+  [#15296](https://github.com/rust-lang/rust-clippy/pull/15296)
+* [`incompatible_msrv`] now checks the right MSRV when in a `const` context 
+  [#15297](https://github.com/rust-lang/rust-clippy/pull/15297)
+* [`zero_ptr`] now lints in `const` context as well 
+  [#15152](https://github.com/rust-lang/rust-clippy/pull/15152)
+* [`map_identity`],[`flat_map_identity`] now recognizes `|[x, y]| [x, y]` as an identity function
+  [#15229](https://github.com/rust-lang/rust-clippy/pull/15229)
+* [`exit`] no longer fails on the main function when using `--test` or `--all-targets` flag 
+  [#15222](https://github.com/rust-lang/rust-clippy/pull/15222)
+
+### False Positive Fixes
+
+* [`if_then_some_else_none`] fixed FP when require type coercion
+  [#15267](https://github.com/rust-lang/rust-clippy/pull/15267)
+* [`module_name_repetitions`] fixed FP on exported macros
+  [#15319](https://github.com/rust-lang/rust-clippy/pull/15319)
+* [`unused_async`] fixed FP on function with `todo!`
+  [#15308](https://github.com/rust-lang/rust-clippy/pull/15308)
+* [`useless_attribute`] fixed FP when using `#[expect(redundant_imports)]` and similar lint attributes 
+  on `use` statements
+  [#15318](https://github.com/rust-lang/rust-clippy/pull/15318)
+* [`pattern_type_mismatch`] fixed FP in external macro
+  [#15306](https://github.com/rust-lang/rust-clippy/pull/15306)
+* [`large_enum_variant`] fixed FP by not suggesting `Box` in `no_std` mode
+  [#15241](https://github.com/rust-lang/rust-clippy/pull/15241)
+* [`missing_inline_in_public_items`] fixed FP on functions with `extern`
+  [#15313](https://github.com/rust-lang/rust-clippy/pull/15313)
+* [`needless_range_loop`] fixed FP on array literals
+  [#15314](https://github.com/rust-lang/rust-clippy/pull/15314)
+* [`ptr_as_ptr`] fixed wrong suggestions with turbo fish
+  [#15289](https://github.com/rust-lang/rust-clippy/pull/15289)
+* [`range_plus_one`], [`range_minus_one`] fixed FP by restricting lint to cases where it is safe 
+  to switch the range type
+  [#14432](https://github.com/rust-lang/rust-clippy/pull/14432)
+* [`ptr_arg`] fixed FP with underscore binding to `&T` or `&mut T` argument
+  [#15105](https://github.com/rust-lang/rust-clippy/pull/15105)
+* [`unsafe_derive_deserialize`] fixed FP caused by the standard library's `pin!()` macro
+  [#15137](https://github.com/rust-lang/rust-clippy/pull/15137)
+* [`unused_trait_names`] fixed FP in macros
+  [#14947](https://github.com/rust-lang/rust-clippy/pull/14947)
+* [`expect_fun_call`] fixed invalid suggestions
+  [#15122](https://github.com/rust-lang/rust-clippy/pull/15122)
+* [`manual_is_multiple_of`] fixed various false positives
+  [#15205](https://github.com/rust-lang/rust-clippy/pull/15205)
+* [`manual_assert`] fixed wrong suggestions for macros
+  [#15264](https://github.com/rust-lang/rust-clippy/pull/15264)
+* [`expect_used`] fixed false negative when meeting `Option::ok().expect`
+  [#15253](https://github.com/rust-lang/rust-clippy/pull/15253)
+* [`manual_abs_diff`] fixed wrong suggestions behind refs
+  [#15265](https://github.com/rust-lang/rust-clippy/pull/15265)
+* [`approx_constant`] fixed FP with overly precise literals and non trivially formatted numerals
+  [#15236](https://github.com/rust-lang/rust-clippy/pull/15236)
+* [`arithmetic_side_effects`] fixed FP on `NonZeroU*.get() - 1`
+  [#15238](https://github.com/rust-lang/rust-clippy/pull/15238)
+* [`legacy_numeric_constants`] fixed suggestion when call is inside parentheses
+  [#15191](https://github.com/rust-lang/rust-clippy/pull/15191)
+* [`manual_is_variant_and`] fixed inverted suggestions that could lead to code with different semantics
+  [#15206](https://github.com/rust-lang/rust-clippy/pull/15206)
+* [`unnecessary_map_or`] fixed FP by not proposing to replace `map_or` call when types wouldn't be correct
+  [#15181](https://github.com/rust-lang/rust-clippy/pull/15181)
+* [`return_and_then`] fixed FP in case of a partially used expression
+  [#15115](https://github.com/rust-lang/rust-clippy/pull/15115)
+* [`manual_let_else`] fixed FP with binding subpattern with unused name
+  [#15118](https://github.com/rust-lang/rust-clippy/pull/15118)
+* [`suboptimal_flops`] fixed FP with ambiguous float types in mul_add suggestions
+  [#15133](https://github.com/rust-lang/rust-clippy/pull/15133)
+* [`borrow_as_ptr`] fixed FP by not removing cast to trait object pointer
+  [#15145](https://github.com/rust-lang/rust-clippy/pull/15145)
+* [`unnecessary_operation`] fixed FP by not removing casts if they are useful to type the expression
+  [#15182](https://github.com/rust-lang/rust-clippy/pull/15182)
+* [`empty_loop`] fixed FP on intrinsic function declaration
+  [#15201](https://github.com/rust-lang/rust-clippy/pull/15201)
+* [`doc_nested_refdefs`] fixed FP where task lists are reported as refdefs
+  [#15146](https://github.com/rust-lang/rust-clippy/pull/15146)
+* [`std_instead_of_core`] fixed FP when not all items come from the new crate
+  [#15165](https://github.com/rust-lang/rust-clippy/pull/15165)
+* [`swap_with_temporary`] fixed FP leading to different semantics being suggested
+  [#15172](https://github.com/rust-lang/rust-clippy/pull/15172)
+* [`cast_possible_truncation`] fixed FP by not giving suggestions inside const context
+  [#15164](https://github.com/rust-lang/rust-clippy/pull/15164)
+* [`missing_panics_doc`] fixed FP by allowing unwrap() and expect()s in const-only contexts
+  [#15170](https://github.com/rust-lang/rust-clippy/pull/15170)
+* [`disallowed_script_idents`] fixed FP on identifiers with `_`
+  [#15123](https://github.com/rust-lang/rust-clippy/pull/15123)
+* [`wildcard_enum_match_arm`] fixed wrong suggestions with raw identifiers
+  [#15093](https://github.com/rust-lang/rust-clippy/pull/15093)
+* [`borrow_deref_ref`] fixed FP by not proposing replacing a reborrow when the reborrow is itself mutably borrowed
+  [#14967](https://github.com/rust-lang/rust-clippy/pull/14967)
+* [`manual_ok_err`] fixed wrong suggestions with references
+  [#15053](https://github.com/rust-lang/rust-clippy/pull/15053)
+* [`identity_op`] fixed FP when encountering `Default::default()`
+  [#15028](https://github.com/rust-lang/rust-clippy/pull/15028)
+* [`exhaustive_structs`] fixed FP on structs with default valued field
+  [#15022](https://github.com/rust-lang/rust-clippy/pull/15022)
+* [`collapsible_else_if`] fixed FP on conditionally compiled stmt
+  [#14906](https://github.com/rust-lang/rust-clippy/pull/14906)
+* [`missing_const_for_fn`] fixed FP by checking MSRV before emitting lint on function containing 
+  non-`Sized` trait bounds
+  [#15080](https://github.com/rust-lang/rust-clippy/pull/15080)
+* [`question_mark`] fixed FP when else branch of let-else contains `#[cfg]`
+  [#15082](https://github.com/rust-lang/rust-clippy/pull/15082)
+
+### ICE Fixes
+
+* [`single_match`] fixed ICE with deref patterns and string literals 
+  [#15124](https://github.com/rust-lang/rust-clippy/pull/15124)
+* [`needless_doctest_main`] fixed panic when doctest is invalid
+  [#15052](https://github.com/rust-lang/rust-clippy/pull/15052)
+* [`zero_sized_map_values`] do not attempt to compute size of a type with escaping lifetimes
+  [#15434](https://github.com/rust-lang/rust-clippy/pull/15434)
+
+### Documentation Improvements
+
+* [`manual_is_variant_and`] improved documentation to include equality comparison patterns 
+  [#15239](https://github.com/rust-lang/rust-clippy/pull/15239)
+* [`uninlined_format_args`] improved documentation with example of how to fix a `{:?}` parameter
+  [#15228](https://github.com/rust-lang/rust-clippy/pull/15228)
+* [`undocumented_unsafe_blocks`] improved documentation wording 
+  [#15213](https://github.com/rust-lang/rust-clippy/pull/15213)
 
 ## Rust 1.89
 
@@ -30,6 +175,8 @@ Current stable, released 2025-08-07
 * Removed superseded lints: `transmute_float_to_int`, `transmute_int_to_char`,
   `transmute_int_to_float`, `transmute_num_to_bytes` (now in rustc)
   [#14703](https://github.com/rust-lang/rust-clippy/pull/14703)
+* Move [`uninlined_format_args`] to `pedantic` (from `style`, now allow-by-default)
+  [#15287](https://github.com/rust-lang/rust-clippy/pull/15287)
 
 ### Enhancements
 
@@ -74,6 +221,9 @@ Current stable, released 2025-08-07
   [#14719](https://github.com/rust-lang/rust-clippy/pull/14719)
 * [`unnecessary_to_owned`] fixed FP when map key is a reference
   [#14834](https://github.com/rust-lang/rust-clippy/pull/14834)
+* [`swap_with_temporary`]: fix false positive leading to different semantics
+  being suggested, and use the right number of dereferences in suggestion
+  [#15172](https://github.com/rust-lang/rust-clippy/pull/15172)
 
 ### ICE Fixes
 
@@ -6403,6 +6553,7 @@ Released 2018-09-13
 [`redundant_feature_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_feature_names
 [`redundant_field_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names
 [`redundant_guards`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_guards
+[`redundant_iter_cloned`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_iter_cloned
 [`redundant_locals`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_locals
 [`redundant_pattern`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern
 [`redundant_pattern_matching`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_pattern_matching
@@ -6707,6 +6858,7 @@ Released 2018-09-13
 [`check-inconsistent-struct-field-initializers`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-inconsistent-struct-field-initializers
 [`check-private-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-private-items
 [`cognitive-complexity-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#cognitive-complexity-threshold
+[`const-literal-digits-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#const-literal-digits-threshold
 [`disallowed-macros`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-macros
 [`disallowed-methods`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-methods
 [`disallowed-names`]: https://doc.rust-lang.org/clippy/lint_configuration.html#disallowed-names
diff --git a/src/tools/clippy/CONTRIBUTING.md b/src/tools/clippy/CONTRIBUTING.md
index 42ed624ec21..f7f0a1ce249 100644
--- a/src/tools/clippy/CONTRIBUTING.md
+++ b/src/tools/clippy/CONTRIBUTING.md
@@ -17,7 +17,7 @@ All contributors are expected to follow the [Rust Code of Conduct].
   - [High level approach](#high-level-approach)
   - [Finding something to fix/improve](#finding-something-to-fiximprove)
   - [Getting code-completion for rustc internals to work](#getting-code-completion-for-rustc-internals-to-work)
-    - [IntelliJ Rust](#intellij-rust)
+    - [RustRover](#rustrover)
     - [Rust Analyzer](#rust-analyzer)
   - [How Clippy works](#how-clippy-works)
   - [Issue and PR triage](#issue-and-pr-triage)
@@ -92,22 +92,22 @@ an AST expression).
 
 ## Getting code-completion for rustc internals to work
 
-### IntelliJ Rust
-Unfortunately, [`IntelliJ Rust`][IntelliJ_rust_homepage] does not (yet?) understand how Clippy uses compiler-internals
+### RustRover
+Unfortunately, [`RustRover`][RustRover_homepage] does not (yet?) understand how Clippy uses compiler-internals
 using `extern crate` and it also needs to be able to read the source files of the rustc-compiler which are not
 available via a `rustup` component at the time of writing.
 To work around this, you need to have a copy of the [rustc-repo][rustc_repo] available which can be obtained via
 `git clone https://github.com/rust-lang/rust/`.
 Then you can run a `cargo dev` command to automatically make Clippy use the rustc-repo via path-dependencies
-which `IntelliJ Rust` will be able to understand.
+which `RustRover` will be able to understand.
 Run `cargo dev setup intellij --repo-path <repo-path>` where `<repo-path>` is a path to the rustc repo
 you just cloned.
 The command will add path-dependencies pointing towards rustc-crates inside the rustc repo to
-Clippy's `Cargo.toml`s and should allow `IntelliJ Rust` to understand most of the types that Clippy uses.
+Clippy's `Cargo.toml`s and should allow `RustRover` to understand most of the types that Clippy uses.
 Just make sure to remove the dependencies again before finally making a pull request!
 
 [rustc_repo]: https://github.com/rust-lang/rust/
-[IntelliJ_rust_homepage]: https://intellij-rust.github.io/
+[RustRover_homepage]: https://www.jetbrains.com/rust/
 
 ### Rust Analyzer
 For [`rust-analyzer`][ra_homepage] to work correctly make sure that in the `rust-analyzer` configuration you set
diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml
index daf1c98cdc9..e0638349989 100644
--- a/src/tools/clippy/Cargo.toml
+++ b/src/tools/clippy/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "clippy"
-version = "0.1.91"
+version = "0.1.92"
 description = "A bunch of helpful lints to avoid common pitfalls in Rust"
 repository = "https://github.com/rust-lang/rust-clippy"
 readme = "README.md"
@@ -64,3 +64,7 @@ harness = false
 [[test]]
 name = "dogfood"
 harness = false
+
+[lints.rust.unexpected_cfgs]
+level = "warn"
+check-cfg = ['cfg(bootstrap)']
diff --git a/src/tools/clippy/book/src/README.md b/src/tools/clippy/book/src/README.md
index db73b49ecc2..5d2c3972b06 100644
--- a/src/tools/clippy/book/src/README.md
+++ b/src/tools/clippy/book/src/README.md
@@ -1,9 +1,5 @@
 # Clippy
 
-[### IMPORTANT NOTE FOR CONTRIBUTORS ================](development/feature_freeze.md)
-
-----
-
 [![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
diff --git a/src/tools/clippy/book/src/SUMMARY.md b/src/tools/clippy/book/src/SUMMARY.md
index b66c3481e49..39fe7358ed8 100644
--- a/src/tools/clippy/book/src/SUMMARY.md
+++ b/src/tools/clippy/book/src/SUMMARY.md
@@ -13,7 +13,6 @@
     - [GitLab CI](continuous_integration/gitlab.md)
     - [Travis CI](continuous_integration/travis.md)
 - [Development](development/README.md)
-    - [IMPORTANT: FEATURE FREEZE](development/feature_freeze.md)
     - [Basics](development/basics.md)
     - [Adding Lints](development/adding_lints.md)
     - [Defining Lints](development/defining_lints.md)
diff --git a/src/tools/clippy/book/src/continuous_integration/github_actions.md b/src/tools/clippy/book/src/continuous_integration/github_actions.md
index b588c8f0f02..62d32446d92 100644
--- a/src/tools/clippy/book/src/continuous_integration/github_actions.md
+++ b/src/tools/clippy/book/src/continuous_integration/github_actions.md
@@ -15,7 +15,7 @@ jobs:
   clippy_check:
     runs-on: ubuntu-latest
     steps:
-      - uses: actions/checkout@v4
+      - uses: actions/checkout@v5
       - name: Run Clippy
         run: cargo clippy --all-targets --all-features
 ```
diff --git a/src/tools/clippy/book/src/development/adding_lints.md b/src/tools/clippy/book/src/development/adding_lints.md
index a42a2983744..2b89e94cf8f 100644
--- a/src/tools/clippy/book/src/development/adding_lints.md
+++ b/src/tools/clippy/book/src/development/adding_lints.md
@@ -1,8 +1,5 @@
 # Adding a new lint
 
-[### IMPORTANT NOTE FOR CONTRIBUTORS ================](feature_freeze.md)
-
-
 You are probably here because you want to add a new lint to Clippy. If this is
 the first time you're contributing to Clippy, this document guides you through
 creating an example lint from scratch.
diff --git a/src/tools/clippy/book/src/development/basics.md b/src/tools/clippy/book/src/development/basics.md
index fc405249bcf..19f626ab804 100644
--- a/src/tools/clippy/book/src/development/basics.md
+++ b/src/tools/clippy/book/src/development/basics.md
@@ -95,7 +95,7 @@ cargo dev new_lint
 cargo dev deprecate
 # automatically formatting all code before each commit
 cargo dev setup git-hook
-# (experimental) Setup Clippy to work with IntelliJ-Rust
+# (experimental) Setup Clippy to work with RustRover
 cargo dev setup intellij
 # runs the `dogfood` tests
 cargo dev dogfood
@@ -103,7 +103,7 @@ cargo dev dogfood
 
 More about [intellij] command usage and reasons.
 
-[intellij]: https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md#intellij-rust
+[intellij]: https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md#rustrover
 
 ## lintcheck
 
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 e23b32039c9..3bec3ce33af 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
@@ -141,7 +141,7 @@ impl LateLintPass<'_> for MyStructLint {
             // we are looking for the `DefId` of `Drop` trait in lang items
             .drop_trait()
             // then we use it with our type `ty` by calling `implements_trait` from Clippy's utils
-            .map_or(false, |id| implements_trait(cx, ty, id, &[])) {
+            .is_some_and(|id| implements_trait(cx, ty, id, &[])) {
                 // `expr` implements `Drop` trait
             }
     }
diff --git a/src/tools/clippy/book/src/development/feature_freeze.md b/src/tools/clippy/book/src/development/feature_freeze.md
deleted file mode 100644
index 260cb136cc0..00000000000
--- a/src/tools/clippy/book/src/development/feature_freeze.md
+++ /dev/null
@@ -1,55 +0,0 @@
-# IMPORTANT: FEATURE FREEZE
-
-This is a temporary notice.
-
-From the 26th of June until the 18th of September we will perform a feature freeze. Only bugfix PRs will be reviewed
-except already open ones. Every feature-adding PR opened in between those dates will be moved into a
-milestone to be reviewed separately at another time.
-
-We do this because of the long backlog of bugs that need to be addressed
-in order to continue being the state-of-the-art linter that Clippy has become known for being.
-
-## For contributors
-
-If you are a contributor or are planning to become one, **please do not open a lint-adding PR**, we have lots of open
-bugs of all levels of difficulty that you can address instead!
-
-We currently have about 800 lints, each one posing a maintainability challenge that needs to account to every possible
-use case of the whole ecosystem. Bugs are natural in every software, but the Clippy team considers that Clippy needs a
-refinement period.
-
-If you open a PR at this time, we will not review it but push it into a milestone until the refinement period ends,
-adding additional load into our reviewing schedules.
-
-## I want to help, what can I do
-
-Thanks a lot to everyone who wants to help Clippy become better software in this feature freeze period!
-If you'd like to help, making a bugfix, making sure that it works, and opening a PR is a great step!
-
-To find things to fix, go to the [tracking issue][tracking_issue], find an issue that you like, go there and claim that
-issue with `@rustbot claim`.
-
-As a general metric and always taking into account your skill and knowledge level, you can use this guide:
-
-- 🟥 [ICEs][search_ice], these are compiler errors that causes Clippy to panic and crash. Usually involves high-level
-debugging, sometimes interacting directly with the upstream compiler. Difficult to fix but a great challenge that
-improves a lot developer workflows!
-
-- 🟧 [Suggestion causes bug][sugg_causes_bug], Clippy suggested code that changed logic in some silent way.
-Unacceptable, as this may have disastrous consequences. Easier to fix than ICEs
-
-- 🟨 [Suggestion causes error][sugg_causes_error], Clippy suggested code snippet that caused a compiler error
-when applied. We need to make sure that Clippy doesn't suggest using a variable twice at the same time or similar
-easy-to-happen occurrences.
-
-- 🟩 [False positives][false_positive], a lint should not have fired, the easiest of them all, as this is "just"
-identifying the root of a false positive and making an exception for those cases.
-
-Note that false negatives do not have priority unless the case is very clear, as they are a feature-request in a
-trench coat.
-
-[search_ice]: https://github.com/rust-lang/rust-clippy/issues?q=sort%3Aupdated-desc+state%3Aopen+label%3A%22I-ICE%22
-[sugg_causes_bug]: https://github.com/rust-lang/rust-clippy/issues?q=sort%3Aupdated-desc%20state%3Aopen%20label%3AI-suggestion-causes-bug
-[sugg_causes_error]: https://github.com/rust-lang/rust-clippy/issues?q=sort%3Aupdated-desc%20state%3Aopen%20label%3AI-suggestion-causes-error%20
-[false_positive]: https://github.com/rust-lang/rust-clippy/issues?q=sort%3Aupdated-desc%20state%3Aopen%20label%3AI-false-positive
-[tracking_issue]: https://github.com/rust-lang/rust-clippy/issues/15086
diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md
index 7f16f3a9810..c2d080cd96a 100644
--- a/src/tools/clippy/book/src/lint_configuration.md
+++ b/src/tools/clippy/book/src/lint_configuration.md
@@ -485,6 +485,16 @@ The maximum cognitive complexity a function can have
 * [`cognitive_complexity`](https://rust-lang.github.io/rust-clippy/master/index.html#cognitive_complexity)
 
 
+## `const-literal-digits-threshold`
+The minimum digits a const float literal must have to supress the `excessive_precicion` lint
+
+**Default Value:** `30`
+
+---
+**Affected lints:**
+* [`excessive_precision`](https://rust-lang.github.io/rust-clippy/master/index.html#excessive_precision)
+
+
 ## `disallowed-macros`
 The list of disallowed macros, written as fully qualified paths.
 
@@ -555,7 +565,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", "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", "NixOS", "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", "InfiniBand", "RoCE", "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", "PowerPC", "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", "NixOS", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]`
 
 ---
 **Affected lints:**
@@ -873,7 +883,6 @@ The minimum rust version that the project supports. Defaults to the `rust-versio
 * [`needless_borrow`](https://rust-lang.github.io/rust-clippy/master/index.html#needless_borrow)
 * [`non_std_lazy_statics`](https://rust-lang.github.io/rust-clippy/master/index.html#non_std_lazy_statics)
 * [`option_as_ref_deref`](https://rust-lang.github.io/rust-clippy/master/index.html#option_as_ref_deref)
-* [`option_map_unwrap_or`](https://rust-lang.github.io/rust-clippy/master/index.html#option_map_unwrap_or)
 * [`ptr_as_ptr`](https://rust-lang.github.io/rust-clippy/master/index.html#ptr_as_ptr)
 * [`question_mark`](https://rust-lang.github.io/rust-clippy/master/index.html#question_mark)
 * [`redundant_field_names`](https://rust-lang.github.io/rust-clippy/master/index.html#redundant_field_names)
@@ -881,7 +890,6 @@ The minimum rust version that the project supports. Defaults to the `rust-versio
 * [`repeat_vec_with_capacity`](https://rust-lang.github.io/rust-clippy/master/index.html#repeat_vec_with_capacity)
 * [`same_item_push`](https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push)
 * [`seek_from_current`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_from_current)
-* [`seek_rewind`](https://rust-lang.github.io/rust-clippy/master/index.html#seek_rewind)
 * [`to_digit_is_some`](https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some)
 * [`transmute_ptr_to_ref`](https://rust-lang.github.io/rust-clippy/master/index.html#transmute_ptr_to_ref)
 * [`tuple_array_conversions`](https://rust-lang.github.io/rust-clippy/master/index.html#tuple_array_conversions)
diff --git a/src/tools/clippy/clippy.toml b/src/tools/clippy/clippy.toml
index 77573105d86..d9bcfd17606 100644
--- a/src/tools/clippy/clippy.toml
+++ b/src/tools/clippy/clippy.toml
@@ -6,12 +6,12 @@ lint-commented-code = true
 
 [[disallowed-methods]]
 path = "rustc_lint::context::LintContext::lint"
-reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead"
+reason = "this function does not add a link to our documentation; please use the `clippy_utils::diagnostics::span_lint*` functions instead"
 
 [[disallowed-methods]]
 path = "rustc_lint::context::LintContext::span_lint"
-reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead"
+reason = "this function does not add a link to our documentation; please use the `clippy_utils::diagnostics::span_lint*` functions instead"
 
 [[disallowed-methods]]
 path = "rustc_middle::ty::context::TyCtxt::node_span_lint"
-reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead"
+reason = "this function does not add a link to our documentation; please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead"
diff --git a/src/tools/clippy/clippy_config/Cargo.toml b/src/tools/clippy/clippy_config/Cargo.toml
index 6ad2cf0d0b1..f8c748290e4 100644
--- a/src/tools/clippy/clippy_config/Cargo.toml
+++ b/src/tools/clippy/clippy_config/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "clippy_config"
-version = "0.1.91"
+version = "0.1.92"
 edition = "2024"
 publish = false
 
diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs
index 8167d75583e..2f28f6175ad 100644
--- a/src/tools/clippy/clippy_config/src/conf.rs
+++ b/src/tools/clippy/clippy_config/src/conf.rs
@@ -33,8 +33,9 @@ const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[
     "GPLv2", "GPLv3",
     "GitHub", "GitLab",
     "IPv4", "IPv6",
+    "InfiniBand", "RoCE",
     "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript",
-    "WebAssembly",
+    "PowerPC", "WebAssembly",
     "NaN", "NaNs",
     "OAuth", "GraphQL",
     "OCaml",
@@ -569,6 +570,9 @@ define_Conf! {
     /// The maximum cognitive complexity a function can have
     #[lints(cognitive_complexity)]
     cognitive_complexity_threshold: u64 = 25,
+    /// The minimum digits a const float literal must have to supress the `excessive_precicion` lint
+    #[lints(excessive_precision)]
+    const_literal_digits_threshold: usize = 30,
     /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY.
     ///
     /// Use the Cognitive Complexity lint instead.
@@ -775,7 +779,6 @@ define_Conf! {
         needless_borrow,
         non_std_lazy_statics,
         option_as_ref_deref,
-        option_map_unwrap_or,
         ptr_as_ptr,
         question_mark,
         redundant_field_names,
@@ -783,7 +786,6 @@ define_Conf! {
         repeat_vec_with_capacity,
         same_item_push,
         seek_from_current,
-        seek_rewind,
         to_digit_is_some,
         transmute_ptr_to_ref,
         tuple_array_conversions,
diff --git a/src/tools/clippy/clippy_dev/src/dogfood.rs b/src/tools/clippy/clippy_dev/src/dogfood.rs
index 7e9d92458d0..d0fca952b93 100644
--- a/src/tools/clippy/clippy_dev/src/dogfood.rs
+++ b/src/tools/clippy/clippy_dev/src/dogfood.rs
@@ -1,35 +1,28 @@
-use crate::utils::exit_if_err;
-use std::process::Command;
+use crate::utils::{cargo_cmd, run_exit_on_err};
+use itertools::Itertools;
 
 /// # Panics
 ///
 /// Panics if unable to run the dogfood test
 #[allow(clippy::fn_params_excessive_bools)]
 pub fn dogfood(fix: bool, allow_dirty: bool, allow_staged: bool, allow_no_vcs: bool) {
-    let mut cmd = Command::new("cargo");
-
-    cmd.args(["test", "--test", "dogfood"])
-        .args(["--features", "internal"])
-        .args(["--", "dogfood_clippy", "--nocapture"]);
-
-    let mut dogfood_args = Vec::new();
-    if fix {
-        dogfood_args.push("--fix");
-    }
-
-    if allow_dirty {
-        dogfood_args.push("--allow-dirty");
-    }
-
-    if allow_staged {
-        dogfood_args.push("--allow-staged");
-    }
-
-    if allow_no_vcs {
-        dogfood_args.push("--allow-no-vcs");
-    }
-
-    cmd.env("__CLIPPY_DOGFOOD_ARGS", dogfood_args.join(" "));
-
-    exit_if_err(cmd.status());
+    run_exit_on_err(
+        "cargo test",
+        cargo_cmd()
+            .args(["test", "--test", "dogfood"])
+            .args(["--features", "internal"])
+            .args(["--", "dogfood_clippy", "--nocapture"])
+            .env(
+                "__CLIPPY_DOGFOOD_ARGS",
+                [
+                    if fix { "--fix" } else { "" },
+                    if allow_dirty { "--allow-dirty" } else { "" },
+                    if allow_staged { "--allow-staged" } else { "" },
+                    if allow_no_vcs { "--allow-no-vcs" } else { "" },
+                ]
+                .iter()
+                .filter(|x| !x.is_empty())
+                .join(" "),
+            ),
+    );
 }
diff --git a/src/tools/clippy/clippy_dev/src/lib.rs b/src/tools/clippy/clippy_dev/src/lib.rs
index 40aadf4589a..16f413e0c86 100644
--- a/src/tools/clippy/clippy_dev/src/lib.rs
+++ b/src/tools/clippy/clippy_dev/src/lib.rs
@@ -15,8 +15,7 @@
 )]
 #![allow(clippy::missing_panics_doc)]
 
-// The `rustc_driver` crate seems to be required in order to use the `rust_lexer` crate.
-#[allow(unused_extern_crates)]
+#[expect(unused_extern_crates, reason = "required to link to rustc crates")]
 extern crate rustc_driver;
 extern crate rustc_lexer;
 extern crate rustc_literal_escaper;
@@ -32,4 +31,6 @@ pub mod serve;
 pub mod setup;
 pub mod sync;
 pub mod update_lints;
-pub mod utils;
+
+mod utils;
+pub use utils::{ClippyInfo, UpdateMode};
diff --git a/src/tools/clippy/clippy_dev/src/lint.rs b/src/tools/clippy/clippy_dev/src/lint.rs
index 0d66f167a38..2d9f563cdae 100644
--- a/src/tools/clippy/clippy_dev/src/lint.rs
+++ b/src/tools/clippy/clippy_dev/src/lint.rs
@@ -1,19 +1,18 @@
-use crate::utils::{cargo_clippy_path, exit_if_err};
-use std::process::{self, Command};
+use crate::utils::{ErrAction, cargo_cmd, expect_action, run_exit_on_err};
+use std::process::Command;
 use std::{env, fs};
 
-pub fn run<'a>(path: &str, edition: &str, args: impl Iterator<Item = &'a String>) {
-    let is_file = match fs::metadata(path) {
-        Ok(metadata) => metadata.is_file(),
-        Err(e) => {
-            eprintln!("Failed to read {path}: {e:?}");
-            process::exit(1);
-        },
-    };
+#[cfg(not(windows))]
+static CARGO_CLIPPY_EXE: &str = "cargo-clippy";
+#[cfg(windows)]
+static CARGO_CLIPPY_EXE: &str = "cargo-clippy.exe";
 
+pub fn run<'a>(path: &str, edition: &str, args: impl Iterator<Item = &'a String>) {
+    let is_file = expect_action(fs::metadata(path), ErrAction::Read, path).is_file();
     if is_file {
-        exit_if_err(
-            Command::new(env::var("CARGO").unwrap_or_else(|_| "cargo".into()))
+        run_exit_on_err(
+            "cargo run",
+            cargo_cmd()
                 .args(["run", "--bin", "clippy-driver", "--"])
                 .args(["-L", "./target/debug"])
                 .args(["-Z", "no-codegen"])
@@ -21,24 +20,25 @@ pub fn run<'a>(path: &str, edition: &str, args: impl Iterator<Item = &'a String>
                 .arg(path)
                 .args(args)
                 // Prevent rustc from creating `rustc-ice-*` files the console output is enough.
-                .env("RUSTC_ICE", "0")
-                .status(),
+                .env("RUSTC_ICE", "0"),
         );
     } else {
-        exit_if_err(
-            Command::new(env::var("CARGO").unwrap_or_else(|_| "cargo".into()))
-                .arg("build")
-                .status(),
-        );
-
-        let status = Command::new(cargo_clippy_path())
-            .arg("clippy")
-            .args(args)
-            // Prevent rustc from creating `rustc-ice-*` files the console output is enough.
-            .env("RUSTC_ICE", "0")
-            .current_dir(path)
-            .status();
+        // Ideally this would just be `cargo run`, but the working directory needs to be
+        // set to clippy's directory when building, and the target project's directory
+        // when running clippy. `cargo` can only set a single working directory for both
+        // when using `run`.
+        run_exit_on_err("cargo build", cargo_cmd().arg("build"));
 
-        exit_if_err(status);
+        let mut exe = env::current_exe().expect("failed to get current executable name");
+        exe.set_file_name(CARGO_CLIPPY_EXE);
+        run_exit_on_err(
+            "cargo clippy",
+            Command::new(exe)
+                .arg("clippy")
+                .args(args)
+                // Prevent rustc from creating `rustc-ice-*` files the console output is enough.
+                .env("RUSTC_ICE", "0")
+                .current_dir(path),
+        );
     }
 }
diff --git a/src/tools/clippy/clippy_dev/src/main.rs b/src/tools/clippy/clippy_dev/src/main.rs
index 26aa269fb63..5fef231f6ca 100644
--- a/src/tools/clippy/clippy_dev/src/main.rs
+++ b/src/tools/clippy/clippy_dev/src/main.rs
@@ -4,14 +4,15 @@
 
 use clap::{Args, Parser, Subcommand};
 use clippy_dev::{
-    deprecate_lint, dogfood, fmt, lint, new_lint, release, rename_lint, serve, setup, sync, update_lints, utils,
+    ClippyInfo, UpdateMode, deprecate_lint, dogfood, fmt, lint, new_lint, release, rename_lint, serve, setup, sync,
+    update_lints,
 };
 use std::convert::Infallible;
 use std::env;
 
 fn main() {
     let dev = Dev::parse();
-    let clippy = utils::ClippyInfo::search_for_manifest();
+    let clippy = ClippyInfo::search_for_manifest();
     if let Err(e) = env::set_current_dir(&clippy.path) {
         panic!("error setting current directory to `{}`: {e}", clippy.path.display());
     }
@@ -26,8 +27,8 @@ fn main() {
             allow_staged,
             allow_no_vcs,
         } => dogfood::dogfood(fix, allow_dirty, allow_staged, allow_no_vcs),
-        DevCommand::Fmt { check } => fmt::run(utils::UpdateMode::from_check(check)),
-        DevCommand::UpdateLints { check } => update_lints::update(utils::UpdateMode::from_check(check)),
+        DevCommand::Fmt { check } => fmt::run(UpdateMode::from_check(check)),
+        DevCommand::UpdateLints { check } => update_lints::update(UpdateMode::from_check(check)),
         DevCommand::NewLint {
             pass,
             name,
@@ -35,7 +36,7 @@ fn main() {
             r#type,
             msrv,
         } => match new_lint::create(clippy.version, pass, &name, &category, r#type.as_deref(), msrv) {
-            Ok(()) => update_lints::update(utils::UpdateMode::Change),
+            Ok(()) => update_lints::update(UpdateMode::Change),
             Err(e) => eprintln!("Unable to create lint: {e}"),
         },
         DevCommand::Setup(SetupCommand { subcommand }) => match subcommand {
diff --git a/src/tools/clippy/clippy_dev/src/serve.rs b/src/tools/clippy/clippy_dev/src/serve.rs
index 498ffeba9d6..d9e01813381 100644
--- a/src/tools/clippy/clippy_dev/src/serve.rs
+++ b/src/tools/clippy/clippy_dev/src/serve.rs
@@ -1,7 +1,11 @@
+use crate::utils::{ErrAction, cargo_cmd, expect_action};
+use core::fmt::Display;
+use core::mem;
 use std::path::Path;
 use std::process::Command;
 use std::time::{Duration, SystemTime};
-use std::{env, thread};
+use std::{fs, thread};
+use walkdir::WalkDir;
 
 #[cfg(windows)]
 const PYTHON: &str = "python";
@@ -18,56 +22,83 @@ pub fn run(port: u16, lint: Option<String>) -> ! {
         Some(lint) => format!("http://localhost:{port}/#{lint}"),
     });
 
+    let mut last_update = mtime("util/gh-pages/index.html");
     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 times.iter().any(|&time| index_time < time) {
-            Command::new(env::var("CARGO").unwrap_or_else(|_| "cargo".into()))
-                .arg("collect-metadata")
-                .spawn()
-                .unwrap()
-                .wait()
-                .unwrap();
+        if is_metadata_outdated(mem::replace(&mut last_update, SystemTime::now())) {
+            // Ignore the command result; we'll fall back to displaying the old metadata.
+            let _ = expect_action(
+                cargo_cmd().arg("collect-metadata").status(),
+                ErrAction::Run,
+                "cargo collect-metadata",
+            );
+            last_update = SystemTime::now();
         }
+
+        // Only start the web server the first time around.
         if let Some(url) = url.take() {
             thread::spawn(move || {
-                let mut child = Command::new(PYTHON)
-                    .arg("-m")
-                    .arg("http.server")
-                    .arg(port.to_string())
-                    .current_dir("util/gh-pages")
-                    .spawn()
-                    .unwrap();
+                let mut child = expect_action(
+                    Command::new(PYTHON)
+                        .args(["-m", "http.server", port.to_string().as_str()])
+                        .current_dir("util/gh-pages")
+                        .spawn(),
+                    ErrAction::Run,
+                    "python -m http.server",
+                );
                 // Give some time for python to start
                 thread::sleep(Duration::from_millis(500));
                 // Launch browser after first export.py has completed and http.server is up
                 let _result = opener::open(url);
-                child.wait().unwrap();
+                expect_action(child.wait(), ErrAction::Run, "python -m http.server");
             });
         }
+
+        // Delay to avoid updating the metadata too aggressively.
         thread::sleep(Duration::from_millis(1000));
     }
 }
 
-fn mtime(path: impl AsRef<Path>) -> SystemTime {
-    let path = path.as_ref();
-    if path.is_dir() {
-        path.read_dir()
-            .into_iter()
-            .flatten()
-            .flatten()
-            .map(|entry| mtime(entry.path()))
-            .max()
-            .unwrap_or(SystemTime::UNIX_EPOCH)
-    } else {
-        path.metadata()
-            .and_then(|metadata| metadata.modified())
-            .unwrap_or(SystemTime::UNIX_EPOCH)
+fn log_err_and_continue<T>(res: Result<T, impl Display>, path: &Path) -> Option<T> {
+    match res {
+        Ok(x) => Some(x),
+        Err(ref e) => {
+            eprintln!("error reading `{}`: {e}", path.display());
+            None
+        },
     }
 }
+
+fn mtime(path: &str) -> SystemTime {
+    log_err_and_continue(fs::metadata(path), path.as_ref())
+        .and_then(|metadata| log_err_and_continue(metadata.modified(), path.as_ref()))
+        .unwrap_or(SystemTime::UNIX_EPOCH)
+}
+
+fn is_metadata_outdated(time: SystemTime) -> bool {
+    // Ignore all IO errors here. We don't want to stop them from hosting the server.
+    if time < mtime("util/gh-pages/index_template.html") || time < mtime("tests/compile-test.rs") {
+        return true;
+    }
+    let Some(dir) = log_err_and_continue(fs::read_dir("."), ".".as_ref()) else {
+        return false;
+    };
+    dir.map_while(|e| log_err_and_continue(e, ".".as_ref())).any(|e| {
+        let name = e.file_name();
+        let name_bytes = name.as_encoded_bytes();
+        if (name_bytes.starts_with(b"clippy_lints") && name_bytes != b"clippy_lints_internal")
+            || name_bytes == b"clippy_config"
+        {
+            WalkDir::new(&name)
+                .into_iter()
+                .map_while(|e| log_err_and_continue(e, name.as_ref()))
+                .filter(|e| e.file_type().is_file())
+                .filter_map(|e| {
+                    log_err_and_continue(e.metadata(), e.path())
+                        .and_then(|m| log_err_and_continue(m.modified(), e.path()))
+                })
+                .any(|ftime| time < ftime)
+        } else {
+            false
+        }
+    })
+}
diff --git a/src/tools/clippy/clippy_dev/src/setup/git_hook.rs b/src/tools/clippy/clippy_dev/src/setup/git_hook.rs
index c7c53bc69d0..c5a1e8264c7 100644
--- a/src/tools/clippy/clippy_dev/src/setup/git_hook.rs
+++ b/src/tools/clippy/clippy_dev/src/setup/git_hook.rs
@@ -1,8 +1,6 @@
 use std::fs;
 use std::path::Path;
 
-use super::verify_inside_clippy_dir;
-
 /// Rusts setup uses `git rev-parse --git-common-dir` to get the root directory of the repo.
 /// I've decided against this for the sake of simplicity and to make sure that it doesn't install
 /// the hook if `clippy_dev` would be used in the rust tree. The hook also references this tool
@@ -35,10 +33,6 @@ pub fn install_hook(force_override: bool) {
 }
 
 fn check_precondition(force_override: bool) -> bool {
-    if !verify_inside_clippy_dir() {
-        return false;
-    }
-
     // Make sure that we can find the git repository
     let git_path = Path::new(REPO_GIT_DIR);
     if !git_path.exists() || !git_path.is_dir() {
diff --git a/src/tools/clippy/clippy_dev/src/setup/mod.rs b/src/tools/clippy/clippy_dev/src/setup/mod.rs
index b0d31814639..5e938fff126 100644
--- a/src/tools/clippy/clippy_dev/src/setup/mod.rs
+++ b/src/tools/clippy/clippy_dev/src/setup/mod.rs
@@ -2,23 +2,3 @@ pub mod git_hook;
 pub mod intellij;
 pub mod toolchain;
 pub mod vscode;
-
-use std::path::Path;
-
-const CLIPPY_DEV_DIR: &str = "clippy_dev";
-
-/// This function verifies that the tool is being executed in the clippy directory.
-/// This is useful to ensure that setups only modify Clippy's resources. The verification
-/// is done by checking that `clippy_dev` is a sub directory of the current directory.
-///
-/// It will print an error message and return `false` if the directory could not be
-/// verified.
-fn verify_inside_clippy_dir() -> bool {
-    let path = Path::new(CLIPPY_DEV_DIR);
-    if path.exists() && path.is_dir() {
-        true
-    } else {
-        eprintln!("error: unable to verify that the working directory is clippy's directory");
-        false
-    }
-}
diff --git a/src/tools/clippy/clippy_dev/src/setup/toolchain.rs b/src/tools/clippy/clippy_dev/src/setup/toolchain.rs
index ecd80215f7e..c64ae4ef3c3 100644
--- a/src/tools/clippy/clippy_dev/src/setup/toolchain.rs
+++ b/src/tools/clippy/clippy_dev/src/setup/toolchain.rs
@@ -1,20 +1,12 @@
+use crate::utils::{cargo_cmd, run_exit_on_err};
 use std::env::consts::EXE_SUFFIX;
 use std::env::current_dir;
 use std::ffi::OsStr;
 use std::fs;
 use std::path::{Path, PathBuf};
-use std::process::Command;
 use walkdir::WalkDir;
 
-use crate::utils::exit_if_err;
-
-use super::verify_inside_clippy_dir;
-
 pub fn create(standalone: bool, force: bool, release: bool, name: &str) {
-    if !verify_inside_clippy_dir() {
-        return;
-    }
-
     let rustup_home = std::env::var("RUSTUP_HOME").unwrap();
     let toolchain = std::env::var("RUSTUP_TOOLCHAIN").unwrap();
 
@@ -51,11 +43,10 @@ pub fn create(standalone: bool, force: bool, release: bool, name: &str) {
         }
     }
 
-    let status = Command::new("cargo")
-        .arg("build")
-        .args(release.then_some("--release"))
-        .status();
-    exit_if_err(status);
+    run_exit_on_err(
+        "cargo build",
+        cargo_cmd().arg("build").args(release.then_some("--release")),
+    );
 
     install_bin("cargo-clippy", &dest, standalone, release);
     install_bin("clippy-driver", &dest, standalone, release);
diff --git a/src/tools/clippy/clippy_dev/src/setup/vscode.rs b/src/tools/clippy/clippy_dev/src/setup/vscode.rs
index a37c873eed4..a24aef65991 100644
--- a/src/tools/clippy/clippy_dev/src/setup/vscode.rs
+++ b/src/tools/clippy/clippy_dev/src/setup/vscode.rs
@@ -1,8 +1,6 @@
 use std::fs;
 use std::path::Path;
 
-use super::verify_inside_clippy_dir;
-
 const VSCODE_DIR: &str = ".vscode";
 const TASK_SOURCE_FILE: &str = "util/etc/vscode-tasks.json";
 const TASK_TARGET_FILE: &str = ".vscode/tasks.json";
@@ -22,10 +20,6 @@ pub fn install_tasks(force_override: bool) {
 }
 
 fn check_install_precondition(force_override: bool) -> bool {
-    if !verify_inside_clippy_dir() {
-        return false;
-    }
-
     let vs_dir_path = Path::new(VSCODE_DIR);
     if vs_dir_path.exists() {
         // verify the target will be valid
diff --git a/src/tools/clippy/clippy_dev/src/utils.rs b/src/tools/clippy/clippy_dev/src/utils.rs
index 89962a11034..057951d0e33 100644
--- a/src/tools/clippy/clippy_dev/src/utils.rs
+++ b/src/tools/clippy/clippy_dev/src/utils.rs
@@ -8,15 +8,10 @@ use std::ffi::OsStr;
 use std::fs::{self, OpenOptions};
 use std::io::{self, Read as _, Seek as _, SeekFrom, Write};
 use std::path::{Path, PathBuf};
-use std::process::{self, Command, ExitStatus, Stdio};
+use std::process::{self, Command, Stdio};
 use std::{env, thread};
 use walkdir::WalkDir;
 
-#[cfg(not(windows))]
-static CARGO_CLIPPY_EXE: &str = "cargo-clippy";
-#[cfg(windows)]
-static CARGO_CLIPPY_EXE: &str = "cargo-clippy.exe";
-
 #[derive(Clone, Copy)]
 pub enum ErrAction {
     Open,
@@ -118,16 +113,14 @@ impl<'a> File<'a> {
     }
 }
 
-/// Returns the path to the `cargo-clippy` binary
-///
-/// # Panics
-///
-/// Panics if the path of current executable could not be retrieved.
+/// Creates a `Command` for running cargo.
 #[must_use]
-pub fn cargo_clippy_path() -> PathBuf {
-    let mut path = env::current_exe().expect("failed to get current executable name");
-    path.set_file_name(CARGO_CLIPPY_EXE);
-    path
+pub fn cargo_cmd() -> Command {
+    if let Some(path) = env::var_os("CARGO") {
+        Command::new(path)
+    } else {
+        Command::new("cargo")
+    }
 }
 
 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
@@ -288,19 +281,6 @@ impl ClippyInfo {
     }
 }
 
-/// # Panics
-/// Panics if given command result was failed.
-pub fn exit_if_err(status: io::Result<ExitStatus>) {
-    match status.expect("failed to run command").code() {
-        Some(0) => {},
-        Some(n) => process::exit(n),
-        None => {
-            eprintln!("Killed by signal");
-            process::exit(1);
-        },
-    }
-}
-
 #[derive(Clone, Copy)]
 pub enum UpdateStatus {
     Unchanged,
@@ -341,6 +321,7 @@ pub struct FileUpdater {
     dst_buf: String,
 }
 impl FileUpdater {
+    #[track_caller]
     fn update_file_checked_inner(
         &mut self,
         tool: &str,
@@ -364,6 +345,7 @@ impl FileUpdater {
         }
     }
 
+    #[track_caller]
     fn update_file_inner(&mut self, path: &Path, update: &mut dyn FnMut(&Path, &str, &mut String) -> UpdateStatus) {
         let mut file = File::open(path, OpenOptions::new().read(true).write(true));
         file.read_to_cleared_string(&mut self.src_buf);
@@ -373,6 +355,7 @@ impl FileUpdater {
         }
     }
 
+    #[track_caller]
     pub fn update_file_checked(
         &mut self,
         tool: &str,
@@ -383,6 +366,7 @@ impl FileUpdater {
         self.update_file_checked_inner(tool, mode, path.as_ref(), update);
     }
 
+    #[track_caller]
     pub fn update_file(
         &mut self,
         path: impl AsRef<Path>,
@@ -450,7 +434,6 @@ pub enum Token<'a> {
     OpenParen,
     Pound,
     Semi,
-    Slash,
 }
 
 pub struct RustSearcher<'txt> {
@@ -528,7 +511,6 @@ impl<'txt> RustSearcher<'txt> {
                 | (Token::OpenParen, lexer::TokenKind::OpenParen)
                 | (Token::Pound, lexer::TokenKind::Pound)
                 | (Token::Semi, lexer::TokenKind::Semi)
-                | (Token::Slash, lexer::TokenKind::Slash)
                 | (
                     Token::LitStr,
                     lexer::TokenKind::Literal {
@@ -601,7 +583,7 @@ impl<'txt> RustSearcher<'txt> {
     }
 }
 
-#[expect(clippy::must_use_candidate)]
+#[track_caller]
 pub fn try_rename_file(old_name: &Path, new_name: &Path) -> bool {
     match OpenOptions::new().create_new(true).write(true).open(new_name) {
         Ok(file) => drop(file),
@@ -623,7 +605,7 @@ pub fn try_rename_file(old_name: &Path, new_name: &Path) -> bool {
     }
 }
 
-#[expect(clippy::must_use_candidate)]
+#[track_caller]
 pub fn try_rename_dir(old_name: &Path, new_name: &Path) -> bool {
     match fs::create_dir(new_name) {
         Ok(()) => {},
@@ -649,10 +631,19 @@ pub fn try_rename_dir(old_name: &Path, new_name: &Path) -> bool {
     }
 }
 
-pub fn write_file(path: &Path, contents: &str) {
-    expect_action(fs::write(path, contents), ErrAction::Write, path);
+#[track_caller]
+pub fn run_exit_on_err(path: &(impl AsRef<Path> + ?Sized), cmd: &mut Command) {
+    match expect_action(cmd.status(), ErrAction::Run, path.as_ref()).code() {
+        Some(0) => {},
+        Some(n) => process::exit(n),
+        None => {
+            eprintln!("{} killed by signal", path.as_ref().display());
+            process::exit(1);
+        },
+    }
 }
 
+#[track_caller]
 #[must_use]
 pub fn run_with_output(path: &(impl AsRef<Path> + ?Sized), cmd: &mut Command) -> Vec<u8> {
     fn f(path: &Path, cmd: &mut Command) -> Vec<u8> {
@@ -738,7 +729,7 @@ pub fn split_args_for_threads(
     }
 }
 
-#[expect(clippy::must_use_candidate)]
+#[track_caller]
 pub fn delete_file_if_exists(path: &Path) -> bool {
     match fs::remove_file(path) {
         Ok(()) => true,
@@ -747,6 +738,7 @@ pub fn delete_file_if_exists(path: &Path) -> bool {
     }
 }
 
+#[track_caller]
 pub fn delete_dir_if_exists(path: &Path) {
     match fs::remove_dir_all(path) {
         Ok(()) => {},
diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml
index b9e6de79cc0..51e59ae2050 100644
--- a/src/tools/clippy/clippy_lints/Cargo.toml
+++ b/src/tools/clippy/clippy_lints/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "clippy_lints"
-version = "0.1.91"
+version = "0.1.92"
 description = "A bunch of helpful lints to avoid common pitfalls in Rust"
 repository = "https://github.com/rust-lang/rust-clippy"
 readme = "README.md"
@@ -27,6 +27,10 @@ url = "2.2"
 [dev-dependencies]
 walkdir = "2.3"
 
+[lints.rust.unexpected_cfgs]
+level = "warn"
+check-cfg = ['cfg(bootstrap)']
+
 [package.metadata.rust-analyzer]
 # This crate uses #[feature(rustc_private)]
 rustc_private = true
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
index d6469d32931..36498adff50 100644
--- a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs
+++ b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs
@@ -534,6 +534,7 @@ fn get_item_name(item: &Item<'_>) -> Option<String> {
 
                         if let Some(of_trait) = im.of_trait {
                             let mut trait_segs: Vec<String> = of_trait
+                                .trait_ref
                                 .path
                                 .segments
                                 .iter()
diff --git a/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs b/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs
index 6f2a6a36a38..08253b0c499 100644
--- a/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs
+++ b/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs
@@ -3,10 +3,10 @@ use clippy_utils::macros::{PanicExpn, find_assert_args, root_macro_call_first_no
 use clippy_utils::source::snippet_with_context;
 use clippy_utils::ty::{has_debug_impl, is_copy, is_type_diagnostic_item};
 use clippy_utils::usage::local_used_after_expr;
-use clippy_utils::{is_expr_final_block_expr, path_res, sym};
+use clippy_utils::{path_res, sym};
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
-use rustc_hir::{Expr, ExprKind};
+use rustc_hir::{Expr, ExprKind, Node};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::{self, Ty};
 use rustc_session::declare_lint_pass;
@@ -77,17 +77,20 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates {
                 _ => return,
             };
             span_lint_and_then(cx, ASSERTIONS_ON_RESULT_STATES, macro_call.span, message, |diag| {
-                let semicolon = if is_expr_final_block_expr(cx.tcx, e) { ";" } else { "" };
                 let mut app = Applicability::MachineApplicable;
-                diag.span_suggestion(
-                    macro_call.span,
-                    "replace with",
-                    format!(
-                        "{}.{replacement}(){semicolon}",
-                        snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0
-                    ),
-                    app,
-                );
+                let recv = snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0;
+
+                // `assert!` doesn't return anything, but `Result::unwrap(_err)` does, so we might need to add a
+                // semicolon to the suggestion to avoid leaking the type
+                let sugg = match cx.tcx.parent_hir_node(e.hir_id) {
+                    // trailing expr of a block
+                    Node::Block(..) => format!("{recv}.{replacement}();"),
+                    // already has a trailing semicolon
+                    Node::Stmt(..) => format!("{recv}.{replacement}()"),
+                    // this is the last-resort option, because it's rather verbose
+                    _ => format!("{{ {recv}.{replacement}(); }}"),
+                };
+                diag.span_suggestion(macro_call.span, "replace with", sugg, app);
             });
         }
     }
diff --git a/src/tools/clippy/clippy_lints/src/async_yields_async.rs b/src/tools/clippy/clippy_lints/src/async_yields_async.rs
index 013819b0da8..1a10db291cd 100644
--- a/src/tools/clippy/clippy_lints/src/async_yields_async.rs
+++ b/src/tools/clippy/clippy_lints/src/async_yields_async.rs
@@ -1,8 +1,12 @@
 use clippy_utils::diagnostics::span_lint_hir_and_then;
-use clippy_utils::source::snippet;
+use clippy_utils::is_expr_async_block;
+use clippy_utils::source::walk_span_to_context;
+use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::implements_trait;
 use rustc_errors::Applicability;
-use rustc_hir::{Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, QPath};
+use rustc_hir::{
+    Block, Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, QPath,
+};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
 
@@ -87,31 +91,37 @@ impl<'tcx> LateLintPass<'tcx> for AsyncYieldsAsync {
         let expr_ty = typeck_results.expr_ty(body_expr);
 
         if implements_trait(cx, expr_ty, future_trait_def_id, &[]) {
-            let return_expr_span = match &body_expr.kind {
-                // XXXkhuey there has to be a better way.
-                ExprKind::Block(block, _) => block.expr.map(|e| e.span),
-                ExprKind::Path(QPath::Resolved(_, path)) => Some(path.span),
-                _ => None,
+            let (return_expr, return_expr_span) = match &body_expr.kind {
+                ExprKind::Block(Block { expr: Some(e), .. }, _) => (*e, e.span),
+                ExprKind::Path(QPath::Resolved(_, path)) => (body_expr, path.span),
+                _ => return,
             };
-            if let Some(return_expr_span) = return_expr_span {
-                span_lint_hir_and_then(
-                    cx,
-                    ASYNC_YIELDS_ASYNC,
-                    body_expr.hir_id,
-                    return_expr_span,
-                    "an async construct yields a type which is itself awaitable",
-                    |db| {
-                        db.span_label(body_expr.span, "outer async construct");
-                        db.span_label(return_expr_span, "awaitable value not awaited");
-                        db.span_suggestion(
-                            return_expr_span,
-                            "consider awaiting this value",
-                            format!("{}.await", snippet(cx, return_expr_span, "..")),
-                            Applicability::MaybeIncorrect,
-                        );
-                    },
-                );
+
+            let return_expr_span = walk_span_to_context(return_expr_span, expr.span.ctxt()).unwrap_or(return_expr_span);
+            let mut applicability = Applicability::MaybeIncorrect;
+            let mut return_expr_snip =
+                Sugg::hir_with_context(cx, return_expr, expr.span.ctxt(), "..", &mut applicability);
+            if !is_expr_async_block(return_expr) {
+                return_expr_snip = return_expr_snip.maybe_paren();
             }
+
+            span_lint_hir_and_then(
+                cx,
+                ASYNC_YIELDS_ASYNC,
+                body_expr.hir_id,
+                return_expr_span,
+                "an async construct yields a type which is itself awaitable",
+                |db| {
+                    db.span_label(body_expr.span, "outer async construct");
+                    db.span_label(return_expr_span, "awaitable value not awaited");
+                    db.span_suggestion(
+                        return_expr_span,
+                        "consider awaiting this value",
+                        format!("{return_expr_snip}.await"),
+                        applicability,
+                    );
+                },
+            );
         }
     }
 }
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 b9b5cedb5aa..1cebc18edc9 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs
@@ -30,6 +30,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) {
                                 sym::ambiguous_glob_reexports
                                     | sym::dead_code
                                     | sym::deprecated
+                                    | sym::deprecated_in_future
                                     | sym::hidden_glob_reexports
                                     | sym::unreachable_pub
                                     | sym::unused
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 581fe33ea0b..f31b67f470f 100644
--- a/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs
+++ b/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs
@@ -130,18 +130,22 @@ impl<'tcx> LateLintPass<'tcx> for BoolAssertComparison {
 
                 let mut suggestions = vec![(name_span, non_eq_mac.to_string()), (lit_span, String::new())];
 
-                if bool_value ^ eq_macro {
-                    let Some(sugg) = Sugg::hir_opt(cx, non_lit_expr) else {
-                        return;
+                if let Some(sugg) = Sugg::hir_opt(cx, non_lit_expr) {
+                    let sugg = if bool_value ^ eq_macro {
+                        !sugg.maybe_paren()
+                    } else if ty::Bool == *non_lit_ty.kind() {
+                        sugg
+                    } else {
+                        !!sugg.maybe_paren()
                     };
-                    suggestions.push((non_lit_expr.span, (!sugg).to_string()));
-                }
+                    suggestions.push((non_lit_expr.span, sugg.to_string()));
 
-                diag.multipart_suggestion(
-                    format!("replace it with `{non_eq_mac}!(..)`"),
-                    suggestions,
-                    Applicability::MachineApplicable,
-                );
+                    diag.multipart_suggestion(
+                        format!("replace it with `{non_eq_mac}!(..)`"),
+                        suggestions,
+                        Applicability::MachineApplicable,
+                    );
+                }
             },
         );
     }
diff --git a/src/tools/clippy/clippy_lints/src/bool_comparison.rs b/src/tools/clippy/clippy_lints/src/bool_comparison.rs
new file mode 100644
index 00000000000..722095909a6
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/bool_comparison.rs
@@ -0,0 +1,179 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::sugg::Sugg;
+use clippy_utils::{is_expn_of, peel_blocks, sym};
+use rustc_ast::ast::LitKind;
+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;
+use rustc_span::source_map::Spanned;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for expressions of the form `x == true`,
+    /// `x != true` and order comparisons such as `x < true` (or vice versa) and
+    /// suggest using the variable directly.
+    ///
+    /// ### Why is this bad?
+    /// Unnecessary code.
+    ///
+    /// ### Example
+    /// ```rust,ignore
+    /// if x == true {}
+    /// if y == false {}
+    /// ```
+    /// use `x` directly:
+    /// ```rust,ignore
+    /// if x {}
+    /// if !y {}
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub BOOL_COMPARISON,
+    complexity,
+    "comparing a variable to a boolean, e.g., `if x == true` or `if x != true`"
+}
+
+declare_lint_pass!(BoolComparison => [BOOL_COMPARISON]);
+
+impl<'tcx> LateLintPass<'tcx> for BoolComparison {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
+        if e.span.from_expansion() {
+            return;
+        }
+
+        if let ExprKind::Binary(Spanned { node, .. }, left_side, right_side) = e.kind
+            && is_expn_of(left_side.span, sym::cfg).is_none()
+            && is_expn_of(right_side.span, sym::cfg).is_none()
+            && cx.typeck_results().expr_ty(left_side).is_bool()
+            && cx.typeck_results().expr_ty(right_side).is_bool()
+        {
+            let ignore_case = None::<(fn(_) -> _, &str)>;
+            let ignore_no_literal = None::<(fn(_, _) -> _, &str)>;
+            match node {
+                BinOpKind::Eq => {
+                    let true_case = Some((|h| h, "equality checks against true are unnecessary"));
+                    let false_case = Some((
+                        |h: Sugg<'tcx>| !h,
+                        "equality checks against false can be replaced by a negation",
+                    ));
+                    check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal);
+                },
+                BinOpKind::Ne => {
+                    let true_case = Some((
+                        |h: Sugg<'tcx>| !h,
+                        "inequality checks against true can be replaced by a negation",
+                    ));
+                    let false_case = Some((|h| h, "inequality checks against false are unnecessary"));
+                    check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal);
+                },
+                BinOpKind::Lt => check_comparison(
+                    cx,
+                    e,
+                    ignore_case,
+                    Some((|h| h, "greater than checks against false are unnecessary")),
+                    Some((
+                        |h: Sugg<'tcx>| !h,
+                        "less than comparison against true can be replaced by a negation",
+                    )),
+                    ignore_case,
+                    Some((
+                        |l: Sugg<'tcx>, r: Sugg<'tcx>| (!l).bit_and(&r),
+                        "order comparisons between booleans can be simplified",
+                    )),
+                ),
+                BinOpKind::Gt => check_comparison(
+                    cx,
+                    e,
+                    Some((
+                        |h: Sugg<'tcx>| !h,
+                        "less than comparison against true can be replaced by a negation",
+                    )),
+                    ignore_case,
+                    ignore_case,
+                    Some((|h| h, "greater than checks against false are unnecessary")),
+                    Some((
+                        |l: Sugg<'tcx>, r: Sugg<'tcx>| l.bit_and(&(!r)),
+                        "order comparisons between booleans can be simplified",
+                    )),
+                ),
+                _ => (),
+            }
+        }
+    }
+}
+
+fn check_comparison<'a, 'tcx>(
+    cx: &LateContext<'tcx>,
+    e: &'tcx Expr<'_>,
+    left_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>,
+    left_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>,
+    right_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>,
+    right_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>,
+    no_literal: Option<(impl FnOnce(Sugg<'a>, Sugg<'a>) -> Sugg<'a>, &'static str)>,
+) {
+    if let ExprKind::Binary(_, left_side, right_side) = e.kind {
+        let mut applicability = Applicability::MachineApplicable;
+        // Eliminate parentheses in `e` by using the lo pos of lhs and hi pos of rhs,
+        // calling `source_callsite` make sure macros are handled correctly, see issue #9907
+        let binop_span = left_side.span.source_callsite().to(right_side.span.source_callsite());
+
+        match (fetch_bool_expr(left_side), fetch_bool_expr(right_side)) {
+            (Some(true), None) => left_true.map_or((), |(h, m)| {
+                suggest_bool_comparison(cx, binop_span, right_side, applicability, m, h);
+            }),
+            (None, Some(true)) => right_true.map_or((), |(h, m)| {
+                suggest_bool_comparison(cx, binop_span, left_side, applicability, m, h);
+            }),
+            (Some(false), None) => left_false.map_or((), |(h, m)| {
+                suggest_bool_comparison(cx, binop_span, right_side, applicability, m, h);
+            }),
+            (None, Some(false)) => right_false.map_or((), |(h, m)| {
+                suggest_bool_comparison(cx, binop_span, left_side, applicability, m, h);
+            }),
+            (None, None) => no_literal.map_or((), |(h, m)| {
+                let left_side = Sugg::hir_with_context(cx, left_side, binop_span.ctxt(), "..", &mut applicability);
+                let right_side = Sugg::hir_with_context(cx, right_side, binop_span.ctxt(), "..", &mut applicability);
+                span_lint_and_sugg(
+                    cx,
+                    BOOL_COMPARISON,
+                    binop_span,
+                    m,
+                    "try",
+                    h(left_side, right_side).into_string(),
+                    applicability,
+                );
+            }),
+            _ => (),
+        }
+    }
+}
+
+fn suggest_bool_comparison<'a, 'tcx>(
+    cx: &LateContext<'tcx>,
+    span: Span,
+    expr: &Expr<'_>,
+    mut app: Applicability,
+    message: &'static str,
+    conv_hint: impl FnOnce(Sugg<'a>) -> Sugg<'a>,
+) {
+    let hint = Sugg::hir_with_context(cx, expr, span.ctxt(), "..", &mut app);
+    span_lint_and_sugg(
+        cx,
+        BOOL_COMPARISON,
+        span,
+        message,
+        "try",
+        conv_hint(hint).into_string(),
+        app,
+    );
+}
+
+fn fetch_bool_expr(expr: &Expr<'_>) -> Option<bool> {
+    if let ExprKind::Lit(lit_ptr) = peel_blocks(expr).kind
+        && let LitKind::Bool(value) = lit_ptr.node
+    {
+        return Some(value);
+    }
+    None
+}
diff --git a/src/tools/clippy/clippy_lints/src/casts/as_underscore.rs b/src/tools/clippy/clippy_lints/src/casts/as_underscore.rs
index 3ac486dd63f..a73e48e5fd5 100644
--- a/src/tools/clippy/clippy_lints/src/casts/as_underscore.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/as_underscore.rs
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, Ty, TyKind};
 use rustc_lint::LateContext;
-use rustc_middle::ty;
+use rustc_middle::ty::IsSuggestable;
 
 use super::AS_UNDERSCORE;
 
@@ -10,15 +10,15 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, ty: &'tc
     if matches!(ty.kind, TyKind::Infer(())) {
         span_lint_and_then(cx, AS_UNDERSCORE, expr.span, "using `as _` conversion", |diag| {
             let ty_resolved = cx.typeck_results().expr_ty(expr);
-            if let ty::Error(_) = ty_resolved.kind() {
-                diag.help("consider giving the type explicitly");
-            } else {
+            if ty_resolved.is_suggestable(cx.tcx, true) {
                 diag.span_suggestion(
                     ty.span,
                     "consider giving the type explicitly",
                     ty_resolved,
                     Applicability::MachineApplicable,
                 );
+            } else {
+                diag.help("consider giving the type explicitly");
             }
         });
     }
diff --git a/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs
index e3b125a8d5b..eb75d5576f5 100644
--- a/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::msrvs::Msrv;
 use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
 use clippy_utils::sugg::has_enclosing_paren;
-use clippy_utils::{get_parent_expr, is_expr_temporary_value, is_lint_allowed, msrvs, std_or_core};
+use clippy_utils::{get_parent_expr, is_expr_temporary_value, is_from_proc_macro, is_lint_allowed, msrvs, std_or_core};
 use rustc_errors::Applicability;
 use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Ty, TyKind};
 use rustc_lint::LateContext;
@@ -22,13 +22,12 @@ pub(super) fn check<'tcx>(
         && !matches!(target.ty.kind, TyKind::TraitObject(..))
         && let ExprKind::AddrOf(BorrowKind::Ref, mutability, e) = cast_expr.kind
         && !is_lint_allowed(cx, BORROW_AS_PTR, expr.hir_id)
+        // Fix #9884
+        && !is_expr_temporary_value(cx, e)
+        && !is_from_proc_macro(cx, expr)
     {
         let mut app = Applicability::MachineApplicable;
         let snip = snippet_with_context(cx, e.span, cast_expr.span.ctxt(), "..", &mut app).0;
-        // Fix #9884
-        if is_expr_temporary_value(cx, e) {
-            return false;
-        }
 
         let (suggestion, span) = if msrv.meets(cx, msrvs::RAW_REF_OP) {
             // Make sure that the span to be replaced doesn't include parentheses, that could break the
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs b/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs
index e26c03ccda9..9eaa6e4cf26 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_possible_wrap.rs
@@ -1,4 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::msrvs::{self, Msrv};
+use clippy_utils::sugg::Sugg;
+use rustc_errors::Applicability;
 use rustc_hir::Expr;
 use rustc_lint::LateContext;
 use rustc_middle::ty::Ty;
@@ -16,7 +19,14 @@ enum EmitState {
     LintOnPtrSize(u64),
 }
 
-pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>) {
+pub(super) fn check(
+    cx: &LateContext<'_>,
+    expr: &Expr<'_>,
+    cast_op: &Expr<'_>,
+    cast_from: Ty<'_>,
+    cast_to: Ty<'_>,
+    msrv: Msrv,
+) {
     let (Some(from_nbits), Some(to_nbits)) = (
         utils::int_ty_to_nbits(cx.tcx, cast_from),
         utils::int_ty_to_nbits(cx.tcx, cast_to),
@@ -85,5 +95,23 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, ca
                 .note("`usize` and `isize` may be as small as 16 bits on some platforms")
                 .note("for more information see https://doc.rust-lang.org/reference/types/numeric.html#machine-dependent-integer-types");
         }
+
+        if msrv.meets(cx, msrvs::INTEGER_SIGN_CAST)
+            && let Some(cast) = utils::is_signedness_cast(cast_from, cast_to)
+        {
+            let method = match cast {
+                utils::CastTo::Signed => "cast_signed()",
+                utils::CastTo::Unsigned => "cast_unsigned()",
+            };
+            let mut app = Applicability::MaybeIncorrect;
+            let sugg = Sugg::hir_with_context(cx, cast_op, expr.span.ctxt(), "..", &mut app);
+
+            diag.span_suggestion(
+                expr.span,
+                format!("if this is intentional, use `{method}` instead"),
+                format!("{}.{method}", sugg.maybe_paren()),
+                app,
+            );
+        }
     });
 }
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 a1543cabd2f..d78da9396fa 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
@@ -8,17 +8,12 @@ use rustc_middle::ty::{self, Ty};
 
 use super::CAST_PTR_ALIGNMENT;
 
-pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
-    if let ExprKind::Cast(cast_expr, cast_to) = expr.kind {
-        if is_hir_ty_cfg_dependant(cx, cast_to) {
-            return;
-        }
-        let (cast_from, cast_to) = (
-            cx.typeck_results().expr_ty(cast_expr),
-            cx.typeck_results().expr_ty(expr),
-        );
-        lint_cast_ptr_alignment(cx, expr, cast_from, cast_to);
-    } else if let ExprKind::MethodCall(method_path, self_arg, [], _) = &expr.kind
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, cast_from: Ty<'tcx>, cast_to: Ty<'tcx>) {
+    lint_cast_ptr_alignment(cx, expr, cast_from, cast_to);
+}
+
+pub(super) fn check_cast_method(cx: &LateContext<'_>, expr: &Expr<'_>) {
+    if let ExprKind::MethodCall(method_path, self_arg, [], _) = &expr.kind
         && method_path.ident.name == sym::cast
         && let Some(generic_args) = method_path.args
         && let [GenericArg::Type(cast_to)] = generic_args.args
@@ -74,14 +69,13 @@ fn is_used_as_unaligned(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
         ExprKind::Call(func, [arg, ..]) if arg.hir_id == e.hir_id => {
             if let ExprKind::Path(path) = &func.kind
                 && let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id()
+                && let Some(name) = cx.tcx.get_diagnostic_name(def_id)
                 && matches!(
-                    cx.tcx.get_diagnostic_name(def_id),
-                    Some(
-                        sym::ptr_write_unaligned
-                            | sym::ptr_read_unaligned
-                            | sym::intrinsics_unaligned_volatile_load
-                            | sym::intrinsics_unaligned_volatile_store
-                    )
+                    name,
+                    sym::ptr_write_unaligned
+                        | sym::ptr_read_unaligned
+                        | sym::intrinsics_unaligned_volatile_load
+                        | sym::intrinsics_unaligned_volatile_store
                 )
             {
                 true
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs b/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs
index a70bd886191..f870d27b796 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs
@@ -2,15 +2,18 @@ use std::convert::Infallible;
 use std::ops::ControlFlow;
 
 use clippy_utils::consts::{ConstEvalCtxt, Constant};
-use clippy_utils::diagnostics::span_lint;
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::msrvs::{self, Msrv};
+use clippy_utils::sugg::Sugg;
 use clippy_utils::visitors::{Descend, for_each_expr_without_closures};
 use clippy_utils::{method_chain_args, sext, sym};
+use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, Ty};
 use rustc_span::Symbol;
 
-use super::CAST_SIGN_LOSS;
+use super::{CAST_SIGN_LOSS, utils};
 
 /// A list of methods that can never return a negative value.
 /// Includes methods that panic rather than returning a negative value.
@@ -42,13 +45,33 @@ pub(super) fn check<'cx>(
     cast_op: &Expr<'_>,
     cast_from: Ty<'cx>,
     cast_to: Ty<'_>,
+    msrv: Msrv,
 ) {
     if should_lint(cx, cast_op, cast_from, cast_to) {
-        span_lint(
+        span_lint_and_then(
             cx,
             CAST_SIGN_LOSS,
             expr.span,
             format!("casting `{cast_from}` to `{cast_to}` may lose the sign of the value"),
+            |diag| {
+                if msrv.meets(cx, msrvs::INTEGER_SIGN_CAST)
+                    && let Some(cast) = utils::is_signedness_cast(cast_from, cast_to)
+                {
+                    let method = match cast {
+                        utils::CastTo::Signed => "cast_signed()",
+                        utils::CastTo::Unsigned => "cast_unsigned()",
+                    };
+                    let mut app = Applicability::MaybeIncorrect;
+                    let sugg = Sugg::hir_with_context(cx, cast_op, expr.span.ctxt(), "..", &mut app);
+
+                    diag.span_suggestion(
+                        expr.span,
+                        format!("if this is intentional, use `{method}` instead"),
+                        format!("{}.{method}", sugg.maybe_paren()),
+                        app,
+                    );
+                }
+            },
         );
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs b/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs
index 46b0c88d3fe..bce7b4c69cc 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs
@@ -1,10 +1,12 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_with_context;
+use clippy_utils::{get_parent_expr, is_no_std_crate};
 use rustc_errors::Applicability;
 use rustc_hir::def_id::DefId;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::LateContext;
+use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
 use rustc_middle::ty::{self, Ty};
 use rustc_span::sym;
 
@@ -42,13 +44,52 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
         let mut applicability = Applicability::MachineApplicable;
         let ptr = snippet_with_context(cx, ptr_arg.span, ctxt, "ptr", &mut applicability).0;
         let len = snippet_with_context(cx, len_arg.span, ctxt, "len", &mut applicability).0;
+        let krate = if is_no_std_crate(cx) { "core" } else { "std" };
         span_lint_and_sugg(
             cx,
             CAST_SLICE_FROM_RAW_PARTS,
             span,
             format!("casting the result of `{func}` to {cast_to}"),
             "replace with",
-            format!("core::ptr::slice_{func}({ptr}, {len})"),
+            format!("{krate}::ptr::slice_{func}({ptr}, {len})"),
+            applicability,
+        );
+    }
+}
+
+/// Checks for implicit cast from slice reference to raw slice pointer.
+pub(super) fn check_implicit_cast(cx: &LateContext<'_>, expr: &Expr<'_>) {
+    if let ExprKind::Call(fun, [ptr_arg, len_arg]) = expr.peel_blocks().kind
+        && let ExprKind::Path(ref qpath) = fun.kind
+        && let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id()
+        && let Some(rpk) = raw_parts_kind(cx, fun_def_id)
+        && !matches!(get_parent_expr(cx, expr).map(|e| e.kind), Some(ExprKind::Cast(..)))
+        && let [deref, borrow] = cx.typeck_results().expr_adjustments(expr)
+        && matches!(deref.kind, Adjust::Deref(..))
+        && let Adjustment {
+            kind: Adjust::Borrow(AutoBorrow::RawPtr(..)),
+            target,
+        } = borrow
+        && let ty::RawPtr(pointee_ty, _) = target.kind()
+        && pointee_ty.is_slice()
+        && !expr.span.from_expansion()
+    {
+        let func = match rpk {
+            RawPartsKind::Immutable => "from_raw_parts",
+            RawPartsKind::Mutable => "from_raw_parts_mut",
+        };
+        let mut applicability = Applicability::MachineApplicable;
+        let ctxt = expr.span.ctxt();
+        let ptr = snippet_with_context(cx, ptr_arg.span, ctxt, "ptr", &mut applicability).0;
+        let len = snippet_with_context(cx, len_arg.span, ctxt, "len", &mut applicability).0;
+        let krate = if is_no_std_crate(cx) { "core" } else { "std" };
+        span_lint_and_sugg(
+            cx,
+            CAST_SLICE_FROM_RAW_PARTS,
+            expr.span,
+            format!("implicitly casting the result of `{func}` to `{target}`"),
+            "replace_with",
+            format!("{krate}::ptr::slice_{func}({ptr}, {len})"),
             applicability,
         );
     }
diff --git a/src/tools/clippy/clippy_lints/src/casts/char_lit_as_u8.rs b/src/tools/clippy/clippy_lints/src/casts/char_lit_as_u8.rs
index a7d3868f76c..964eaf2a0a2 100644
--- a/src/tools/clippy/clippy_lints/src/casts/char_lit_as_u8.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/char_lit_as_u8.rs
@@ -4,18 +4,17 @@ use rustc_ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::LateContext;
-use rustc_middle::ty::{self, UintTy};
+use rustc_middle::ty::{self, Ty, UintTy};
 
 use super::CHAR_LIT_AS_U8;
 
-pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
-    if let ExprKind::Cast(e, _) = &expr.kind
-        && let ExprKind::Lit(l) = &e.kind
+pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from_expr: &Expr<'_>, cast_to: Ty<'_>) {
+    if let ExprKind::Lit(l) = &cast_from_expr.kind
         && let LitKind::Char(c) = l.node
-        && ty::Uint(UintTy::U8) == *cx.typeck_results().expr_ty(expr).kind()
+        && ty::Uint(UintTy::U8) == *cast_to.kind()
     {
         let mut applicability = Applicability::MachineApplicable;
-        let snippet = snippet_with_applicability(cx, e.span, "'x'", &mut applicability);
+        let snippet = snippet_with_applicability(cx, cast_from_expr.span, "'x'", &mut applicability);
 
         span_lint_and_then(
             cx,
diff --git a/src/tools/clippy/clippy_lints/src/casts/mod.rs b/src/tools/clippy/clippy_lints/src/casts/mod.rs
index dcc439a272c..47cc1da0a6e 100644
--- a/src/tools/clippy/clippy_lints/src/casts/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/mod.rs
@@ -871,8 +871,11 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
             if !expr.span.from_expansion() && unnecessary_cast::check(cx, expr, cast_from_expr, cast_from, cast_to) {
                 return;
             }
+            char_lit_as_u8::check(cx, expr, cast_from_expr, cast_to);
             cast_slice_from_raw_parts::check(cx, expr, cast_from_expr, cast_to, self.msrv);
+            cast_ptr_alignment::check(cx, expr, cast_from, cast_to);
             ptr_cast_constness::check(cx, expr, cast_from_expr, cast_from, cast_to, self.msrv);
+            ptr_as_ptr::check(cx, expr, cast_from_expr, cast_from, cast_to_hir, cast_to, self.msrv);
             as_ptr_cast_mut::check(cx, expr, cast_from_expr, cast_to);
             fn_to_numeric_cast_any::check(cx, expr, cast_from_expr, cast_from, cast_to);
             confusing_method_to_numeric_cast::check(cx, expr, cast_from_expr, cast_from, cast_to);
@@ -887,9 +890,9 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
             if cast_to.is_numeric() {
                 cast_possible_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to, cast_to_hir.span);
                 if cast_from.is_numeric() {
-                    cast_possible_wrap::check(cx, expr, cast_from, cast_to);
+                    cast_possible_wrap::check(cx, expr, cast_from_expr, cast_from, cast_to, self.msrv);
                     cast_precision_loss::check(cx, expr, cast_from, cast_to);
-                    cast_sign_loss::check(cx, expr, cast_from_expr, cast_from, cast_to);
+                    cast_sign_loss::check(cx, expr, cast_from_expr, cast_from, cast_to, self.msrv);
                     cast_abs_to_unsigned::check(cx, expr, cast_from_expr, cast_from, cast_to, self.msrv);
                     cast_nan_to_int::check(cx, expr, cast_from_expr, cast_from, cast_to);
                 }
@@ -910,9 +913,10 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
         if self.msrv.meets(cx, msrvs::RAW_REF_OP) {
             borrow_as_ptr::check_implicit_cast(cx, expr);
         }
-        cast_ptr_alignment::check(cx, expr);
-        char_lit_as_u8::check(cx, expr);
-        ptr_as_ptr::check(cx, expr, self.msrv);
+        if self.msrv.meets(cx, msrvs::PTR_SLICE_RAW_PARTS) {
+            cast_slice_from_raw_parts::check_implicit_cast(cx, expr);
+        }
+        cast_ptr_alignment::check_cast_method(cx, expr);
         cast_slice_different_sizes::check(cx, expr, self.msrv);
         ptr_cast_constness::check_null_ptr_cast_method(cx, expr);
     }
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 ee0f3fa81c6..d012380cb76 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
@@ -1,11 +1,12 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_from_proc_macro;
 use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::sugg::Sugg;
 use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind, Mutability, QPath, TyKind};
+use rustc_hir::{self as hir, Expr, ExprKind, QPath, TyKind};
 use rustc_lint::LateContext;
-use rustc_middle::ty;
+use rustc_middle::ty::{self, Ty};
 use rustc_span::{Span, sym};
 
 use super::PTR_AS_PTR;
@@ -25,20 +26,26 @@ impl OmitFollowedCastReason<'_> {
     }
 }
 
-pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: Msrv) {
-    if let ExprKind::Cast(cast_expr, cast_to_hir_ty) = expr.kind
-        && let (cast_from, cast_to) = (cx.typeck_results().expr_ty(cast_expr), cx.typeck_results().expr_ty(expr))
-        && let ty::RawPtr(_, from_mutbl) = cast_from.kind()
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &Expr<'tcx>,
+    cast_from_expr: &Expr<'_>,
+    cast_from: Ty<'_>,
+    cast_to_hir: &hir::Ty<'_>,
+    cast_to: Ty<'tcx>,
+    msrv: Msrv,
+) {
+    if let ty::RawPtr(_, from_mutbl) = cast_from.kind()
         && let ty::RawPtr(to_pointee_ty, to_mutbl) = cast_to.kind()
-        && matches!((from_mutbl, to_mutbl),
-            (Mutability::Not, Mutability::Not) | (Mutability::Mut, Mutability::Mut))
+        && from_mutbl == to_mutbl
         // 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.typing_env())
         && msrv.meets(cx, msrvs::POINTER_CAST)
+        && !is_from_proc_macro(cx, expr)
     {
         let mut app = Applicability::MachineApplicable;
-        let turbofish = match &cast_to_hir_ty.kind {
+        let turbofish = match &cast_to_hir.kind {
             TyKind::Infer(()) => String::new(),
             TyKind::Ptr(mut_ty) => {
                 if matches!(mut_ty.ty.kind, TyKind::Infer(())) {
@@ -56,16 +63,14 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: Msrv) {
         // following `cast` does not compile because it fails to infer what type is expected
         // as type argument to `std::ptr::ptr_null` or `std::ptr::ptr_null_mut`, so
         // we omit following `cast`:
-        let omit_cast = if let ExprKind::Call(func, []) = cast_expr.kind
+        let omit_cast = if let ExprKind::Call(func, []) = cast_from_expr.kind
             && let ExprKind::Path(ref qpath @ QPath::Resolved(None, path)) = func.kind
             && let Some(method_defid) = path.res.opt_def_id()
         {
-            if cx.tcx.is_diagnostic_item(sym::ptr_null, method_defid) {
-                OmitFollowedCastReason::Null(qpath)
-            } else if cx.tcx.is_diagnostic_item(sym::ptr_null_mut, method_defid) {
-                OmitFollowedCastReason::NullMut(qpath)
-            } else {
-                OmitFollowedCastReason::None
+            match cx.tcx.get_diagnostic_name(method_defid) {
+                Some(sym::ptr_null) => OmitFollowedCastReason::Null(qpath),
+                Some(sym::ptr_null_mut) => OmitFollowedCastReason::NullMut(qpath),
+                _ => OmitFollowedCastReason::None,
             }
         } else {
             OmitFollowedCastReason::None
@@ -76,7 +81,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: Msrv) {
             let method = snippet_with_applicability(cx, qpath_span_without_turbofish(method), "..", &mut app);
             ("try call directly", format!("{method}{turbofish}()"))
         } else {
-            let cast_expr_sugg = Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app);
+            let cast_expr_sugg = Sugg::hir_with_context(cx, cast_from_expr, expr.span.ctxt(), "_", &mut app);
 
             (
                 "try `pointer::cast`, a safer alternative",
diff --git a/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs b/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs
index c0c0a47f855..c0f13f5e578 100644
--- a/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs
@@ -1,9 +1,10 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::msrvs::{self, Msrv};
+use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::{std_or_core, sym};
 use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind, Mutability, QPath};
+use rustc_hir::{self as hir, Expr, ExprKind, QPath};
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, Ty, TypeVisitableExt};
 
@@ -12,26 +13,23 @@ use super::PTR_CAST_CONSTNESS;
 pub(super) fn check<'tcx>(
     cx: &LateContext<'_>,
     expr: &Expr<'_>,
-    cast_expr: &Expr<'_>,
+    cast_from_expr: &Expr<'_>,
     cast_from: Ty<'tcx>,
     cast_to: Ty<'tcx>,
     msrv: Msrv,
 ) {
     if let ty::RawPtr(from_ty, from_mutbl) = cast_from.kind()
         && let ty::RawPtr(to_ty, to_mutbl) = cast_to.kind()
-        && matches!(
-            (from_mutbl, to_mutbl),
-            (Mutability::Not, Mutability::Mut) | (Mutability::Mut, Mutability::Not)
-        )
+        && from_mutbl != to_mutbl
         && from_ty == to_ty
         && !from_ty.has_erased_regions()
     {
-        if let ExprKind::Call(func, []) = cast_expr.kind
+        if let ExprKind::Call(func, []) = cast_from_expr.kind
             && let ExprKind::Path(QPath::Resolved(None, path)) = func.kind
             && let Some(defid) = path.res.opt_def_id()
             && let Some(prefix) = std_or_core(cx)
             && let mut app = Applicability::MachineApplicable
-            && let sugg = format!("{}", Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app))
+            && let sugg = snippet_with_applicability(cx, cast_from_expr.span, "_", &mut app)
             && let Some((_, after_lt)) = sugg.split_once("::<")
             && let Some((source, target, target_func)) = match cx.tcx.get_diagnostic_name(defid) {
                 Some(sym::ptr_null) => Some(("const", "mutable", "null_mut")),
@@ -53,11 +51,17 @@ pub(super) fn check<'tcx>(
 
         if msrv.meets(cx, msrvs::POINTER_CAST_CONSTNESS) {
             let mut app = Applicability::MachineApplicable;
-            let sugg = Sugg::hir_with_context(cx, cast_expr, expr.span.ctxt(), "_", &mut app);
-            let constness = match *to_mutbl {
-                Mutability::Not => "const",
-                Mutability::Mut => "mut",
+            let sugg = if let ExprKind::Cast(nested_from, nested_hir_ty) = cast_from_expr.kind
+                && let hir::TyKind::Ptr(ptr_ty) = nested_hir_ty.kind
+                && let hir::TyKind::Infer(()) = ptr_ty.ty.kind
+            {
+                // `(foo as *const _).cast_mut()` fails method name resolution
+                // avoid this by `as`-ing the full type
+                Sugg::hir_with_context(cx, nested_from, expr.span.ctxt(), "_", &mut app).as_ty(cast_from)
+            } else {
+                Sugg::hir_with_context(cx, cast_from_expr, expr.span.ctxt(), "_", &mut app)
             };
+            let constness = to_mutbl.ptr_str();
 
             span_lint_and_sugg(
                 cx,
@@ -73,8 +77,8 @@ pub(super) fn check<'tcx>(
 }
 
 pub(super) fn check_null_ptr_cast_method(cx: &LateContext<'_>, expr: &Expr<'_>) {
-    if let ExprKind::MethodCall(method, cast_expr, [], _) = expr.kind
-        && let ExprKind::Call(func, []) = cast_expr.kind
+    if let ExprKind::MethodCall(method, cast_from_expr, [], _) = expr.kind
+        && let ExprKind::Call(func, []) = cast_from_expr.kind
         && let ExprKind::Path(QPath::Resolved(None, path)) = func.kind
         && let Some(defid) = path.res.opt_def_id()
         && let method = match (cx.tcx.get_diagnostic_name(defid), method.ident.name) {
@@ -84,7 +88,7 @@ pub(super) fn check_null_ptr_cast_method(cx: &LateContext<'_>, expr: &Expr<'_>)
         }
         && let Some(prefix) = std_or_core(cx)
         && let mut app = Applicability::MachineApplicable
-        && let sugg = format!("{}", Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app))
+        && let sugg = snippet_with_applicability(cx, cast_from_expr.span, "_", &mut app)
         && let Some((_, after_lt)) = sugg.split_once("::<")
     {
         span_lint_and_sugg(
diff --git a/src/tools/clippy/clippy_lints/src/casts/utils.rs b/src/tools/clippy/clippy_lints/src/casts/utils.rs
index d846d78b9ee..707fc2a8eed 100644
--- a/src/tools/clippy/clippy_lints/src/casts/utils.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/utils.rs
@@ -60,3 +60,18 @@ pub(super) fn enum_ty_to_nbits(adt: AdtDef<'_>, tcx: TyCtxt<'_>) -> u64 {
         neg_bits.max(pos_bits).into()
     }
 }
+
+pub(super) enum CastTo {
+    Signed,
+    Unsigned,
+}
+/// Returns `Some` if the type cast is between 2 integral types that differ
+/// only in signedness, otherwise `None`. The value of `Some` is which
+/// signedness is casted to.
+pub(super) fn is_signedness_cast(cast_from: Ty<'_>, cast_to: Ty<'_>) -> Option<CastTo> {
+    match (cast_from.kind(), cast_to.kind()) {
+        (ty::Int(from), ty::Uint(to)) if from.to_unsigned() == *to => Some(CastTo::Unsigned),
+        (ty::Uint(from), ty::Int(to)) if *from == to.to_unsigned() => Some(CastTo::Signed),
+        _ => None,
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs
index 8f1c0296524..7646aa48b77 100644
--- a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs
+++ b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs
@@ -23,8 +23,8 @@ declare_clippy_lint! {
     ///
     /// ### Known problems
     /// The true Cognitive Complexity of a method is not something we can
-    /// calculate using modern technology. This lint has been left in the
-    /// `nursery` so as to not mislead users into using this lint as a
+    /// calculate using modern technology. This lint has been left in
+    /// `restriction` so as to not mislead users into using this lint as a
     /// measurement tool.
     ///
     /// For more detailed information, see [rust-clippy#3793](https://github.com/rust-lang/rust-clippy/issues/3793)
@@ -56,7 +56,7 @@ impl_lint_pass!(CognitiveComplexity => [COGNITIVE_COMPLEXITY]);
 
 impl CognitiveComplexity {
     fn check<'tcx>(
-        &mut self,
+        &self,
         cx: &LateContext<'tcx>,
         kind: FnKind<'tcx>,
         decl: &'tcx FnDecl<'_>,
diff --git a/src/tools/clippy/clippy_lints/src/collapsible_if.rs b/src/tools/clippy/clippy_lints/src/collapsible_if.rs
index e3103e2d301..ad610fbd8d2 100644
--- a/src/tools/clippy/clippy_lints/src/collapsible_if.rs
+++ b/src/tools/clippy/clippy_lints/src/collapsible_if.rs
@@ -5,7 +5,7 @@ use clippy_utils::source::{IntoSpan as _, SpanRangeExt, snippet, snippet_block_w
 use clippy_utils::{span_contains_non_whitespace, tokenize_with_text};
 use rustc_ast::BinOpKind;
 use rustc_errors::Applicability;
-use rustc_hir::{Block, Expr, ExprKind, Stmt, StmtKind};
+use rustc_hir::{Block, Expr, ExprKind, StmtKind};
 use rustc_lexer::TokenKind;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
@@ -141,11 +141,7 @@ impl CollapsibleIf {
 
                     // Prevent "elseif"
                     // Check that the "else" is followed by whitespace
-                    let requires_space = if let Some(c) = snippet(cx, up_to_else, "..").chars().last() {
-                        !c.is_whitespace()
-                    } else {
-                        false
-                    };
+                    let requires_space = snippet(cx, up_to_else, "..").ends_with(|c: char| !c.is_whitespace());
                     let mut applicability = Applicability::MachineApplicable;
                     diag.span_suggestion(
                         else_block.span,
@@ -173,8 +169,7 @@ impl CollapsibleIf {
             && cx.tcx.hir_attrs(inner.hir_id).is_empty()
             && let ExprKind::If(check_inner, _, None) = &inner.kind
             && self.eligible_condition(cx, check_inner)
-            && let ctxt = expr.span.ctxt()
-            && inner.span.ctxt() == ctxt
+            && expr.span.eq_ctxt(inner.span)
             && !block_starts_with_significant_tokens(cx, then, inner, self.lint_commented_code)
         {
             span_lint_and_then(
@@ -262,14 +257,9 @@ fn block_starts_with_significant_tokens(
 /// If `block` is a block with either one expression or a statement containing an expression,
 /// return the expression. We don't peel blocks recursively, as extra blocks might be intentional.
 fn expr_block<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> {
-    match block.stmts {
-        [] => block.expr,
-        [
-            Stmt {
-                kind: StmtKind::Semi(expr),
-                ..
-            },
-        ] if block.expr.is_none() => Some(expr),
+    match (block.stmts, block.expr) {
+        ([], expr) => expr,
+        ([stmt], None) if let StmtKind::Semi(expr) = stmt.kind => Some(expr),
         _ => None,
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/copy_iterator.rs b/src/tools/clippy/clippy_lints/src/copy_iterator.rs
index 4ecf3e41611..51aebd8b0cf 100644
--- a/src/tools/clippy/clippy_lints/src/copy_iterator.rs
+++ b/src/tools/clippy/clippy_lints/src/copy_iterator.rs
@@ -37,12 +37,12 @@ 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(trait_ref),
+            of_trait: Some(of_trait),
             ..
         }) = item.kind
             && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity()
             && is_copy(cx, ty)
-            && let Some(trait_id) = trait_ref.trait_def_id()
+            && let Some(trait_id) = of_trait.trait_ref.trait_def_id()
             && cx.tcx.is_diagnostic_item(sym::Iterator, trait_id)
         {
             span_lint_and_note(
diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs
index e1cb08e361c..2a4bedc1845 100644
--- a/src/tools/clippy/clippy_lints/src/declared_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs
@@ -35,6 +35,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
     crate::await_holding_invalid::AWAIT_HOLDING_REFCELL_REF_INFO,
     crate::blocks_in_conditions::BLOCKS_IN_CONDITIONS_INFO,
     crate::bool_assert_comparison::BOOL_ASSERT_COMPARISON_INFO,
+    crate::bool_comparison::BOOL_COMPARISON_INFO,
     crate::bool_to_int_with_if::BOOL_TO_INT_WITH_IF_INFO,
     crate::booleans::NONMINIMAL_BOOL_INFO,
     crate::booleans::OVERLY_COMPLEX_BOOL_EXPR_INFO,
@@ -187,6 +188,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
     crate::from_raw_with_void_ptr::FROM_RAW_WITH_VOID_PTR_INFO,
     crate::from_str_radix_10::FROM_STR_RADIX_10_INFO,
     crate::functions::DOUBLE_MUST_USE_INFO,
+    crate::functions::DUPLICATE_UNDERSCORE_ARGUMENT_INFO,
     crate::functions::IMPL_TRAIT_IN_PARAMS_INFO,
     crate::functions::MISNAMED_GETTERS_INFO,
     crate::functions::MUST_USE_CANDIDATE_INFO,
@@ -446,10 +448,12 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
     crate::methods::OR_THEN_UNWRAP_INFO,
     crate::methods::PATH_BUF_PUSH_OVERWRITE_INFO,
     crate::methods::PATH_ENDS_WITH_EXT_INFO,
+    crate::methods::PTR_OFFSET_WITH_CAST_INFO,
     crate::methods::RANGE_ZIP_WITH_LEN_INFO,
     crate::methods::READONLY_WRITE_LOCK_INFO,
     crate::methods::READ_LINE_WITHOUT_TRIM_INFO,
     crate::methods::REDUNDANT_AS_STR_INFO,
+    crate::methods::REDUNDANT_ITER_CLONED_INFO,
     crate::methods::REPEAT_ONCE_INFO,
     crate::methods::RESULT_FILTER_MAP_INFO,
     crate::methods::RESULT_MAP_OR_INTO_OPTION_INFO,
@@ -501,11 +505,9 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
     crate::min_ident_chars::MIN_IDENT_CHARS_INFO,
     crate::minmax::MIN_MAX_INFO,
     crate::misc::SHORT_CIRCUIT_STATEMENT_INFO,
-    crate::misc::TOPLEVEL_REF_ARG_INFO,
     crate::misc::USED_UNDERSCORE_BINDING_INFO,
     crate::misc::USED_UNDERSCORE_ITEMS_INFO,
     crate::misc_early::BUILTIN_TYPE_SHADOW_INFO,
-    crate::misc_early::DUPLICATE_UNDERSCORE_ARGUMENT_INFO,
     crate::misc_early::MIXED_CASE_HEX_LITERALS_INFO,
     crate::misc_early::REDUNDANT_AT_REST_PATTERN_INFO,
     crate::misc_early::REDUNDANT_PATTERN_INFO,
@@ -538,7 +540,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
     crate::mutex_atomic::MUTEX_ATOMIC_INFO,
     crate::mutex_atomic::MUTEX_INTEGER_INFO,
     crate::needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE_INFO,
-    crate::needless_bool::BOOL_COMPARISON_INFO,
     crate::needless_bool::NEEDLESS_BOOL_INFO,
     crate::needless_bool::NEEDLESS_BOOL_ASSIGN_INFO,
     crate::needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE_INFO,
@@ -625,7 +626,6 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
     crate::ptr::MUT_FROM_REF_INFO,
     crate::ptr::PTR_ARG_INFO,
     crate::ptr::PTR_EQ_INFO,
-    crate::ptr_offset_with_cast::PTR_OFFSET_WITH_CAST_INFO,
     crate::pub_underscore_fields::PUB_UNDERSCORE_FIELDS_INFO,
     crate::pub_use::PUB_USE_INFO,
     crate::question_mark::QUESTION_MARK_INFO,
@@ -706,6 +706,7 @@ pub static LINTS: &[&::declare_clippy_lint::LintInfo] = &[
     crate::tests_outside_test_module::TESTS_OUTSIDE_TEST_MODULE_INFO,
     crate::to_digit_is_some::TO_DIGIT_IS_SOME_INFO,
     crate::to_string_trait_impl::TO_STRING_TRAIT_IMPL_INFO,
+    crate::toplevel_ref_arg::TOPLEVEL_REF_ARG_INFO,
     crate::trailing_empty_array::TRAILING_EMPTY_ARRAY_INFO,
     crate::trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS_INFO,
     crate::trait_bounds::TYPE_REPETITION_IN_BOUNDS_INFO,
diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs
index 995a1209595..a70105db194 100644
--- a/src/tools/clippy/clippy_lints/src/dereference.rs
+++ b/src/tools/clippy/clippy_lints/src/dereference.rs
@@ -1,12 +1,10 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
 use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
 use clippy_utils::sugg::has_enclosing_paren;
-use clippy_utils::ty::{implements_trait, is_manually_drop};
+use clippy_utils::ty::{adjust_derefs_manually_drop, implements_trait, is_manually_drop, peel_and_count_ty_refs};
 use clippy_utils::{
     DefinedTy, ExprUseNode, expr_use_ctxt, get_parent_expr, is_block_like, is_lint_allowed, path_to_local,
-    peel_middle_ty_refs,
 };
-use core::mem;
 use rustc_ast::util::parser::ExprPrecedence;
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_errors::Applicability;
@@ -365,7 +363,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
                                 //   priority.
                                 if let Some(fn_id) = typeck.type_dependent_def_id(hir_id)
                                     && let Some(trait_id) = cx.tcx.trait_of_assoc(fn_id)
-                                    && let arg_ty = cx.tcx.erase_regions(adjusted_ty)
+                                    && let arg_ty = cx.tcx.erase_and_anonymize_regions(adjusted_ty)
                                     && let ty::Ref(_, sub_ty, _) = *arg_ty.kind()
                                     && let args =
                                         typeck.node_args_opt(hir_id).map(|args| &args[1..]).unwrap_or_default()
@@ -707,14 +705,6 @@ fn try_parse_ref_op<'tcx>(
     ))
 }
 
-// Checks if the adjustments contains a deref of `ManuallyDrop<_>`
-fn adjust_derefs_manually_drop<'tcx>(adjustments: &'tcx [Adjustment<'tcx>], mut ty: Ty<'tcx>) -> bool {
-    adjustments.iter().any(|a| {
-        let ty = mem::replace(&mut ty, a.target);
-        matches!(a.kind, Adjust::Deref(Some(ref op)) if op.mutbl == Mutability::Mut) && is_manually_drop(ty)
-    })
-}
-
 // Checks whether the type for a deref call actually changed the type, not just the mutability of
 // the reference.
 fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool {
@@ -951,7 +941,7 @@ fn report<'tcx>(
             let (expr_str, expr_is_macro_call) =
                 snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app);
             let ty = typeck.expr_ty(expr);
-            let (_, ref_count) = peel_middle_ty_refs(ty);
+            let (_, ref_count, _) = peel_and_count_ty_refs(ty);
             let deref_str = if ty_changed_count >= ref_count && ref_count != 0 {
                 // a deref call changing &T -> &U requires two deref operators the first time
                 // this occurs. One to remove the reference, a second to call the deref impl.
@@ -1054,7 +1044,7 @@ fn report<'tcx>(
             if let ty::Ref(_, dst, _) = data.adjusted_ty.kind()
                 && dst.is_slice()
             {
-                let (src, n_src_refs) = peel_middle_ty_refs(ty);
+                let (src, n_src_refs, _) = peel_and_count_ty_refs(ty);
                 if n_src_refs >= 2 && src.is_array() {
                     return;
                 }
diff --git a/src/tools/clippy/clippy_lints/src/derivable_impls.rs b/src/tools/clippy/clippy_lints/src/derivable_impls.rs
index 0a481ddcd12..06c2393e0a3 100644
--- a/src/tools/clippy/clippy_lints/src/derivable_impls.rs
+++ b/src/tools/clippy/clippy_lints/src/derivable_impls.rs
@@ -10,7 +10,7 @@ use rustc_hir::{
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::adjustment::{Adjust, PointerCoercion};
-use rustc_middle::ty::{self, AdtDef, GenericArgsRef, Ty, TypeckResults};
+use rustc_middle::ty::{self, AdtDef, GenericArgsRef, Ty, TypeckResults, VariantDef};
 use rustc_session::impl_lint_pass;
 use rustc_span::sym;
 
@@ -85,6 +85,13 @@ fn contains_trait_object(ty: Ty<'_>) -> bool {
     }
 }
 
+fn determine_derive_macro(cx: &LateContext<'_>, is_const: bool) -> Option<&'static str> {
+    (!is_const)
+        .then_some("derive")
+        .or_else(|| cx.tcx.features().enabled(sym::derive_const).then_some("derive_const"))
+}
+
+#[expect(clippy::too_many_arguments)]
 fn check_struct<'tcx>(
     cx: &LateContext<'tcx>,
     item: &'tcx Item<'_>,
@@ -93,6 +100,7 @@ fn check_struct<'tcx>(
     adt_def: AdtDef<'_>,
     ty_args: GenericArgsRef<'_>,
     typeck_results: &'tcx TypeckResults<'tcx>,
+    is_const: bool,
 ) {
     if let TyKind::Path(QPath::Resolved(_, p)) = self_ty.kind
         && let Some(PathSegment { args, .. }) = p.segments.last()
@@ -125,14 +133,18 @@ fn check_struct<'tcx>(
         ExprKind::Tup(fields) => fields.iter().all(is_default_without_adjusts),
         ExprKind::Call(callee, args) if is_path_self(callee) => args.iter().all(is_default_without_adjusts),
         ExprKind::Struct(_, fields, _) => fields.iter().all(|ef| is_default_without_adjusts(ef.expr)),
-        _ => false,
+        _ => return,
     };
 
-    if should_emit {
+    if should_emit && let Some(derive_snippet) = determine_derive_macro(cx, is_const) {
         let struct_span = cx.tcx.def_span(adt_def.did());
+        let indent_enum = indent_of(cx, struct_span).unwrap_or(0);
         let suggestions = vec![
             (item.span, String::new()), // Remove the manual implementation
-            (struct_span.shrink_to_lo(), "#[derive(Default)]\n".to_string()), // Add the derive attribute
+            (
+                struct_span.shrink_to_lo(),
+                format!("#[{derive_snippet}(Default)]\n{}", " ".repeat(indent_enum)),
+            ), // Add the derive attribute
         ];
 
         span_lint_and_then(cx, DERIVABLE_IMPLS, item.span, "this `impl` can be derived", |diag| {
@@ -145,11 +157,41 @@ fn check_struct<'tcx>(
     }
 }
 
-fn check_enum<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>, func_expr: &Expr<'_>, adt_def: AdtDef<'_>) {
-    if let ExprKind::Path(QPath::Resolved(None, p)) = &peel_blocks(func_expr).kind
-        && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = p.res
-        && let variant_id = cx.tcx.parent(id)
-        && let Some(variant_def) = adt_def.variants().iter().find(|v| v.def_id == variant_id)
+fn extract_enum_variant<'tcx>(
+    cx: &LateContext<'tcx>,
+    func_expr: &'tcx Expr<'tcx>,
+    adt_def: AdtDef<'tcx>,
+) -> Option<&'tcx VariantDef> {
+    match &peel_blocks(func_expr).kind {
+        ExprKind::Path(QPath::Resolved(None, p))
+            if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Const), id) = p.res
+                && let variant_id = cx.tcx.parent(id)
+                && let Some(variant_def) = adt_def.variants().iter().find(|v| v.def_id == variant_id) =>
+        {
+            Some(variant_def)
+        },
+        ExprKind::Path(QPath::TypeRelative(ty, segment))
+            if let TyKind::Path(QPath::Resolved(None, p)) = &ty.kind
+                && let Res::SelfTyAlias {
+                    is_trait_impl: true, ..
+                } = p.res
+                && let variant_ident = segment.ident
+                && let Some(variant_def) = adt_def.variants().iter().find(|v| v.ident(cx.tcx) == variant_ident) =>
+        {
+            Some(variant_def)
+        },
+        _ => None,
+    }
+}
+
+fn check_enum<'tcx>(
+    cx: &LateContext<'tcx>,
+    item: &'tcx Item<'tcx>,
+    func_expr: &'tcx Expr<'tcx>,
+    adt_def: AdtDef<'tcx>,
+    is_const: bool,
+) {
+    if let Some(variant_def) = extract_enum_variant(cx, func_expr, adt_def)
         && variant_def.fields.is_empty()
         && !variant_def.is_field_list_non_exhaustive()
     {
@@ -158,11 +200,15 @@ fn check_enum<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>, func_expr: &Ex
         let variant_span = cx.tcx.def_span(variant_def.def_id);
         let indent_variant = indent_of(cx, variant_span).unwrap_or(0);
 
+        let Some(derive_snippet) = determine_derive_macro(cx, is_const) else {
+            return;
+        };
+
         let suggestions = vec![
             (item.span, String::new()), // Remove the manual implementation
             (
                 enum_span.shrink_to_lo(),
-                format!("#[derive(Default)]\n{}", " ".repeat(indent_enum)),
+                format!("#[{derive_snippet}(Default)]\n{}", " ".repeat(indent_enum)),
             ), // Add the derive attribute
             (
                 variant_span.shrink_to_lo(),
@@ -183,14 +229,14 @@ 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(trait_ref),
+            of_trait: Some(of_trait),
             items: [child],
             self_ty,
             ..
         }) = item.kind
             && !cx.tcx.is_automatically_derived(item.owner_id.to_def_id())
             && !item.span.from_expansion()
-            && let Some(def_id) = trait_ref.trait_def_id()
+            && let Some(def_id) = of_trait.trait_ref.trait_def_id()
             && cx.tcx.is_diagnostic_item(sym::Default, def_id)
             && let impl_item_hir = child.hir_id()
             && let Node::ImplItem(impl_item) = cx.tcx.hir_node(impl_item_hir)
@@ -201,10 +247,20 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
             && !attrs.iter().any(|attr| attr.doc_str().is_some())
             && cx.tcx.hir_attrs(impl_item_hir).is_empty()
         {
+            let is_const = of_trait.constness == hir::Constness::Const;
             if adt_def.is_struct() {
-                check_struct(cx, item, self_ty, func_expr, adt_def, args, cx.tcx.typeck_body(*b));
+                check_struct(
+                    cx,
+                    item,
+                    self_ty,
+                    func_expr,
+                    adt_def,
+                    args,
+                    cx.tcx.typeck_body(*b),
+                    is_const,
+                );
             } else if adt_def.is_enum() && self.msrv.meets(cx, msrvs::DEFAULT_ENUM_ATTRIBUTE) {
-                check_enum(cx, item, func_expr, adt_def);
+                check_enum(cx, item, func_expr, adt_def, is_const);
             }
         }
     }
diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs
deleted file mode 100644
index 49dd1bb09c6..00000000000
--- a/src/tools/clippy/clippy_lints/src/derive.rs
+++ /dev/null
@@ -1,526 +0,0 @@
-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, paths};
-use rustc_errors::Applicability;
-use rustc_hir::def_id::DefId;
-use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn, walk_item};
-use rustc_hir::{self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, Impl, Item, ItemKind, UnsafeSource};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::hir::nested_filter;
-use rustc_middle::ty::{
-    self, ClauseKind, GenericArgKind, GenericParamDefKind, ParamEnv, TraitPredicate, Ty, TyCtxt, Upcast,
-};
-use rustc_session::declare_lint_pass;
-use rustc_span::def_id::LocalDefId;
-use rustc_span::{Span, sym};
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Lints against manual `PartialEq` implementations for types with a derived `Hash`
-    /// implementation.
-    ///
-    /// ### Why is this bad?
-    /// The implementation of these traits must agree (for
-    /// example for use with `HashMap`) so it’s probably a bad idea to use a
-    /// default-generated `Hash` implementation with an explicitly defined
-    /// `PartialEq`. In particular, the following must hold for any type:
-    ///
-    /// ```text
-    /// k1 == k2 ⇒ hash(k1) == hash(k2)
-    /// ```
-    ///
-    /// ### Example
-    /// ```ignore
-    /// #[derive(Hash)]
-    /// struct Foo;
-    ///
-    /// impl PartialEq for Foo {
-    ///     ...
-    /// }
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub DERIVED_HASH_WITH_MANUAL_EQ,
-    correctness,
-    "deriving `Hash` but implementing `PartialEq` explicitly"
-}
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Lints against manual `PartialOrd` and `Ord` implementations for types with a derived `Ord`
-    /// or `PartialOrd` implementation.
-    ///
-    /// ### Why is this bad?
-    /// The implementation of these traits must agree (for
-    /// example for use with `sort`) so it’s probably a bad idea to use a
-    /// default-generated `Ord` implementation with an explicitly defined
-    /// `PartialOrd`. In particular, the following must hold for any type
-    /// implementing `Ord`:
-    ///
-    /// ```text
-    /// k1.cmp(&k2) == k1.partial_cmp(&k2).unwrap()
-    /// ```
-    ///
-    /// ### Example
-    /// ```rust,ignore
-    /// #[derive(Ord, PartialEq, Eq)]
-    /// struct Foo;
-    ///
-    /// impl PartialOrd for Foo {
-    ///     ...
-    /// }
-    /// ```
-    /// Use instead:
-    /// ```rust,ignore
-    /// #[derive(PartialEq, Eq)]
-    /// struct Foo;
-    ///
-    /// impl PartialOrd for Foo {
-    ///     fn partial_cmp(&self, other: &Foo) -> Option<Ordering> {
-    ///        Some(self.cmp(other))
-    ///     }
-    /// }
-    ///
-    /// impl Ord for Foo {
-    ///     ...
-    /// }
-    /// ```
-    /// or, if you don't need a custom ordering:
-    /// ```rust,ignore
-    /// #[derive(Ord, PartialOrd, PartialEq, Eq)]
-    /// struct Foo;
-    /// ```
-    #[clippy::version = "1.47.0"]
-    pub DERIVE_ORD_XOR_PARTIAL_ORD,
-    correctness,
-    "deriving `Ord` but implementing `PartialOrd` explicitly"
-}
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for explicit `Clone` implementations for `Copy`
-    /// types.
-    ///
-    /// ### Why is this bad?
-    /// To avoid surprising behavior, these traits should
-    /// agree and the behavior of `Copy` cannot be overridden. In almost all
-    /// situations a `Copy` type should have a `Clone` implementation that does
-    /// nothing more than copy the object, which is what `#[derive(Copy, Clone)]`
-    /// gets you.
-    ///
-    /// ### Example
-    /// ```rust,ignore
-    /// #[derive(Copy)]
-    /// struct Foo;
-    ///
-    /// impl Clone for Foo {
-    ///     // ..
-    /// }
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub EXPL_IMPL_CLONE_ON_COPY,
-    pedantic,
-    "implementing `Clone` explicitly on `Copy` types"
-}
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for deriving `serde::Deserialize` on a type that
-    /// has methods using `unsafe`.
-    ///
-    /// ### Why is this bad?
-    /// Deriving `serde::Deserialize` will create a constructor
-    /// that may violate invariants held by another constructor.
-    ///
-    /// ### Example
-    /// ```rust,ignore
-    /// use serde::Deserialize;
-    ///
-    /// #[derive(Deserialize)]
-    /// pub struct Foo {
-    ///     // ..
-    /// }
-    ///
-    /// impl Foo {
-    ///     pub fn new() -> Self {
-    ///         // setup here ..
-    ///     }
-    ///
-    ///     pub unsafe fn parts() -> (&str, &str) {
-    ///         // assumes invariants hold
-    ///     }
-    /// }
-    /// ```
-    #[clippy::version = "1.45.0"]
-    pub UNSAFE_DERIVE_DESERIALIZE,
-    pedantic,
-    "deriving `serde::Deserialize` on a type that has methods using `unsafe`"
-}
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for types that derive `PartialEq` and could implement `Eq`.
-    ///
-    /// ### Why is this bad?
-    /// If a type `T` derives `PartialEq` and all of its members implement `Eq`,
-    /// then `T` can always implement `Eq`. Implementing `Eq` allows `T` to be used
-    /// in APIs that require `Eq` types. It also allows structs containing `T` to derive
-    /// `Eq` themselves.
-    ///
-    /// ### Example
-    /// ```no_run
-    /// #[derive(PartialEq)]
-    /// struct Foo {
-    ///     i_am_eq: i32,
-    ///     i_am_eq_too: Vec<String>,
-    /// }
-    /// ```
-    /// Use instead:
-    /// ```no_run
-    /// #[derive(PartialEq, Eq)]
-    /// struct Foo {
-    ///     i_am_eq: i32,
-    ///     i_am_eq_too: Vec<String>,
-    /// }
-    /// ```
-    #[clippy::version = "1.63.0"]
-    pub DERIVE_PARTIAL_EQ_WITHOUT_EQ,
-    nursery,
-    "deriving `PartialEq` on a type that can implement `Eq`, without implementing `Eq`"
-}
-
-declare_lint_pass!(Derive => [
-    EXPL_IMPL_CLONE_ON_COPY,
-    DERIVED_HASH_WITH_MANUAL_EQ,
-    DERIVE_ORD_XOR_PARTIAL_ORD,
-    UNSAFE_DERIVE_DESERIALIZE,
-    DERIVE_PARTIAL_EQ_WITHOUT_EQ
-]);
-
-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(trait_ref),
-            ..
-        }) = item.kind
-        {
-            let ty = cx.tcx.type_of(item.owner_id).instantiate_identity();
-            let is_automatically_derived = cx.tcx.is_automatically_derived(item.owner_id.to_def_id());
-
-            check_hash_peq(cx, item.span, trait_ref, ty, is_automatically_derived);
-            check_ord_partial_ord(cx, item.span, trait_ref, ty, is_automatically_derived);
-
-            if is_automatically_derived {
-                check_unsafe_derive_deserialize(cx, item, trait_ref, ty);
-                check_partial_eq_without_eq(cx, item.span, trait_ref, ty);
-            } else {
-                check_copy_clone(cx, item, trait_ref, ty);
-            }
-        }
-    }
-}
-
-/// Implementation of the `DERIVED_HASH_WITH_MANUAL_EQ` lint.
-fn check_hash_peq<'tcx>(
-    cx: &LateContext<'tcx>,
-    span: Span,
-    trait_ref: &hir::TraitRef<'_>,
-    ty: Ty<'tcx>,
-    hash_is_automatically_derived: bool,
-) {
-    if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait()
-        && let Some(def_id) = trait_ref.trait_def_id()
-        && cx.tcx.is_diagnostic_item(sym::Hash, def_id)
-    {
-        // Look for the PartialEq implementations for `ty`
-        cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| {
-            let peq_is_automatically_derived = cx.tcx.is_automatically_derived(impl_id);
-
-            if !hash_is_automatically_derived || peq_is_automatically_derived {
-                return;
-            }
-
-            let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation");
-
-            // Only care about `impl PartialEq<Foo> for Foo`
-            // For `impl PartialEq<B> for A, input_types is [A, B]
-            if trait_ref.instantiate_identity().args.type_at(1) == ty {
-                span_lint_and_then(
-                    cx,
-                    DERIVED_HASH_WITH_MANUAL_EQ,
-                    span,
-                    "you are deriving `Hash` but have implemented `PartialEq` explicitly",
-                    |diag| {
-                        if let Some(local_def_id) = impl_id.as_local() {
-                            let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id);
-                            diag.span_note(cx.tcx.hir_span(hir_id), "`PartialEq` implemented here");
-                        }
-                    },
-                );
-            }
-        });
-    }
-}
-
-/// Implementation of the `DERIVE_ORD_XOR_PARTIAL_ORD` lint.
-fn check_ord_partial_ord<'tcx>(
-    cx: &LateContext<'tcx>,
-    span: Span,
-    trait_ref: &hir::TraitRef<'_>,
-    ty: Ty<'tcx>,
-    ord_is_automatically_derived: bool,
-) {
-    if let Some(ord_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Ord)
-        && let Some(partial_ord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait()
-        && let Some(def_id) = &trait_ref.trait_def_id()
-        && *def_id == ord_trait_def_id
-    {
-        // Look for the PartialOrd implementations for `ty`
-        cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| {
-            let partial_ord_is_automatically_derived = cx.tcx.is_automatically_derived(impl_id);
-
-            if partial_ord_is_automatically_derived == ord_is_automatically_derived {
-                return;
-            }
-
-            let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation");
-
-            // Only care about `impl PartialOrd<Foo> for Foo`
-            // For `impl PartialOrd<B> for A, input_types is [A, B]
-            if trait_ref.instantiate_identity().args.type_at(1) == ty {
-                let mess = if partial_ord_is_automatically_derived {
-                    "you are implementing `Ord` explicitly but have derived `PartialOrd`"
-                } else {
-                    "you are deriving `Ord` but have implemented `PartialOrd` explicitly"
-                };
-
-                span_lint_and_then(cx, DERIVE_ORD_XOR_PARTIAL_ORD, span, mess, |diag| {
-                    if let Some(local_def_id) = impl_id.as_local() {
-                        let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id);
-                        diag.span_note(cx.tcx.hir_span(hir_id), "`PartialOrd` implemented here");
-                    }
-                });
-            }
-        });
-    }
-}
-
-/// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint.
-fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) {
-    let clone_id = match cx.tcx.lang_items().clone_trait() {
-        Some(id) if trait_ref.trait_def_id() == Some(id) => id,
-        _ => return,
-    };
-    let Some(copy_id) = cx.tcx.lang_items().copy_trait() else {
-        return;
-    };
-    let (ty_adt, ty_subs) = match *ty.kind() {
-        // Unions can't derive clone.
-        ty::Adt(adt, subs) if !adt.is_union() => (adt, subs),
-        _ => return,
-    };
-    // If the current self type doesn't implement Copy (due to generic constraints), search to see if
-    // 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.local_trait_impls(copy_id).iter().any(|&id| {
-                matches!(cx.tcx.type_of(id).instantiate_identity().kind(), ty::Adt(adt, _)
-                                        if ty_adt.did() == adt.did())
-            });
-            if !has_copy_impl {
-                return;
-            }
-        } else {
-            return;
-        }
-    }
-    // Derive constrains all generic types to requiring Clone. Check if any type is not constrained for
-    // this impl.
-    if ty_subs.types().any(|ty| !implements_trait(cx, ty, clone_id, &[])) {
-        return;
-    }
-    // `#[repr(packed)]` structs with type/const parameters can't derive `Clone`.
-    // https://github.com/rust-lang/rust-clippy/issues/10188
-    if ty_adt.repr().packed()
-        && ty_subs
-            .iter()
-            .any(|arg| matches!(arg.kind(), GenericArgKind::Type(_) | GenericArgKind::Const(_)))
-    {
-        return;
-    }
-    // The presence of `unsafe` fields prevents deriving `Clone` automatically
-    if ty_adt.all_fields().any(|f| f.safety.is_unsafe()) {
-        return;
-    }
-
-    span_lint_and_note(
-        cx,
-        EXPL_IMPL_CLONE_ON_COPY,
-        item.span,
-        "you are implementing `Clone` explicitly on a `Copy` type",
-        Some(item.span),
-        "consider deriving `Clone` or removing `Copy`",
-    );
-}
-
-/// Implementation of the `UNSAFE_DERIVE_DESERIALIZE` lint.
-fn check_unsafe_derive_deserialize<'tcx>(
-    cx: &LateContext<'tcx>,
-    item: &Item<'_>,
-    trait_ref: &hir::TraitRef<'_>,
-    ty: Ty<'tcx>,
-) {
-    fn has_unsafe<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>) -> bool {
-        let mut visitor = UnsafeVisitor { cx };
-        walk_item(&mut visitor, item).is_break()
-    }
-
-    if let Some(trait_def_id) = trait_ref.trait_def_id()
-        && paths::SERDE_DESERIALIZE.matches(cx, trait_def_id)
-        && let ty::Adt(def, _) = ty.kind()
-        && let Some(local_def_id) = def.did().as_local()
-        && let adt_hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id)
-        && !is_lint_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id)
-        && cx
-            .tcx
-            .inherent_impls(def.did())
-            .iter()
-            .map(|imp_did| cx.tcx.hir_expect_item(imp_did.expect_local()))
-            .any(|imp| has_unsafe(cx, imp))
-    {
-        span_lint_hir_and_then(
-            cx,
-            UNSAFE_DERIVE_DESERIALIZE,
-            adt_hir_id,
-            item.span,
-            "you are deriving `serde::Deserialize` on a type that has methods using `unsafe`",
-            |diag| {
-                diag.help(
-                    "consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html",
-                );
-            },
-        );
-    }
-}
-
-struct UnsafeVisitor<'a, 'tcx> {
-    cx: &'a LateContext<'tcx>,
-}
-
-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,
-    ) -> Self::Result {
-        if let Some(header) = kind.header()
-            && header.is_unsafe()
-        {
-            ControlFlow::Break(())
-        } else {
-            walk_fn(self, kind, decl, body_id, id)
-        }
-    }
-
-    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) -> Self::Result {
-        if let ExprKind::Block(block, _) = expr.kind
-            && block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
-            && block
-                .span
-                .source_callee()
-                .and_then(|expr| expr.macro_def_id)
-                .is_none_or(|did| !self.cx.tcx.is_diagnostic_item(sym::pin_macro, did))
-        {
-            return ControlFlow::Break(());
-        }
-
-        walk_expr(self, expr)
-    }
-
-    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
-        self.cx.tcx
-    }
-}
-
-/// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint.
-fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) {
-    if let ty::Adt(adt, args) = ty.kind()
-        && cx.tcx.visibility(adt.did()).is_public()
-        && let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq)
-        && let Some(def_id) = trait_ref.trait_def_id()
-        && 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 typing_env = typing_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, typing_env, ty, eq_trait_def_id, None, &[]))
-    {
-        span_lint_hir_and_then(
-            cx,
-            DERIVE_PARTIAL_EQ_WITHOUT_EQ,
-            cx.tcx.local_def_id_to_hir_id(local_def_id),
-            span.ctxt().outer_expn_data().call_site,
-            "you are deriving `PartialEq` and can implement `Eq`",
-            |diag| {
-                diag.span_suggestion(
-                    span.ctxt().outer_expn_data().call_site,
-                    "consider deriving `Eq` as well",
-                    "PartialEq, Eq",
-                    Applicability::MachineApplicable,
-                );
-            },
-        );
-    }
-}
-
-fn ty_implements_eq_trait<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, eq_trait_id: DefId) -> bool {
-    tcx.non_blanket_impls_for_ty(eq_trait_id, ty).next().is_some()
-}
-
-/// Creates the `ParamEnv` used for the given type's derived `Eq` impl.
-fn typing_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
-        .generics_of(did)
-        .own_params
-        .iter()
-        .map(|p| (p, matches!(p.kind, GenericParamDefKind::Type { .. })))
-        .collect::<Vec<_>>();
-
-    let ty_predicates = tcx.predicates_of(did).predicates;
-    for (p, _) in ty_predicates {
-        if let ClauseKind::Trait(p) = p.kind().skip_binder()
-            && p.trait_ref.def_id == eq_trait_id
-            && let ty::Param(self_ty) = p.trait_ref.self_ty().kind()
-        {
-            // Flag types which already have an `Eq` bound.
-            params[self_ty.index as usize].1 = false;
-        }
-    }
-
-    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 {
-                trait_ref: ty::TraitRef::new(tcx, eq_trait_id, [tcx.mk_param_from_def(param)]),
-                polarity: ty::PredicatePolarity::Positive,
-            })
-            .upcast(tcx)
-        }),
-    )));
-    ty::TypingEnv {
-        typing_mode: ty::TypingMode::non_body_analysis(),
-        param_env,
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/derive/derive_ord_xor_partial_ord.rs b/src/tools/clippy/clippy_lints/src/derive/derive_ord_xor_partial_ord.rs
new file mode 100644
index 00000000000..cbbcb2f7a3b
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/derive/derive_ord_xor_partial_ord.rs
@@ -0,0 +1,50 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+use rustc_middle::ty::Ty;
+use rustc_span::{Span, sym};
+
+use super::DERIVE_ORD_XOR_PARTIAL_ORD;
+
+/// Implementation of the `DERIVE_ORD_XOR_PARTIAL_ORD` lint.
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    span: Span,
+    trait_ref: &hir::TraitRef<'_>,
+    ty: Ty<'tcx>,
+    ord_is_automatically_derived: bool,
+) {
+    if let Some(ord_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Ord)
+        && let Some(partial_ord_trait_def_id) = cx.tcx.lang_items().partial_ord_trait()
+        && let Some(def_id) = &trait_ref.trait_def_id()
+        && *def_id == ord_trait_def_id
+    {
+        // Look for the PartialOrd implementations for `ty`
+        cx.tcx.for_each_relevant_impl(partial_ord_trait_def_id, ty, |impl_id| {
+            let partial_ord_is_automatically_derived = cx.tcx.is_automatically_derived(impl_id);
+
+            if partial_ord_is_automatically_derived == ord_is_automatically_derived {
+                return;
+            }
+
+            let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation");
+
+            // Only care about `impl PartialOrd<Foo> for Foo`
+            // For `impl PartialOrd<B> for A, input_types is [A, B]
+            if trait_ref.instantiate_identity().args.type_at(1) == ty {
+                let mess = if partial_ord_is_automatically_derived {
+                    "you are implementing `Ord` explicitly but have derived `PartialOrd`"
+                } else {
+                    "you are deriving `Ord` but have implemented `PartialOrd` explicitly"
+                };
+
+                span_lint_and_then(cx, DERIVE_ORD_XOR_PARTIAL_ORD, span, mess, |diag| {
+                    if let Some(local_def_id) = impl_id.as_local() {
+                        let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id);
+                        diag.span_note(cx.tcx.hir_span(hir_id), "`PartialOrd` implemented here");
+                    }
+                });
+            }
+        });
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/derive/derive_partial_eq_without_eq.rs b/src/tools/clippy/clippy_lints/src/derive/derive_partial_eq_without_eq.rs
new file mode 100644
index 00000000000..ed7881c461f
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/derive/derive_partial_eq_without_eq.rs
@@ -0,0 +1,87 @@
+use clippy_utils::diagnostics::span_lint_hir_and_then;
+use clippy_utils::has_non_exhaustive_attr;
+use clippy_utils::ty::implements_trait_with_env;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_hir::def_id::DefId;
+use rustc_lint::LateContext;
+use rustc_middle::ty::{self, ClauseKind, GenericParamDefKind, ParamEnv, TraitPredicate, Ty, TyCtxt, Upcast};
+use rustc_span::{Span, sym};
+
+use super::DERIVE_PARTIAL_EQ_WITHOUT_EQ;
+
+/// Implementation of the `DERIVE_PARTIAL_EQ_WITHOUT_EQ` lint.
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) {
+    if let ty::Adt(adt, args) = ty.kind()
+        && cx.tcx.visibility(adt.did()).is_public()
+        && let Some(eq_trait_def_id) = cx.tcx.get_diagnostic_item(sym::Eq)
+        && let Some(def_id) = trait_ref.trait_def_id()
+        && 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 typing_env = typing_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, typing_env, ty, eq_trait_def_id, None, &[]))
+    {
+        span_lint_hir_and_then(
+            cx,
+            DERIVE_PARTIAL_EQ_WITHOUT_EQ,
+            cx.tcx.local_def_id_to_hir_id(local_def_id),
+            span.ctxt().outer_expn_data().call_site,
+            "you are deriving `PartialEq` and can implement `Eq`",
+            |diag| {
+                diag.span_suggestion(
+                    span.ctxt().outer_expn_data().call_site,
+                    "consider deriving `Eq` as well",
+                    "PartialEq, Eq",
+                    Applicability::MachineApplicable,
+                );
+            },
+        );
+    }
+}
+
+fn ty_implements_eq_trait<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, eq_trait_id: DefId) -> bool {
+    tcx.non_blanket_impls_for_ty(eq_trait_id, ty).next().is_some()
+}
+
+/// Creates the `ParamEnv` used for the given type's derived `Eq` impl.
+fn typing_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
+        .generics_of(did)
+        .own_params
+        .iter()
+        .map(|p| (p, matches!(p.kind, GenericParamDefKind::Type { .. })))
+        .collect::<Vec<_>>();
+
+    let ty_predicates = tcx.predicates_of(did).predicates;
+    for (p, _) in ty_predicates {
+        if let ClauseKind::Trait(p) = p.kind().skip_binder()
+            && p.trait_ref.def_id == eq_trait_id
+            && let ty::Param(self_ty) = p.trait_ref.self_ty().kind()
+        {
+            // Flag types which already have an `Eq` bound.
+            params[self_ty.index as usize].1 = false;
+        }
+    }
+
+    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 {
+                trait_ref: ty::TraitRef::new(tcx, eq_trait_id, [tcx.mk_param_from_def(param)]),
+                polarity: ty::PredicatePolarity::Positive,
+            })
+            .upcast(tcx)
+        }),
+    )));
+    ty::TypingEnv {
+        typing_mode: ty::TypingMode::non_body_analysis(),
+        param_env,
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/derive/derived_hash_with_manual_eq.rs b/src/tools/clippy/clippy_lints/src/derive/derived_hash_with_manual_eq.rs
new file mode 100644
index 00000000000..6f36a58025a
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/derive/derived_hash_with_manual_eq.rs
@@ -0,0 +1,49 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+use rustc_middle::ty::Ty;
+use rustc_span::{Span, sym};
+
+use super::DERIVED_HASH_WITH_MANUAL_EQ;
+
+/// Implementation of the `DERIVED_HASH_WITH_MANUAL_EQ` lint.
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    span: Span,
+    trait_ref: &hir::TraitRef<'_>,
+    ty: Ty<'tcx>,
+    hash_is_automatically_derived: bool,
+) {
+    if let Some(peq_trait_def_id) = cx.tcx.lang_items().eq_trait()
+        && let Some(def_id) = trait_ref.trait_def_id()
+        && cx.tcx.is_diagnostic_item(sym::Hash, def_id)
+    {
+        // Look for the PartialEq implementations for `ty`
+        cx.tcx.for_each_relevant_impl(peq_trait_def_id, ty, |impl_id| {
+            let peq_is_automatically_derived = cx.tcx.is_automatically_derived(impl_id);
+
+            if !hash_is_automatically_derived || peq_is_automatically_derived {
+                return;
+            }
+
+            let trait_ref = cx.tcx.impl_trait_ref(impl_id).expect("must be a trait implementation");
+
+            // Only care about `impl PartialEq<Foo> for Foo`
+            // For `impl PartialEq<B> for A, input_types is [A, B]
+            if trait_ref.instantiate_identity().args.type_at(1) == ty {
+                span_lint_and_then(
+                    cx,
+                    DERIVED_HASH_WITH_MANUAL_EQ,
+                    span,
+                    "you are deriving `Hash` but have implemented `PartialEq` explicitly",
+                    |diag| {
+                        if let Some(local_def_id) = impl_id.as_local() {
+                            let hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id);
+                            diag.span_note(cx.tcx.hir_span(hir_id), "`PartialEq` implemented here");
+                        }
+                    },
+                );
+            }
+        });
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/derive/expl_impl_clone_on_copy.rs b/src/tools/clippy/clippy_lints/src/derive/expl_impl_clone_on_copy.rs
new file mode 100644
index 00000000000..6b97b4bd6b4
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/derive/expl_impl_clone_on_copy.rs
@@ -0,0 +1,65 @@
+use clippy_utils::diagnostics::span_lint_and_note;
+use clippy_utils::ty::{implements_trait, is_copy};
+use rustc_hir::{self as hir, Item};
+use rustc_lint::LateContext;
+use rustc_middle::ty::{self, GenericArgKind, Ty};
+
+use super::EXPL_IMPL_CLONE_ON_COPY;
+
+/// Implementation of the `EXPL_IMPL_CLONE_ON_COPY` lint.
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) {
+    let clone_id = match cx.tcx.lang_items().clone_trait() {
+        Some(id) if trait_ref.trait_def_id() == Some(id) => id,
+        _ => return,
+    };
+    let Some(copy_id) = cx.tcx.lang_items().copy_trait() else {
+        return;
+    };
+    let (ty_adt, ty_subs) = match *ty.kind() {
+        // Unions can't derive clone.
+        ty::Adt(adt, subs) if !adt.is_union() => (adt, subs),
+        _ => return,
+    };
+    // If the current self type doesn't implement Copy (due to generic constraints), search to see if
+    // 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.local_trait_impls(copy_id).iter().any(|&id| {
+                matches!(cx.tcx.type_of(id).instantiate_identity().kind(), ty::Adt(adt, _)
+                                        if ty_adt.did() == adt.did())
+            });
+            if !has_copy_impl {
+                return;
+            }
+        } else {
+            return;
+        }
+    }
+    // Derive constrains all generic types to requiring Clone. Check if any type is not constrained for
+    // this impl.
+    if ty_subs.types().any(|ty| !implements_trait(cx, ty, clone_id, &[])) {
+        return;
+    }
+    // `#[repr(packed)]` structs with type/const parameters can't derive `Clone`.
+    // https://github.com/rust-lang/rust-clippy/issues/10188
+    if ty_adt.repr().packed()
+        && ty_subs
+            .iter()
+            .any(|arg| matches!(arg.kind(), GenericArgKind::Type(_) | GenericArgKind::Const(_)))
+    {
+        return;
+    }
+    // The presence of `unsafe` fields prevents deriving `Clone` automatically
+    if ty_adt.all_fields().any(|f| f.safety.is_unsafe()) {
+        return;
+    }
+
+    span_lint_and_note(
+        cx,
+        EXPL_IMPL_CLONE_ON_COPY,
+        item.span,
+        "you are implementing `Clone` explicitly on a `Copy` type",
+        Some(item.span),
+        "consider deriving `Clone` or removing `Copy`",
+    );
+}
diff --git a/src/tools/clippy/clippy_lints/src/derive/mod.rs b/src/tools/clippy/clippy_lints/src/derive/mod.rs
new file mode 100644
index 00000000000..1d63394ce37
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/derive/mod.rs
@@ -0,0 +1,215 @@
+use rustc_hir::{Impl, Item, ItemKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::declare_lint_pass;
+
+mod derive_ord_xor_partial_ord;
+mod derive_partial_eq_without_eq;
+mod derived_hash_with_manual_eq;
+mod expl_impl_clone_on_copy;
+mod unsafe_derive_deserialize;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Lints against manual `PartialEq` implementations for types with a derived `Hash`
+    /// implementation.
+    ///
+    /// ### Why is this bad?
+    /// The implementation of these traits must agree (for
+    /// example for use with `HashMap`) so it’s probably a bad idea to use a
+    /// default-generated `Hash` implementation with an explicitly defined
+    /// `PartialEq`. In particular, the following must hold for any type:
+    ///
+    /// ```text
+    /// k1 == k2 ⇒ hash(k1) == hash(k2)
+    /// ```
+    ///
+    /// ### Example
+    /// ```ignore
+    /// #[derive(Hash)]
+    /// struct Foo;
+    ///
+    /// impl PartialEq for Foo {
+    ///     ...
+    /// }
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub DERIVED_HASH_WITH_MANUAL_EQ,
+    correctness,
+    "deriving `Hash` but implementing `PartialEq` explicitly"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Lints against manual `PartialOrd` and `Ord` implementations for types with a derived `Ord`
+    /// or `PartialOrd` implementation.
+    ///
+    /// ### Why is this bad?
+    /// The implementation of these traits must agree (for
+    /// example for use with `sort`) so it’s probably a bad idea to use a
+    /// default-generated `Ord` implementation with an explicitly defined
+    /// `PartialOrd`. In particular, the following must hold for any type
+    /// implementing `Ord`:
+    ///
+    /// ```text
+    /// k1.cmp(&k2) == k1.partial_cmp(&k2).unwrap()
+    /// ```
+    ///
+    /// ### Example
+    /// ```rust,ignore
+    /// #[derive(Ord, PartialEq, Eq)]
+    /// struct Foo;
+    ///
+    /// impl PartialOrd for Foo {
+    ///     ...
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust,ignore
+    /// #[derive(PartialEq, Eq)]
+    /// struct Foo;
+    ///
+    /// impl PartialOrd for Foo {
+    ///     fn partial_cmp(&self, other: &Foo) -> Option<Ordering> {
+    ///        Some(self.cmp(other))
+    ///     }
+    /// }
+    ///
+    /// impl Ord for Foo {
+    ///     ...
+    /// }
+    /// ```
+    /// or, if you don't need a custom ordering:
+    /// ```rust,ignore
+    /// #[derive(Ord, PartialOrd, PartialEq, Eq)]
+    /// struct Foo;
+    /// ```
+    #[clippy::version = "1.47.0"]
+    pub DERIVE_ORD_XOR_PARTIAL_ORD,
+    correctness,
+    "deriving `Ord` but implementing `PartialOrd` explicitly"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for explicit `Clone` implementations for `Copy`
+    /// types.
+    ///
+    /// ### Why is this bad?
+    /// To avoid surprising behavior, these traits should
+    /// agree and the behavior of `Copy` cannot be overridden. In almost all
+    /// situations a `Copy` type should have a `Clone` implementation that does
+    /// nothing more than copy the object, which is what `#[derive(Copy, Clone)]`
+    /// gets you.
+    ///
+    /// ### Example
+    /// ```rust,ignore
+    /// #[derive(Copy)]
+    /// struct Foo;
+    ///
+    /// impl Clone for Foo {
+    ///     // ..
+    /// }
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub EXPL_IMPL_CLONE_ON_COPY,
+    pedantic,
+    "implementing `Clone` explicitly on `Copy` types"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for deriving `serde::Deserialize` on a type that
+    /// has methods using `unsafe`.
+    ///
+    /// ### Why is this bad?
+    /// Deriving `serde::Deserialize` will create a constructor
+    /// that may violate invariants held by another constructor.
+    ///
+    /// ### Example
+    /// ```rust,ignore
+    /// use serde::Deserialize;
+    ///
+    /// #[derive(Deserialize)]
+    /// pub struct Foo {
+    ///     // ..
+    /// }
+    ///
+    /// impl Foo {
+    ///     pub fn new() -> Self {
+    ///         // setup here ..
+    ///     }
+    ///
+    ///     pub unsafe fn parts() -> (&str, &str) {
+    ///         // assumes invariants hold
+    ///     }
+    /// }
+    /// ```
+    #[clippy::version = "1.45.0"]
+    pub UNSAFE_DERIVE_DESERIALIZE,
+    pedantic,
+    "deriving `serde::Deserialize` on a type that has methods using `unsafe`"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for types that derive `PartialEq` and could implement `Eq`.
+    ///
+    /// ### Why is this bad?
+    /// If a type `T` derives `PartialEq` and all of its members implement `Eq`,
+    /// then `T` can always implement `Eq`. Implementing `Eq` allows `T` to be used
+    /// in APIs that require `Eq` types. It also allows structs containing `T` to derive
+    /// `Eq` themselves.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// #[derive(PartialEq)]
+    /// struct Foo {
+    ///     i_am_eq: i32,
+    ///     i_am_eq_too: Vec<String>,
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// #[derive(PartialEq, Eq)]
+    /// struct Foo {
+    ///     i_am_eq: i32,
+    ///     i_am_eq_too: Vec<String>,
+    /// }
+    /// ```
+    #[clippy::version = "1.63.0"]
+    pub DERIVE_PARTIAL_EQ_WITHOUT_EQ,
+    nursery,
+    "deriving `PartialEq` on a type that can implement `Eq`, without implementing `Eq`"
+}
+
+declare_lint_pass!(Derive => [
+    EXPL_IMPL_CLONE_ON_COPY,
+    DERIVED_HASH_WITH_MANUAL_EQ,
+    DERIVE_ORD_XOR_PARTIAL_ORD,
+    UNSAFE_DERIVE_DESERIALIZE,
+    DERIVE_PARTIAL_EQ_WITHOUT_EQ
+]);
+
+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(of_trait),
+            ..
+        }) = item.kind
+        {
+            let trait_ref = &of_trait.trait_ref;
+            let ty = cx.tcx.type_of(item.owner_id).instantiate_identity();
+            let is_automatically_derived = cx.tcx.is_automatically_derived(item.owner_id.to_def_id());
+
+            derived_hash_with_manual_eq::check(cx, item.span, trait_ref, ty, is_automatically_derived);
+            derive_ord_xor_partial_ord::check(cx, item.span, trait_ref, ty, is_automatically_derived);
+
+            if is_automatically_derived {
+                unsafe_derive_deserialize::check(cx, item, trait_ref, ty);
+                derive_partial_eq_without_eq::check(cx, item.span, trait_ref, ty);
+            } else {
+                expl_impl_clone_on_copy::check(cx, item, trait_ref, ty);
+            }
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/derive/unsafe_derive_deserialize.rs b/src/tools/clippy/clippy_lints/src/derive/unsafe_derive_deserialize.rs
new file mode 100644
index 00000000000..c391e7b6228
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/derive/unsafe_derive_deserialize.rs
@@ -0,0 +1,93 @@
+use std::ops::ControlFlow;
+
+use clippy_utils::diagnostics::span_lint_hir_and_then;
+use clippy_utils::{is_lint_allowed, paths};
+use rustc_hir::def_id::LocalDefId;
+use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn, walk_item};
+use rustc_hir::{self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, Item, UnsafeSource};
+use rustc_lint::LateContext;
+use rustc_middle::hir::nested_filter;
+use rustc_middle::ty::{self, Ty};
+use rustc_span::{Span, sym};
+
+use super::UNSAFE_DERIVE_DESERIALIZE;
+
+/// Implementation of the `UNSAFE_DERIVE_DESERIALIZE` lint.
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &hir::TraitRef<'_>, ty: Ty<'tcx>) {
+    fn has_unsafe<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>) -> bool {
+        let mut visitor = UnsafeVisitor { cx };
+        walk_item(&mut visitor, item).is_break()
+    }
+
+    if let Some(trait_def_id) = trait_ref.trait_def_id()
+        && paths::SERDE_DESERIALIZE.matches(cx, trait_def_id)
+        && let ty::Adt(def, _) = ty.kind()
+        && let Some(local_def_id) = def.did().as_local()
+        && let adt_hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id)
+        && !is_lint_allowed(cx, UNSAFE_DERIVE_DESERIALIZE, adt_hir_id)
+        && cx
+            .tcx
+            .inherent_impls(def.did())
+            .iter()
+            .map(|imp_did| cx.tcx.hir_expect_item(imp_did.expect_local()))
+            .any(|imp| has_unsafe(cx, imp))
+    {
+        span_lint_hir_and_then(
+            cx,
+            UNSAFE_DERIVE_DESERIALIZE,
+            adt_hir_id,
+            item.span,
+            "you are deriving `serde::Deserialize` on a type that has methods using `unsafe`",
+            |diag| {
+                diag.help(
+                    "consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html",
+                );
+            },
+        );
+    }
+}
+
+struct UnsafeVisitor<'a, 'tcx> {
+    cx: &'a LateContext<'tcx>,
+}
+
+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,
+    ) -> Self::Result {
+        if let Some(header) = kind.header()
+            && header.is_unsafe()
+        {
+            ControlFlow::Break(())
+        } else {
+            walk_fn(self, kind, decl, body_id, id)
+        }
+    }
+
+    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) -> Self::Result {
+        if let ExprKind::Block(block, _) = expr.kind
+            && block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
+            && block
+                .span
+                .source_callee()
+                .and_then(|expr| expr.macro_def_id)
+                .is_none_or(|did| !self.cx.tcx.is_diagnostic_item(sym::pin_macro, did))
+        {
+            return ControlFlow::Break(());
+        }
+
+        walk_expr(self, expr)
+    }
+
+    fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
+        self.cx.tcx
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/disallowed_macros.rs b/src/tools/clippy/clippy_lints/src/disallowed_macros.rs
index 23e7c7251cf..1c9c971730f 100644
--- a/src/tools/clippy/clippy_lints/src/disallowed_macros.rs
+++ b/src/tools/clippy/clippy_lints/src/disallowed_macros.rs
@@ -1,4 +1,3 @@
-
 use clippy_config::Conf;
 use clippy_config::types::{DisallowedPath, create_disallowed_map};
 use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
@@ -8,7 +7,8 @@ use rustc_data_structures::fx::FxHashSet;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::DefIdMap;
 use rustc_hir::{
-    AmbigArg, Expr, ExprKind, ForeignItem, HirId, ImplItem, Item, ItemKind, OwnerId, Pat, Path, Stmt, TraitItem, Ty,
+    AmbigArg, Expr, ExprKind, ForeignItem, HirId, ImplItem, ImplItemImplKind, Item, ItemKind, OwnerId, Pat, Path, Stmt,
+    TraitItem, Ty,
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::TyCtxt;
@@ -177,7 +177,9 @@ impl LateLintPass<'_> for DisallowedMacros {
 
     fn check_impl_item(&mut self, cx: &LateContext<'_>, item: &ImplItem<'_>) {
         self.check(cx, item.span, None);
-        self.check(cx, item.vis_span, None);
+        if let ImplItemImplKind::Inherent { vis_span, .. } = item.impl_kind {
+            self.check(cx, vis_span, None);
+        }
     }
 
     fn check_trait_item(&mut self, cx: &LateContext<'_>, item: &TraitItem<'_>) {
diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs
index d27d68d3866..eca3bc390d7 100644
--- a/src/tools/clippy/clippy_lints/src/doc/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs
@@ -1139,12 +1139,12 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
                         None,
                         "a backtick may be missing a pair",
                     );
+                    text_to_check.clear();
                 } else {
-                    for (text, range, assoc_code_level) in text_to_check {
+                    for (text, range, assoc_code_level) in text_to_check.drain(..) {
                         markdown::check(cx, valid_idents, &text, &fragments, range, assoc_code_level, blockquote_level);
                     }
                 }
-                text_to_check = Vec::new();
             },
             Start(FootnoteDefinition(..)) => in_footnote_definition = true,
             End(TagEnd::FootnoteDefinition) => in_footnote_definition = false,
diff --git a/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs b/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs
index 74283d7ba86..43bb9723555 100644
--- a/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/needless_doctest_main.rs
@@ -8,6 +8,7 @@ use rustc_ast::{CoroutineKind, Fn, FnRetTy, Item, ItemKind};
 use rustc_errors::emitter::HumanEmitter;
 use rustc_errors::{Diag, DiagCtxt};
 use rustc_lint::LateContext;
+use rustc_parse::lexer::StripTokens;
 use rustc_parse::new_parser_from_source_str;
 use rustc_parse::parser::ForceCollect;
 use rustc_session::parse::ParseSess;
@@ -49,13 +50,14 @@ pub fn check(
                 let sm = Arc::new(SourceMap::new(FilePathMapping::empty()));
                 let psess = ParseSess::with_dcx(dcx, sm);
 
-                let mut parser = match new_parser_from_source_str(&psess, filename, code) {
-                    Ok(p) => p,
-                    Err(errs) => {
-                        errs.into_iter().for_each(Diag::cancel);
-                        return (false, test_attr_spans);
-                    },
-                };
+                let mut parser =
+                    match new_parser_from_source_str(&psess, filename, code, StripTokens::ShebangAndFrontmatter) {
+                        Ok(p) => p,
+                        Err(errs) => {
+                            errs.into_iter().for_each(Diag::cancel);
+                            return (false, test_attr_spans);
+                        },
+                    };
 
                 let mut relevant_main_found = false;
                 let mut eligible = true;
diff --git a/src/tools/clippy/clippy_lints/src/duplicate_mod.rs b/src/tools/clippy/clippy_lints/src/duplicate_mod.rs
index ce551a64d99..759b7b6837b 100644
--- a/src/tools/clippy/clippy_lints/src/duplicate_mod.rs
+++ b/src/tools/clippy/clippy_lints/src/duplicate_mod.rs
@@ -63,7 +63,7 @@ impl_lint_pass!(DuplicateMod => [DUPLICATE_MOD]);
 
 impl EarlyLintPass for DuplicateMod {
     fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
-        if let ItemKind::Mod(_, _, ModKind::Loaded(_, Inline::No, mod_spans, _)) = &item.kind
+        if let ItemKind::Mod(_, _, ModKind::Loaded(_, Inline::No { .. }, mod_spans)) = &item.kind
             && let FileName::Real(real) = cx.sess().source_map().span_to_filename(mod_spans.inner_span)
             && let Some(local_path) = real.into_local_path()
             && let Ok(absolute_path) = local_path.canonicalize()
diff --git a/src/tools/clippy/clippy_lints/src/empty_drop.rs b/src/tools/clippy/clippy_lints/src/empty_drop.rs
index 4e948701da4..2b822188434 100644
--- a/src/tools/clippy/clippy_lints/src/empty_drop.rs
+++ b/src/tools/clippy/clippy_lints/src/empty_drop.rs
@@ -36,11 +36,11 @@ 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(trait_ref),
+            of_trait: Some(of_trait),
             items: [child],
             ..
         }) = item.kind
-            && trait_ref.trait_def_id() == cx.tcx.lang_items().drop_trait()
+            && of_trait.trait_ref.trait_def_id() == cx.tcx.lang_items().drop_trait()
             && let impl_item_hir = child.hir_id()
             && let Node::ImplItem(impl_item) = cx.tcx.hir_node(impl_item_hir)
             && let ImplItemKind::Fn(_, b) = &impl_item.kind
diff --git a/src/tools/clippy/clippy_lints/src/empty_line_after.rs b/src/tools/clippy/clippy_lints/src/empty_line_after.rs
index 3bd74856165..76e67b1154b 100644
--- a/src/tools/clippy/clippy_lints/src/empty_line_after.rs
+++ b/src/tools/clippy/clippy_lints/src/empty_line_after.rs
@@ -442,7 +442,7 @@ impl EmptyLineAfter {
                 None => span.shrink_to_lo(),
             },
             mod_items: match kind {
-                ItemKind::Mod(_, _, ModKind::Loaded(items, _, _, _)) => items
+                ItemKind::Mod(_, _, ModKind::Loaded(items, _, _)) => items
                     .iter()
                     .filter(|i| !matches!(i.span.ctxt().outer_expn_data().kind, ExpnKind::AstPass(_)))
                     .map(|i| i.id)
diff --git a/src/tools/clippy/clippy_lints/src/entry.rs b/src/tools/clippy/clippy_lints/src/entry.rs
index 182cb4e46d2..a5ec6777b43 100644
--- a/src/tools/clippy/clippy_lints/src/entry.rs
+++ b/src/tools/clippy/clippy_lints/src/entry.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
 use clippy_utils::source::{reindent_multiline, snippet_indent, snippet_with_applicability, snippet_with_context};
 use clippy_utils::ty::is_copy;
 use clippy_utils::visitors::for_each_expr;
@@ -194,7 +194,17 @@ impl<'tcx> LateLintPass<'tcx> for HashMapPass {
         if let Some(sugg) = sugg {
             span_lint_and_sugg(cx, MAP_ENTRY, expr.span, lint_msg, "try", sugg, app);
         } else {
-            span_lint(cx, MAP_ENTRY, expr.span, lint_msg);
+            span_lint_and_help(
+                cx,
+                MAP_ENTRY,
+                expr.span,
+                lint_msg,
+                None,
+                format!(
+                    "consider using the `Entry` API: https://doc.rust-lang.org/std/collections/struct.{}.html#entry-api",
+                    map_ty.name()
+                ),
+            );
         }
     }
 }
@@ -254,35 +264,28 @@ fn try_parse_contains<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'_>) -> Optio
         _ => None,
     });
 
-    match expr.kind {
-        ExprKind::MethodCall(
-            _,
+    if let ExprKind::MethodCall(_, map, [arg], _) = expr.kind
+        && let Expr {
+            kind: ExprKind::AddrOf(_, _, key),
+            span: key_span,
+            ..
+        } = arg
+        && key_span.eq_ctxt(expr.span)
+    {
+        let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?;
+        let expr = ContainsExpr {
+            negated,
             map,
-            [
-                Expr {
-                    kind: ExprKind::AddrOf(_, _, key),
-                    span: key_span,
-                    ..
-                },
-            ],
-            _,
-        ) if key_span.eq_ctxt(expr.span) => {
-            let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?;
-            let expr = ContainsExpr {
-                negated,
-                map,
-                key,
-                call_ctxt: expr.span.ctxt(),
-            };
-            if cx.tcx.is_diagnostic_item(sym::btreemap_contains_key, id) {
-                Some((MapType::BTree, expr))
-            } else if cx.tcx.is_diagnostic_item(sym::hashmap_contains_key, id) {
-                Some((MapType::Hash, expr))
-            } else {
-                None
-            }
-        },
-        _ => None,
+            key,
+            call_ctxt: expr.span.ctxt(),
+        };
+        match cx.tcx.get_diagnostic_name(id) {
+            Some(sym::btreemap_contains_key) => Some((MapType::BTree, expr)),
+            Some(sym::hashmap_contains_key) => Some((MapType::Hash, expr)),
+            _ => None,
+        }
+    } else {
+        None
     }
 }
 
@@ -311,7 +314,9 @@ struct InsertExpr<'tcx> {
 fn try_parse_insert<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<InsertExpr<'tcx>> {
     if let ExprKind::MethodCall(_, map, [key, value], _) = expr.kind {
         let id = cx.typeck_results().type_dependent_def_id(expr.hir_id)?;
-        if cx.tcx.is_diagnostic_item(sym::btreemap_insert, id) || cx.tcx.is_diagnostic_item(sym::hashmap_insert, id) {
+        if let Some(insert) = cx.tcx.get_diagnostic_name(id)
+            && matches!(insert, sym::btreemap_insert | sym::hashmap_insert)
+        {
             Some(InsertExpr { map, key, value })
         } else {
             None
diff --git a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs
index 72f5eaf8a4b..c3fc09343db 100644
--- a/src/tools/clippy/clippy_lints/src/equatable_if_let.rs
+++ b/src/tools/clippy/clippy_lints/src/equatable_if_let.rs
@@ -53,7 +53,7 @@ fn unary_pattern(pat: &Pat<'_>) -> bool {
         | PatKind::Never
         | PatKind::Or(_)
         | PatKind::Err(_) => false,
-        PatKind::Struct(_, a, etc) => !etc && a.iter().all(|x| unary_pattern(x.pat)),
+        PatKind::Struct(_, a, etc) => etc.is_none() && a.iter().all(|x| unary_pattern(x.pat)),
         PatKind::Tuple(a, etc) | PatKind::TupleStruct(_, a, etc) => etc.as_opt_usize().is_none() && array_rec(a),
         PatKind::Ref(x, _) | PatKind::Box(x) | PatKind::Deref(x) | PatKind::Guard(x, _) => unary_pattern(x),
         PatKind::Expr(_) => true,
diff --git a/src/tools/clippy/clippy_lints/src/error_impl_error.rs b/src/tools/clippy/clippy_lints/src/error_impl_error.rs
index 6525648efb1..3018e1f1273 100644
--- a/src/tools/clippy/clippy_lints/src/error_impl_error.rs
+++ b/src/tools/clippy/clippy_lints/src/error_impl_error.rs
@@ -52,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for ErrorImplError {
                 );
             },
             ItemKind::Impl(imp)
-                if let Some(trait_def_id) = imp.of_trait.and_then(|t| t.trait_def_id())
+                if let Some(trait_def_id) = imp.of_trait.and_then(|t| t.trait_ref.trait_def_id())
                     && let Some(error_def_id) = cx.tcx.get_diagnostic_item(sym::Error)
                     && error_def_id == trait_def_id
                     && let Some(def_id) = path_res(cx, imp.self_ty).opt_def_id().and_then(DefId::as_local)
diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs
index e467246741c..752f39b4e6d 100644
--- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs
+++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs
@@ -12,6 +12,7 @@ use rustc_hir::attrs::AttributeKind;
 use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, GenericArgs, Param, PatKind, QPath, Safety, TyKind, find_attr};
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::adjustment::Adjust;
 use rustc_middle::ty::{
     self, Binder, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, Ty, TypeVisitableExt, TypeckResults,
 };
@@ -148,10 +149,9 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx
             {
                 return;
             }
-            let callee_ty_adjusted = typeck
-                .expr_adjustments(callee)
-                .last()
-                .map_or(callee_ty, |a| a.target.peel_refs());
+
+            let callee_ty_adjustments = typeck.expr_adjustments(callee);
+            let callee_ty_adjusted = callee_ty_adjustments.last().map_or(callee_ty, |a| a.target);
 
             let sig = match callee_ty_adjusted.kind() {
                 ty::FnDef(def, _) => {
@@ -197,6 +197,18 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx
                 // in a type which is `'static`.
                 // For now ignore all callee types which reference a type parameter.
                 && !generic_args.types().any(|t| matches!(t.kind(), ty::Param(_)))
+                // Rule out `AsyncFn*`s, because while they can be called as `|x| f(x)`,
+                // they can't be passed directly into a place expecting an `Fn*` (#13892)
+                && let Ok((closure_kind, _)) = 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,
+                    )
             {
                 span_lint_hir_and_then(
                     cx,
@@ -213,27 +225,35 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx
                                 // '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 closure_kind {
                                     // 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() => {
+                                    ClosureKind::FnMut => snippet = format!("&mut {snippet}"),
+                                    ClosureKind::Fn if !callee_ty_raw.is_ref() => {
                                         snippet = format!("&{snippet}");
                                     },
                                     _ => (),
                                 }
+                            } else if let n_refs =
+                                callee_ty_adjustments
+                                    .iter()
+                                    .rev()
+                                    .fold(0, |acc, adjustment| match adjustment.kind {
+                                        Adjust::Deref(Some(_)) => acc + 1,
+                                        Adjust::Deref(_) if acc > 0 => acc + 1,
+                                        _ => acc,
+                                    })
+                                && n_refs > 0
+                            {
+                                snippet = format!("{}{snippet}", "*".repeat(n_refs));
                             }
+
+                            let replace_with = match callee_ty_adjusted.kind() {
+                                ty::FnDef(def, _) => cx.tcx.def_descr(*def),
+                                _ => "function",
+                            };
                             diag.span_suggestion(
                                 expr.span,
-                                "replace the closure with the function itself",
+                                format!("replace the closure with the {replace_with} itself"),
                                 snippet,
                                 Applicability::MachineApplicable,
                             );
diff --git a/src/tools/clippy/clippy_lints/src/excessive_nesting.rs b/src/tools/clippy/clippy_lints/src/excessive_nesting.rs
index 1d3ae894944..5368701c304 100644
--- a/src/tools/clippy/clippy_lints/src/excessive_nesting.rs
+++ b/src/tools/clippy/clippy_lints/src/excessive_nesting.rs
@@ -164,7 +164,7 @@ impl Visitor<'_> for NestingVisitor<'_, '_> {
         }
 
         match &item.kind {
-            ItemKind::Trait(_) | ItemKind::Impl(_) | ItemKind::Mod(.., ModKind::Loaded(_, Inline::Yes, _, _)) => {
+            ItemKind::Trait(_) | ItemKind::Impl(_) | ItemKind::Mod(.., ModKind::Loaded(_, Inline::Yes, _)) => {
                 self.nest_level += 1;
 
                 if !self.check_indent(item.span, item.id) {
diff --git a/src/tools/clippy/clippy_lints/src/float_literal.rs b/src/tools/clippy/clippy_lints/src/float_literal.rs
index ccaf38aee4d..6178addfff1 100644
--- a/src/tools/clippy/clippy_lints/src/float_literal.rs
+++ b/src/tools/clippy/clippy_lints/src/float_literal.rs
@@ -1,11 +1,12 @@
+use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::numeric_literal;
+use clippy_utils::{ExprUseNode, expr_use_ctxt, numeric_literal};
 use rustc_ast::ast::{LitFloatType, LitKind};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::{self, FloatTy};
-use rustc_session::declare_lint_pass;
+use rustc_session::impl_lint_pass;
 use std::fmt;
 
 declare_clippy_lint! {
@@ -13,6 +14,8 @@ declare_clippy_lint! {
     /// Checks for float literals with a precision greater
     /// than that supported by the underlying type.
     ///
+    /// The lint is suppressed for literals with over `const_literal_digits_threshold` digits.
+    ///
     /// ### Why is this bad?
     /// Rust will truncate the literal silently.
     ///
@@ -58,7 +61,21 @@ declare_clippy_lint! {
     "lossy whole number float literals"
 }
 
-declare_lint_pass!(FloatLiteral => [EXCESSIVE_PRECISION, LOSSY_FLOAT_LITERAL]);
+pub struct FloatLiteral {
+    const_literal_digits_threshold: usize,
+}
+
+impl_lint_pass!(FloatLiteral => [
+    EXCESSIVE_PRECISION, LOSSY_FLOAT_LITERAL
+]);
+
+impl FloatLiteral {
+    pub fn new(conf: &'static Conf) -> Self {
+        Self {
+            const_literal_digits_threshold: conf.const_literal_digits_threshold,
+        }
+    }
+}
 
 impl<'tcx> LateLintPass<'tcx> for FloatLiteral {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
@@ -126,13 +143,25 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral {
                         },
                     );
                 }
-            } else if digits > max as usize && count_digits(&float_str) < count_digits(sym_str) {
+            } else if digits > max as usize && count_digits(&float_str) < digits {
+                if digits >= self.const_literal_digits_threshold
+                    && matches!(expr_use_ctxt(cx, expr).use_node(cx), ExprUseNode::ConstStatic(_))
+                {
+                    // If a big enough number of digits is specified and it's a constant
+                    // we assume the user is definining a constant, and excessive precision is ok
+                    return;
+                }
                 span_lint_and_then(
                     cx,
                     EXCESSIVE_PRECISION,
                     expr.span,
                     "float has excessive precision",
                     |diag| {
+                        if digits >= self.const_literal_digits_threshold
+                            && let Some(let_stmt) = maybe_let_stmt(cx, expr)
+                        {
+                            diag.span_note(let_stmt.span, "consider making it a `const` item");
+                        }
                         diag.span_suggestion_verbose(
                             expr.span,
                             "consider changing the type or truncating it to",
@@ -196,3 +225,11 @@ impl FloatFormat {
         }
     }
 }
+
+fn maybe_let_stmt<'a>(cx: &LateContext<'a>, expr: &hir::Expr<'_>) -> Option<&'a hir::LetStmt<'a>> {
+    let parent = cx.tcx.parent_hir_node(expr.hir_id);
+    match parent {
+        hir::Node::LetStmt(let_stmt) => Some(let_stmt),
+        _ => None,
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/format_impl.rs b/src/tools/clippy/clippy_lints/src/format_impl.rs
index 0535ecf5240..416aea51ea1 100644
--- a/src/tools/clippy/clippy_lints/src/format_impl.rs
+++ b/src/tools/clippy/clippy_lints/src/format_impl.rs
@@ -254,10 +254,10 @@ fn is_format_trait_impl(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) -> Optio
     if impl_item.ident.name == sym::fmt
         && let ImplItemKind::Fn(_, body_id) = impl_item.kind
         && let Some(Impl {
-            of_trait: Some(trait_ref),
+            of_trait: Some(of_trait),
             ..
         }) = get_parent_as_impl(cx.tcx, impl_item.hir_id())
-        && let Some(did) = trait_ref.trait_def_id()
+        && let Some(did) = of_trait.trait_ref.trait_def_id()
         && let Some(name) = cx.tcx.get_diagnostic_name(did)
         && matches!(name, sym::Debug | sym::Display)
     {
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 1da6952eb64..e3bb5ee10db 100644
--- a/src/tools/clippy/clippy_lints/src/from_over_into.rs
+++ b/src/tools/clippy/clippy_lints/src/from_over_into.rs
@@ -67,12 +67,12 @@ impl_lint_pass!(FromOverInto => [FROM_OVER_INTO]);
 impl<'tcx> LateLintPass<'tcx> for FromOverInto {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
         if let ItemKind::Impl(Impl {
-            of_trait: Some(hir_trait_ref),
+            of_trait: Some(of_trait),
             self_ty,
             items: [impl_item_ref],
             ..
         }) = item.kind
-            && let Some(into_trait_seg) = hir_trait_ref.path.segments.last()
+            && let Some(into_trait_seg) = of_trait.trait_ref.path.segments.last()
             // `impl Into<target_ty> for self_ty`
             && let Some(GenericArgs { args: [GenericArg::Type(target_ty)], .. }) = into_trait_seg.args
             && span_is_local(item.span)
diff --git a/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs b/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs
index b816963cc82..d5873b3f85a 100644
--- a/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs
+++ b/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::sugg::Sugg;
-use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
+use clippy_utils::ty::is_type_lang_item;
 use clippy_utils::{is_in_const_context, is_integer_literal, sym};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, LangItem, PrimTy, QPath, TyKind, def};
@@ -89,5 +89,5 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 {
 
 /// Checks if a Ty is `String` or `&str`
 fn is_ty_stringish(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
-    is_type_lang_item(cx, ty, LangItem::String) || is_type_diagnostic_item(cx, ty, sym::str)
+    is_type_lang_item(cx, ty, LangItem::String) || ty.peel_refs().is_str()
 }
diff --git a/src/tools/clippy/clippy_lints/src/functions/duplicate_underscore_argument.rs b/src/tools/clippy/clippy_lints/src/functions/duplicate_underscore_argument.rs
new file mode 100644
index 00000000000..b15d1b1bb79
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/functions/duplicate_underscore_argument.rs
@@ -0,0 +1,34 @@
+use clippy_utils::diagnostics::span_lint;
+use rustc_ast::PatKind;
+use rustc_ast::visit::FnKind;
+use rustc_data_structures::fx::FxHashMap;
+use rustc_lint::EarlyContext;
+use rustc_span::Span;
+
+use super::DUPLICATE_UNDERSCORE_ARGUMENT;
+
+pub(super) fn check(cx: &EarlyContext<'_>, fn_kind: FnKind<'_>) {
+    let mut registered_names: FxHashMap<String, Span> = FxHashMap::default();
+
+    for arg in &fn_kind.decl().inputs {
+        if let PatKind::Ident(_, ident, None) = arg.pat.kind {
+            let arg_name = ident.to_string();
+
+            if let Some(arg_name) = arg_name.strip_prefix('_') {
+                if let Some(correspondence) = registered_names.get(arg_name) {
+                    span_lint(
+                        cx,
+                        DUPLICATE_UNDERSCORE_ARGUMENT,
+                        *correspondence,
+                        format!(
+                            "`{arg_name}` already exists, having another argument having almost the same \
+                                 name makes code comprehension and documentation more difficult"
+                        ),
+                    );
+                }
+            } else {
+                registered_names.insert(arg_name, arg.pat.span);
+            }
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs b/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs
index cb83b1395d2..3105e303ae3 100644
--- a/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs
@@ -54,8 +54,7 @@ pub(super) fn check_impl_item(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) {
     if let ImplItemKind::Fn(_, body_id) = impl_item.kind
         && let hir::Node::Item(item) = cx.tcx.parent_hir_node(impl_item.hir_id())
         && let hir::ItemKind::Impl(impl_) = item.kind
-        && let hir::Impl { of_trait, .. } = *impl_
-        && of_trait.is_none()
+        && let hir::Impl { of_trait: None, .. } = impl_
         && let body = cx.tcx.hir_body(body_id)
         && cx.tcx.visibility(cx.tcx.hir_body_owner_def_id(body.id())).is_public()
         && !is_in_test(cx.tcx, impl_item.hir_id())
diff --git a/src/tools/clippy/clippy_lints/src/functions/mod.rs b/src/tools/clippy/clippy_lints/src/functions/mod.rs
index 6051dc9479b..5a40af42194 100644
--- a/src/tools/clippy/clippy_lints/src/functions/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/mod.rs
@@ -1,3 +1,4 @@
+mod duplicate_underscore_argument;
 mod impl_trait_in_params;
 mod misnamed_getters;
 mod must_use;
@@ -11,16 +12,40 @@ mod too_many_lines;
 use clippy_config::Conf;
 use clippy_utils::msrvs::Msrv;
 use clippy_utils::paths::{PathNS, lookup_path_str};
+use rustc_ast::{self as ast, visit};
 use rustc_hir as hir;
 use rustc_hir::intravisit;
-use rustc_lint::{LateContext, LateLintPass};
+use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
 use rustc_middle::ty::TyCtxt;
-use rustc_session::impl_lint_pass;
+use rustc_session::{declare_lint_pass, impl_lint_pass};
 use rustc_span::Span;
 use rustc_span::def_id::{DefIdSet, LocalDefId};
 
 declare_clippy_lint! {
     /// ### What it does
+    /// Checks for function arguments having the similar names
+    /// differing by an underscore.
+    ///
+    /// ### Why is this bad?
+    /// It affects code readability.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// fn foo(a: i32, _a: i32) {}
+    /// ```
+    ///
+    /// Use instead:
+    /// ```no_run
+    /// fn bar(a: i32, _b: i32) {}
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub DUPLICATE_UNDERSCORE_ARGUMENT,
+    style,
+    "function arguments having names which only differ by an underscore"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
     /// Checks for functions with too many parameters.
     ///
     /// ### Why is this bad?
@@ -448,6 +473,14 @@ declare_clippy_lint! {
     "function signature uses `&Option<T>` instead of `Option<&T>`"
 }
 
+declare_lint_pass!(EarlyFunctions => [DUPLICATE_UNDERSCORE_ARGUMENT]);
+
+impl EarlyLintPass for EarlyFunctions {
+    fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: visit::FnKind<'_>, _: Span, _: ast::NodeId) {
+        duplicate_underscore_argument::check(cx, fn_kind);
+    }
+}
+
 pub struct Functions {
     too_many_arguments_threshold: u64,
     too_many_lines_threshold: u64,
@@ -502,8 +535,8 @@ impl<'tcx> LateLintPass<'tcx> for Functions {
         def_id: LocalDefId,
     ) {
         let hir_id = cx.tcx.local_def_id_to_hir_id(def_id);
-        too_many_arguments::check_fn(cx, kind, decl, span, hir_id, self.too_many_arguments_threshold);
-        too_many_lines::check_fn(cx, kind, span, body, self.too_many_lines_threshold);
+        too_many_arguments::check_fn(cx, kind, decl, hir_id, def_id, self.too_many_arguments_threshold);
+        too_many_lines::check_fn(cx, kind, body, span, def_id, self.too_many_lines_threshold);
         not_unsafe_ptr_arg_deref::check_fn(cx, kind, decl, body, def_id);
         misnamed_getters::check_fn(cx, kind, decl, body, span);
         impl_trait_in_params::check_fn(cx, &kind, body, hir_id);
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 906bbd006d4..72f87953040 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
@@ -4,7 +4,7 @@ use rustc_middle::ty;
 use rustc_span::def_id::LocalDefId;
 
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::ty::type_is_unsafe_function;
+use clippy_utils::ty::is_unsafe_fn;
 use clippy_utils::visitors::for_each_expr;
 use clippy_utils::{iter_input_pats, path_to_local};
 
@@ -51,7 +51,7 @@ fn check_raw_ptr<'tcx>(
             let typeck = cx.tcx.typeck_body(body.id());
             let _: Option<!> = for_each_expr(cx, body.value, |e| {
                 match e.kind {
-                    hir::ExprKind::Call(f, args) if type_is_unsafe_function(cx, typeck.expr_ty(f)) => {
+                    hir::ExprKind::Call(f, args) if is_unsafe_fn(cx, typeck.expr_ty(f)) => {
                         for arg in args {
                             check_arg(cx, &raw_ptrs, arg);
                         }
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 106202d00d4..5dc1b7269b7 100644
--- a/src/tools/clippy/clippy_lints/src/functions/ref_option.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/ref_option.rs
@@ -1,23 +1,20 @@
 use crate::functions::REF_OPTION;
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::is_trait_impl_item;
 use clippy_utils::source::snippet;
-use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::ty::option_arg_ty;
+use clippy_utils::{is_from_proc_macro, is_trait_impl_item};
 use rustc_errors::Applicability;
-use rustc_hir as hir;
 use rustc_hir::intravisit::FnKind;
-use rustc_hir::{FnDecl, HirId};
-use rustc_lint::LateContext;
-use rustc_middle::ty::{self, GenericArgKind, Mutability, Ty};
+use rustc_hir::{self as hir, FnDecl, HirId};
+use rustc_lint::{LateContext, LintContext};
+use rustc_middle::ty::{self, Mutability, Ty};
+use rustc_span::Span;
 use rustc_span::def_id::LocalDefId;
-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_args) = opt_ty.kind()
-        && let [gen_arg] = opt_gen_args.as_slice()
-        && let GenericArgKind::Type(gen_ty) = gen_arg.kind()
+fn check_ty<'a>(cx: &LateContext<'a>, param: &hir::Ty<'a>, param_ty: Ty<'a>, fixes: &mut Vec<(Span, String)>) {
+    if !param.span.in_external_macro(cx.sess().source_map())
+        && let ty::Ref(_, opt_ty, Mutability::Not) = param_ty.kind()
+        && let Some(gen_ty) = option_arg_ty(cx, *opt_ty)
         && !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
@@ -27,6 +24,7 @@ fn check_ty<'a>(cx: &LateContext<'a>, param: &rustc_hir::Ty<'a>, param_ty: Ty<'a
             args: [hir::GenericArg::Type(opt_ty)],
             ..
         }) = last.args
+        && !is_from_proc_macro(cx, param)
     {
         let lifetime = snippet(cx, lifetime.ident.span, "..");
         fixes.push((
@@ -67,21 +65,24 @@ fn check_fn_sig<'a>(cx: &LateContext<'a>, decl: &FnDecl<'a>, span: Span, sig: ty
 #[allow(clippy::too_many_arguments)]
 pub(crate) fn check_fn<'a>(
     cx: &LateContext<'a>,
-    kind: FnKind<'_>,
+    kind: FnKind<'a>,
     decl: &FnDecl<'a>,
     span: Span,
     hir_id: HirId,
     def_id: LocalDefId,
-    body: &hir::Body<'_>,
+    body: &hir::Body<'a>,
     avoid_breaking_exported_api: bool,
 ) {
     if avoid_breaking_exported_api && cx.effective_visibilities.is_exported(def_id) {
         return;
     }
+    if span.in_external_macro(cx.sess().source_map()) {
+        return;
+    }
 
     if let FnKind::Closure = kind {
         // Compute the span of the closure parameters + return type if set
-        let span = if let hir::FnRetTy::Return(out_ty) = &decl.output {
+        let inputs_output_span = if let hir::FnRetTy::Return(out_ty) = &decl.output {
             if decl.inputs.is_empty() {
                 out_ty.span
             } else {
@@ -100,9 +101,18 @@ pub(crate) fn check_fn<'a>(
         };
         let sig = args.as_closure().sig().skip_binder();
 
-        check_fn_sig(cx, decl, span, sig);
+        if is_from_proc_macro(cx, &(&kind, body, hir_id, span)) {
+            return;
+        }
+
+        check_fn_sig(cx, decl, inputs_output_span, sig);
     } else if !is_trait_impl_item(cx, hir_id) {
         let sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder();
+
+        if is_from_proc_macro(cx, &(&kind, body, hir_id, span)) {
+            return;
+        }
+
         check_fn_sig(cx, decl, span, sig);
     }
 }
@@ -112,8 +122,10 @@ pub(super) fn check_trait_item<'a>(
     trait_item: &hir::TraitItem<'a>,
     avoid_breaking_exported_api: bool,
 ) {
-    if let hir::TraitItemKind::Fn(ref sig, _) = trait_item.kind
+    if !trait_item.span.in_external_macro(cx.sess().source_map())
+        && let hir::TraitItemKind::Fn(ref sig, _) = trait_item.kind
         && !(avoid_breaking_exported_api && cx.effective_visibilities.is_exported(trait_item.owner_id.def_id))
+        && !is_from_proc_macro(cx, trait_item)
     {
         let def_id = trait_item.owner_id.def_id;
         let ty_sig = cx.tcx.fn_sig(def_id).instantiate_identity().skip_binder();
diff --git a/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs b/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs
index 0d6191f2c97..e25611d4881 100644
--- a/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs
@@ -1,11 +1,11 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use rustc_errors::{Applicability, MultiSpan};
-use rustc_hir::def_id::{DefId, DefIdSet};
-use rustc_hir::hir_id::OwnerId;
+use rustc_hir::def_id::DefIdSet;
 use rustc_hir::{Impl, ImplItem, ImplItemKind, ItemKind, Node, TraitRef};
 use rustc_lint::LateContext;
 use rustc_span::Span;
 use rustc_span::symbol::{Ident, kw};
+use std::iter;
 
 use super::RENAMED_FUNCTION_PARAMS;
 
@@ -15,11 +15,11 @@ pub(super) fn check_impl_item(cx: &LateContext<'_>, item: &ImplItem<'_>, ignored
         && let parent_node = cx.tcx.parent_hir_node(item.hir_id())
         && let Node::Item(parent_item) = parent_node
         && let ItemKind::Impl(Impl {
-            of_trait: Some(trait_ref),
+            of_trait: Some(of_trait),
             ..
         }) = &parent_item.kind
-        && let Some(did) = trait_item_def_id_of_impl(cx, item.owner_id)
-        && !is_from_ignored_trait(trait_ref, ignored_traits)
+        && let Some(did) = cx.tcx.trait_item_of(item.owner_id)
+        && !is_from_ignored_trait(&of_trait.trait_ref, ignored_traits)
     {
         let mut param_idents_iter = cx.tcx.hir_body_param_idents(body_id);
         let mut default_param_idents_iter = cx.tcx.fn_arg_idents(did).iter().copied();
@@ -58,16 +58,11 @@ impl RenamedFnArgs {
         let mut renamed: Vec<(Span, String)> = vec![];
 
         debug_assert!(default_idents.size_hint() == current_idents.size_hint());
-        while let (Some(default_ident), Some(current_ident)) = (default_idents.next(), current_idents.next()) {
+        for (default_ident, current_ident) in iter::zip(default_idents, current_idents) {
             let has_name_to_check = |ident: Option<Ident>| {
-                if let Some(ident) = ident
-                    && ident.name != kw::Underscore
-                    && !ident.name.as_str().starts_with('_')
-                {
-                    Some(ident)
-                } else {
-                    None
-                }
+                ident
+                    .filter(|ident| ident.name != kw::Underscore)
+                    .filter(|ident| !ident.name.as_str().starts_with('_'))
             };
 
             if let Some(default_ident) = has_name_to_check(default_ident)
@@ -91,14 +86,8 @@ impl RenamedFnArgs {
     }
 }
 
-/// Get the [`trait_item_def_id`](ImplItemRef::trait_item_def_id) of a relevant impl item.
-fn trait_item_def_id_of_impl(cx: &LateContext<'_>, target: OwnerId) -> Option<DefId> {
-    cx.tcx.associated_item(target).trait_item_def_id
-}
-
 fn is_from_ignored_trait(of_trait: &TraitRef<'_>, ignored_traits: &DefIdSet) -> bool {
-    let Some(trait_did) = of_trait.trait_def_id() else {
-        return false;
-    };
-    ignored_traits.contains(&trait_did)
+    of_trait
+        .trait_def_id()
+        .is_some_and(|trait_did| ignored_traits.contains(&trait_did))
 }
diff --git a/src/tools/clippy/clippy_lints/src/functions/result.rs b/src/tools/clippy/clippy_lints/src/functions/result.rs
index bb98ae82611..1f2fce687ed 100644
--- a/src/tools/clippy/clippy_lints/src/functions/result.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/result.rs
@@ -97,11 +97,7 @@ fn check_result_unit_err(cx: &LateContext<'_>, err_ty: Ty<'_>, fn_header_span: S
 
 fn check_result_large_err<'tcx>(cx: &LateContext<'tcx>, err_ty: Ty<'tcx>, hir_ty_span: Span, large_err_threshold: u64) {
     if let ty::Adt(adt, subst) = err_ty.kind()
-        && let Some(local_def_id) = err_ty
-            .ty_adt_def()
-            .expect("already checked this is adt")
-            .did()
-            .as_local()
+        && let Some(local_def_id) = adt.did().as_local()
         && let hir::Node::Item(item) = cx.tcx.hir_node_by_def_id(local_def_id)
         && let hir::ItemKind::Enum(_, _, ref def) = item.kind
     {
diff --git a/src/tools/clippy/clippy_lints/src/functions/too_many_arguments.rs b/src/tools/clippy/clippy_lints/src/functions/too_many_arguments.rs
index 48d050aa36a..6c3c3d354ec 100644
--- a/src/tools/clippy/clippy_lints/src/functions/too_many_arguments.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/too_many_arguments.rs
@@ -1,5 +1,7 @@
 use rustc_abi::ExternAbi;
-use rustc_hir::{self as hir, intravisit};
+use rustc_hir as hir;
+use rustc_hir::def_id::LocalDefId;
+use rustc_hir::intravisit::FnKind;
 use rustc_lint::LateContext;
 use rustc_span::Span;
 
@@ -10,39 +12,18 @@ use super::TOO_MANY_ARGUMENTS;
 
 pub(super) fn check_fn(
     cx: &LateContext<'_>,
-    kind: intravisit::FnKind<'_>,
+    kind: FnKind<'_>,
     decl: &hir::FnDecl<'_>,
-    span: Span,
     hir_id: hir::HirId,
+    def_id: LocalDefId,
     too_many_arguments_threshold: u64,
 ) {
     // don't warn for implementations, it's not their fault
-    if !is_trait_impl_item(cx, hir_id) {
+    if !is_trait_impl_item(cx, hir_id)
         // don't lint extern functions decls, it's not their fault either
-        match kind {
-            intravisit::FnKind::Method(
-                _,
-                &hir::FnSig {
-                    header: hir::FnHeader {
-                        abi: ExternAbi::Rust, ..
-                    },
-                    ..
-                },
-            )
-            | intravisit::FnKind::ItemFn(
-                _,
-                _,
-                hir::FnHeader {
-                    abi: ExternAbi::Rust, ..
-                },
-            ) => check_arg_number(
-                cx,
-                decl,
-                span.with_hi(decl.output.span().hi()),
-                too_many_arguments_threshold,
-            ),
-            _ => {},
-        }
+        && kind.header().is_some_and(|header| header.abi == ExternAbi::Rust)
+    {
+        check_arg_number(cx, decl, cx.tcx.def_span(def_id), too_many_arguments_threshold);
     }
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs b/src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs
index 4f90d9655b4..33eede8e65a 100644
--- a/src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/too_many_lines.rs
@@ -1,6 +1,7 @@
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::source::SpanRangeExt;
 use rustc_hir as hir;
+use rustc_hir::def_id::LocalDefId;
 use rustc_hir::intravisit::FnKind;
 use rustc_lint::{LateContext, LintContext};
 use rustc_span::Span;
@@ -10,8 +11,9 @@ use super::TOO_MANY_LINES;
 pub(super) fn check_fn(
     cx: &LateContext<'_>,
     kind: FnKind<'_>,
-    span: Span,
     body: &hir::Body<'_>,
+    span: Span,
+    def_id: LocalDefId,
     too_many_lines_threshold: u64,
 ) {
     // Closures must be contained in a parent body, which will be checked for `too_many_lines`.
@@ -74,7 +76,7 @@ pub(super) fn check_fn(
         span_lint(
             cx,
             TOO_MANY_LINES,
-            span,
+            cx.tcx.def_span(def_id),
             format!("this function has too many lines ({line_count}/{too_many_lines_threshold})"),
         );
     }
diff --git a/src/tools/clippy/clippy_lints/src/future_not_send.rs b/src/tools/clippy/clippy_lints/src/future_not_send.rs
index 3ccfa51ab70..596047977a9 100644
--- a/src/tools/clippy/clippy_lints/src/future_not_send.rs
+++ b/src/tools/clippy/clippy_lints/src/future_not_send.rs
@@ -78,66 +78,65 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend {
         if let ty::Alias(ty::Opaque, AliasTy { def_id, args, .. }) = *ret_ty.kind()
             && let Some(future_trait) = cx.tcx.lang_items().future_trait()
             && let Some(send_trait) = cx.tcx.get_diagnostic_item(sym::Send)
+            && let preds = cx.tcx.explicit_item_self_bounds(def_id)
+            // If is a Future
+            && preds
+                .iter_instantiated_copied(cx.tcx, args)
+                .filter_map(|(p, _)| p.as_trait_clause())
+                .any(|trait_pred| trait_pred.skip_binder().trait_ref.def_id == future_trait)
         {
-            let preds = cx.tcx.explicit_item_self_bounds(def_id);
-            let is_future = preds.iter_instantiated_copied(cx.tcx, args).any(|(p, _)| {
-                p.as_trait_clause()
-                    .is_some_and(|trait_pred| trait_pred.skip_binder().trait_ref.def_id == future_trait)
-            });
-            if is_future {
-                let span = decl.output.span();
-                let infcx = cx.tcx.infer_ctxt().build(cx.typing_mode());
-                let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
-                let cause = traits::ObligationCause::misc(span, fn_def_id);
-                ocx.register_bound(cause, cx.param_env, ret_ty, send_trait);
-                let send_errors = ocx.select_all_or_error();
+            let span = decl.output.span();
+            let infcx = cx.tcx.infer_ctxt().build(cx.typing_mode());
+            let ocx = ObligationCtxt::new_with_diagnostics(&infcx);
+            let cause = traits::ObligationCause::misc(span, fn_def_id);
+            ocx.register_bound(cause, cx.param_env, ret_ty, send_trait);
+            let send_errors = ocx.select_all_or_error();
 
-                // Allow errors that try to prove `Send` for types that "mention" a generic parameter at the "top
-                // level".
-                // For example, allow errors that `T: Send` can't be proven, but reject `Rc<T>: Send` errors,
-                // which is always unconditionally `!Send` for any possible type `T`.
-                //
-                // We also allow associated type projections if the self type is either itself a projection or a
-                // type parameter.
-                // This is to prevent emitting warnings for e.g. holding a `<Fut as Future>::Output` across await
-                // points, where `Fut` is a type parameter.
+            // Allow errors that try to prove `Send` for types that "mention" a generic parameter at the "top
+            // level".
+            // For example, allow errors that `T: Send` can't be proven, but reject `Rc<T>: Send` errors,
+            // which is always unconditionally `!Send` for any possible type `T`.
+            //
+            // We also allow associated type projections if the self type is either itself a projection or a
+            // type parameter.
+            // This is to prevent emitting warnings for e.g. holding a `<Fut as Future>::Output` across await
+            // points, where `Fut` is a type parameter.
 
-                let is_send = send_errors.iter().all(|err| {
-                    err.obligation
-                        .predicate
-                        .as_trait_clause()
-                        .map(Binder::skip_binder)
-                        .is_some_and(|pred| {
-                            pred.def_id() == send_trait
-                                && pred.self_ty().has_param()
-                                && TyParamAtTopLevelVisitor.visit_ty(pred.self_ty()) == ControlFlow::Break(true)
-                        })
-                });
+            let is_send = send_errors.iter().all(|err| {
+                err.obligation
+                    .predicate
+                    .as_trait_clause()
+                    .map(Binder::skip_binder)
+                    .is_some_and(|pred| {
+                        pred.def_id() == send_trait
+                            && pred.self_ty().has_param()
+                            && TyParamAtTopLevelVisitor.visit_ty(pred.self_ty()) == ControlFlow::Break(true)
+                    })
+            });
 
-                if !is_send {
-                    span_lint_and_then(
-                        cx,
-                        FUTURE_NOT_SEND,
-                        span,
-                        "future cannot be sent between threads safely",
-                        |db| {
-                            for FulfillmentError { obligation, .. } in send_errors {
-                                infcx
-                                    .err_ctxt()
-                                    .maybe_note_obligation_cause_for_async_await(db, &obligation);
-                                if let PredicateKind::Clause(ClauseKind::Trait(trait_pred)) =
-                                    obligation.predicate.kind().skip_binder()
-                                {
-                                    db.note(format!(
-                                        "`{}` doesn't implement `{}`",
-                                        trait_pred.self_ty(),
-                                        trait_pred.trait_ref.print_only_trait_path(),
-                                    ));
-                                }
+            if !is_send {
+                span_lint_and_then(
+                    cx,
+                    FUTURE_NOT_SEND,
+                    span,
+                    "future cannot be sent between threads safely",
+                    |db| {
+                        for FulfillmentError { obligation, .. } in send_errors {
+                            infcx
+                                .err_ctxt()
+                                .maybe_note_obligation_cause_for_async_await(db, &obligation);
+                            if let PredicateKind::Clause(ClauseKind::Trait(trait_pred)) =
+                                obligation.predicate.kind().skip_binder()
+                            {
+                                db.note(format!(
+                                    "`{}` doesn't implement `{}`",
+                                    trait_pred.self_ty(),
+                                    trait_pred.trait_ref.print_only_trait_path(),
+                                ));
                             }
-                        },
-                    );
-                }
+                        }
+                    },
+                );
             }
         }
     }
diff --git a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs
index 7158f9419c1..b50d91f1014 100644
--- a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs
+++ b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs
@@ -2,16 +2,16 @@ use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::eager_or_lazy::switch_to_eager_eval;
 use clippy_utils::msrvs::{self, Msrv};
-use clippy_utils::source::snippet_with_context;
+use clippy_utils::source::{snippet_with_applicability, snippet_with_context, walk_span_to_context};
 use clippy_utils::sugg::Sugg;
 use clippy_utils::{
     contains_return, expr_adjustment_requires_coercion, higher, is_else_clause, is_in_const_context, is_res_lang_ctor,
-    path_res, peel_blocks,
+    path_res, peel_blocks, sym,
 };
 use rustc_errors::Applicability;
 use rustc_hir::LangItem::{OptionNone, OptionSome};
 use rustc_hir::{Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
 
 declare_clippy_lint! {
@@ -71,21 +71,21 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
             && let ExprKind::Block(then_block, _) = then.kind
             && let Some(then_expr) = then_block.expr
             && let ExprKind::Call(then_call, [then_arg]) = then_expr.kind
-            && let ctxt = expr.span.ctxt()
-            && then_expr.span.ctxt() == ctxt
+            && !expr.span.from_expansion()
+            && !then_expr.span.from_expansion()
             && is_res_lang_ctor(cx, path_res(cx, then_call), OptionSome)
             && is_res_lang_ctor(cx, path_res(cx, peel_blocks(els)), OptionNone)
             && !is_else_clause(cx.tcx, expr)
             && !is_in_const_context(cx)
-            && !expr.span.in_external_macro(cx.sess().source_map())
             && self.msrv.meets(cx, msrvs::BOOL_THEN)
             && !contains_return(then_block.stmts)
         {
             let method_name = if switch_to_eager_eval(cx, expr) && self.msrv.meets(cx, msrvs::BOOL_THEN_SOME) {
-                "then_some"
+                sym::then_some
             } else {
-                "then"
+                sym::then
             };
+            let ctxt = expr.span.ctxt();
 
             span_lint_and_then(
                 cx,
@@ -98,16 +98,18 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
                     }
 
                     let mut app = Applicability::MachineApplicable;
-                    let cond_snip = Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "[condition]", &mut app)
+                    let cond_snip = Sugg::hir_with_context(cx, cond, ctxt, "[condition]", &mut app)
                         .maybe_paren()
                         .to_string();
                     let arg_snip = snippet_with_context(cx, then_arg.span, ctxt, "[body]", &mut app).0;
-                    let method_body = if let Some(first_stmt) = then_block.stmts.first() {
-                        let (block_snippet, _) =
-                            snippet_with_context(cx, first_stmt.span.until(then_arg.span), ctxt, "..", &mut app);
-                        let closure = if method_name == "then" { "|| " } else { "" };
-                        format!("{closure} {{ {block_snippet}; {arg_snip} }}")
-                    } else if method_name == "then" {
+                    let method_body = if let Some(first_stmt) = then_block.stmts.first()
+                        && let Some(first_stmt_span) = walk_span_to_context(first_stmt.span, ctxt)
+                    {
+                        let block_snippet =
+                            snippet_with_applicability(cx, first_stmt_span.until(then_expr.span), "..", &mut app);
+                        let closure = if method_name == sym::then { "|| " } else { "" };
+                        format!("{closure} {{ {} {arg_snip} }}", block_snippet.trim_end())
+                    } else if method_name == sym::then {
                         (std::borrow::Cow::Borrowed("|| ") + arg_snip).into_owned()
                     } else {
                         arg_snip.into_owned()
diff --git a/src/tools/clippy/clippy_lints/src/impl_hash_with_borrow_str_and_bytes.rs b/src/tools/clippy/clippy_lints/src/impl_hash_with_borrow_str_and_bytes.rs
index 940adbae428..f73182d3af0 100644
--- a/src/tools/clippy/clippy_lints/src/impl_hash_with_borrow_str_and_bytes.rs
+++ b/src/tools/clippy/clippy_lints/src/impl_hash_with_borrow_str_and_bytes.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::ty::implements_trait;
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::{Item, ItemKind, Path, TraitRef};
+use rustc_hir::{Item, ItemKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::Ty;
 use rustc_session::declare_lint_pass;
@@ -76,10 +76,10 @@ impl LateLintPass<'_> for ImplHashWithBorrowStrBytes {
     /// three of `Hash`, `Borrow<str>` and `Borrow<[u8]>`.
     fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
         if let ItemKind::Impl(imp) = item.kind
-            && let Some(TraitRef {path: Path {span, res, ..}, ..}) = imp.of_trait
+            && let Some(of_trait) = imp.of_trait
             && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity()
             && let Some(hash_id) = cx.tcx.get_diagnostic_item(sym::Hash)
-            && Res::Def(DefKind::Trait, hash_id) == *res
+            && Res::Def(DefKind::Trait, hash_id) == of_trait.trait_ref.path.res
             && let Some(borrow_id) = cx.tcx.get_diagnostic_item(sym::Borrow)
             // since we are in the `Hash` impl, we don't need to check for that.
             // we need only to check for `Borrow<str>` and `Borrow<[u8]>`
@@ -89,7 +89,7 @@ impl LateLintPass<'_> for ImplHashWithBorrowStrBytes {
             span_lint_and_then(
                 cx,
                 IMPL_HASH_BORROW_WITH_STR_AND_BYTES,
-                *span,
+                of_trait.trait_ref.path.span,
                 "the semantics of `Borrow<T>` around `Hash` can't be satisfied when both `Borrow<str>` and `Borrow<[u8]>` are implemented",
                 |diag| {
                     diag.note("the `Borrow` semantics require that `Hash` must behave the same for all implementations of Borrow<T>");
diff --git a/src/tools/clippy/clippy_lints/src/infallible_try_from.rs b/src/tools/clippy/clippy_lints/src/infallible_try_from.rs
index 589c294a678..36df07a4370 100644
--- a/src/tools/clippy/clippy_lints/src/infallible_try_from.rs
+++ b/src/tools/clippy/clippy_lints/src/infallible_try_from.rs
@@ -45,8 +45,8 @@ declare_lint_pass!(InfallibleTryFrom => [INFALLIBLE_TRY_FROM]);
 impl<'tcx> LateLintPass<'tcx> for InfallibleTryFrom {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
         let ItemKind::Impl(imp) = item.kind else { return };
-        let Some(r#trait) = imp.of_trait else { return };
-        let Some(trait_def_id) = r#trait.trait_def_id() else {
+        let Some(of_trait) = imp.of_trait else { return };
+        let Some(trait_def_id) = of_trait.trait_ref.trait_def_id() else {
             return;
         };
         if !cx.tcx.is_diagnostic_item(sym::TryFrom, trait_def_id) {
diff --git a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs
index 91f65d0b79c..13117f60abd 100644
--- a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs
+++ b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs
@@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_with_context;
 use clippy_utils::sugg::Sugg;
-use clippy_utils::ty;
+use clippy_utils::{is_path_diagnostic_item, ty};
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
@@ -107,8 +107,7 @@ impl LateLintPass<'_> for InstantSubtraction {
 
 fn is_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool {
     if let ExprKind::Call(fn_expr, []) = expr_block.kind
-        && let Some(fn_id) = clippy_utils::path_def_id(cx, fn_expr)
-        && cx.tcx.is_diagnostic_item(sym::instant_now, fn_id)
+        && is_path_diagnostic_item(cx, fn_expr, sym::instant_now)
     {
         true
     } else {
diff --git a/src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs b/src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs
index b0ecc5d52dd..1666e8e5ae3 100644
--- a/src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs
+++ b/src/tools/clippy/clippy_lints/src/invalid_upcast_comparisons.rs
@@ -1,3 +1,4 @@
+use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::layout::LayoutOf;
@@ -9,7 +10,7 @@ use clippy_utils::comparisons;
 use clippy_utils::comparisons::Rel;
 use clippy_utils::consts::{ConstEvalCtxt, FullInt};
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::source::snippet;
+use clippy_utils::source::snippet_with_context;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -69,13 +70,21 @@ fn numeric_cast_precast_bounds(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<
 
 fn err_upcast_comparison(cx: &LateContext<'_>, span: Span, expr: &Expr<'_>, always: bool) {
     if let ExprKind::Cast(cast_val, _) = expr.kind {
+        let mut applicability = Applicability::MachineApplicable;
+        let (cast_val_snip, _) = snippet_with_context(
+            cx,
+            cast_val.span,
+            expr.span.ctxt(),
+            "the expression",
+            &mut applicability,
+        );
         span_lint(
             cx,
             INVALID_UPCAST_COMPARISONS,
             span,
             format!(
                 "because of the numeric bounds on `{}` prior to casting, this expression is always {}",
-                snippet(cx, cast_val.span, "the expression"),
+                cast_val_snip,
                 if always { "true" } else { "false" },
             ),
         );
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 95e16aae40f..945bb84708f 100644
--- a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs
+++ b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs
@@ -8,7 +8,6 @@ use rustc_data_structures::fx::FxHashSet;
 use rustc_hir::{EnumDef, FieldDef, Item, ItemKind, OwnerId, QPath, TyKind, Variant, VariantData};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
-use rustc_span::MacroKind;
 use rustc_span::symbol::Symbol;
 
 declare_clippy_lint! {
@@ -503,8 +502,8 @@ impl LateLintPass<'_> for ItemNameRepetitions {
                 );
             }
 
-            let is_macro_rule = matches!(item.kind, ItemKind::Macro(_, _, MacroKind::Bang));
-            if both_are_public && item_camel.len() > mod_camel.len() && !is_macro_rule {
+            let is_macro = matches!(item.kind, ItemKind::Macro(_, _, _));
+            if both_are_public && item_camel.len() > mod_camel.len() && !is_macro {
                 let matching = count_match_start(mod_camel, &item_camel);
                 let rmatching = count_match_end(mod_camel, &item_camel);
                 let nchars = mod_camel.chars().count();
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 b89f91f7255..645e0f981f2 100644
--- a/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs
+++ b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs
@@ -125,8 +125,9 @@ impl LateLintPass<'_> for IterWithoutIntoIter {
     fn check_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::Item<'_>) {
         if let ItemKind::Impl(imp) = item.kind
             && let TyKind::Ref(_, self_ty_without_ref) = &imp.self_ty.kind
-            && let Some(trait_ref) = imp.of_trait
-            && trait_ref
+            && let Some(of_trait) = imp.of_trait
+            && of_trait
+                .trait_ref
                 .trait_def_id()
                 .is_some_and(|did| cx.tcx.is_diagnostic_item(sym::IntoIterator, did))
             && !item.span.in_external_macro(cx.sess().source_map())
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 8707612fbdd..48ce1afc6e6 100644
--- a/src/tools/clippy/clippy_lints/src/large_include_file.rs
+++ b/src/tools/clippy/clippy_lints/src/large_include_file.rs
@@ -64,8 +64,8 @@ impl LateLintPass<'_> for LargeIncludeFile {
             }
             && len as u64 > self.max_file_size
             && let Some(macro_call) = root_macro_call_first_node(cx, expr)
-            && (cx.tcx.is_diagnostic_item(sym::include_bytes_macro, macro_call.def_id)
-                || cx.tcx.is_diagnostic_item(sym::include_str_macro, macro_call.def_id))
+            && let Some(macro_name) = cx.tcx.get_diagnostic_name(macro_call.def_id)
+            && matches!(macro_name, sym::include_bytes_macro | sym::include_str_macro)
         {
             #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
             span_lint_and_then(
diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs
index 6beddc1be14..28a0fbc0511 100644
--- a/src/tools/clippy/clippy_lints/src/len_zero.rs
+++ b/src/tools/clippy/clippy_lints/src/len_zero.rs
@@ -176,12 +176,11 @@ impl<'tcx> LateLintPass<'tcx> for LenZero {
         if let ExprKind::Let(lt) = expr.kind
             && match lt.pat.kind {
                 PatKind::Slice([], None, []) => true,
-                PatKind::Expr(lit) => match lit.kind {
-                    PatExprKind::Lit { lit, .. } => match lit.node {
-                        LitKind::Str(lit, _) => lit.as_str().is_empty(),
-                        _ => false,
-                    },
-                    _ => false,
+                PatKind::Expr(lit)
+                    if let PatExprKind::Lit { lit, .. } = lit.kind
+                        && let LitKind::Str(lit, _) = lit.node =>
+                {
+                    lit.as_str().is_empty()
                 },
                 _ => false,
             }
@@ -336,42 +335,35 @@ fn extract_future_output<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<&
 }
 
 fn is_first_generic_integral<'tcx>(segment: &'tcx PathSegment<'tcx>) -> bool {
-    if let Some(generic_args) = segment.args {
-        if generic_args.args.is_empty() {
-            return false;
-        }
-        let arg = &generic_args.args[0];
-        if let GenericArg::Type(rustc_hir::Ty {
-            kind: TyKind::Path(QPath::Resolved(_, path)),
-            ..
-        }) = arg
-        {
-            let segments = &path.segments;
-            let segment = &segments[0];
-            let res = &segment.res;
-            if matches!(res, Res::PrimTy(PrimTy::Uint(_))) || matches!(res, Res::PrimTy(PrimTy::Int(_))) {
-                return true;
-            }
-        }
+    if let Some(generic_args) = segment.args
+        && let [GenericArg::Type(ty), ..] = &generic_args.args
+        && let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
+        && let [segment, ..] = &path.segments
+        && matches!(segment.res, Res::PrimTy(PrimTy::Uint(_) | PrimTy::Int(_)))
+    {
+        true
+    } else {
+        false
     }
-
-    false
 }
 
 fn parse_len_output<'tcx>(cx: &LateContext<'tcx>, sig: FnSig<'tcx>) -> Option<LenOutput> {
     if let Some(segment) = extract_future_output(cx, sig.output()) {
         let res = segment.res;
 
-        if matches!(res, Res::PrimTy(PrimTy::Uint(_))) || matches!(res, Res::PrimTy(PrimTy::Int(_))) {
+        if matches!(res, Res::PrimTy(PrimTy::Uint(_) | PrimTy::Int(_))) {
             return Some(LenOutput::Integral);
         }
 
-        if let Res::Def(_, def_id) = res {
-            if cx.tcx.is_diagnostic_item(sym::Option, def_id) && is_first_generic_integral(segment) {
-                return Some(LenOutput::Option(def_id));
-            } else if cx.tcx.is_diagnostic_item(sym::Result, def_id) && is_first_generic_integral(segment) {
-                return Some(LenOutput::Result(def_id));
+        if let Res::Def(_, def_id) = res
+            && let Some(res) = match cx.tcx.get_diagnostic_name(def_id) {
+                Some(sym::Option) => Some(LenOutput::Option(def_id)),
+                Some(sym::Result) => Some(LenOutput::Result(def_id)),
+                _ => None,
             }
+            && is_first_generic_integral(segment)
+        {
+            return Some(res);
         }
 
         return None;
@@ -379,11 +371,10 @@ fn parse_len_output<'tcx>(cx: &LateContext<'tcx>, sig: FnSig<'tcx>) -> Option<Le
 
     match *sig.output().kind() {
         ty::Int(_) | ty::Uint(_) => Some(LenOutput::Integral),
-        ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) => {
-            subs.type_at(0).is_integral().then(|| LenOutput::Option(adt.did()))
-        },
-        ty::Adt(adt, subs) if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) => {
-            subs.type_at(0).is_integral().then(|| LenOutput::Result(adt.did()))
+        ty::Adt(adt, subs) => match cx.tcx.get_diagnostic_name(adt.did()) {
+            Some(sym::Option) => subs.type_at(0).is_integral().then(|| LenOutput::Option(adt.did())),
+            Some(sym::Result) => subs.type_at(0).is_integral().then(|| LenOutput::Result(adt.did())),
+            _ => None,
         },
         _ => None,
     }
diff --git a/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs
index 5b0f95ffc37..24a4c321bda 100644
--- a/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs
+++ b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs
@@ -1,9 +1,9 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_from_proc_macro;
 use clippy_utils::source::{IntoSpan, SpanRangeExt};
+use rustc_ast::{Local, TyKind};
 use rustc_errors::Applicability;
-use rustc_hir::{LetStmt, TyKind};
-use rustc_lint::{LateContext, LateLintPass};
+use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
 use rustc_session::declare_lint_pass;
 
 declare_clippy_lint! {
@@ -26,14 +26,14 @@ declare_clippy_lint! {
 }
 declare_lint_pass!(UnderscoreTyped => [LET_WITH_TYPE_UNDERSCORE]);
 
-impl<'tcx> LateLintPass<'tcx> for UnderscoreTyped {
-    fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'_>) {
-        if let Some(ty) = local.ty // Ensure that it has a type defined
-            && let TyKind::Infer(()) = &ty.kind // that type is '_'
+impl EarlyLintPass for UnderscoreTyped {
+    fn check_local(&mut self, cx: &EarlyContext<'_>, local: &Local) {
+        if let Some(ty) = &local.ty // Ensure that it has a type defined
+            && let TyKind::Infer = ty.kind // that type is '_'
             && local.span.eq_ctxt(ty.span)
-            && let sm = cx.tcx.sess.source_map()
+            && let sm = cx.sess().source_map()
             && !local.span.in_external_macro(sm)
-            && !is_from_proc_macro(cx, ty)
+            && !is_from_proc_macro(cx, &**ty)
         {
             let span_to_remove = sm
                 .span_extend_to_prev_char_before(ty.span, ':', true)
diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs
index 844bc1b0e39..c56fa257b06 100644
--- a/src/tools/clippy/clippy_lints/src/lib.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.rs
@@ -7,7 +7,7 @@
 #![feature(iter_intersperse)]
 #![feature(iter_partition_in_place)]
 #![feature(never_type)]
-#![feature(round_char_boundary)]
+#![cfg_attr(bootstrap, feature(round_char_boundary))]
 #![feature(rustc_private)]
 #![feature(stmt_expr_attributes)]
 #![feature(unwrap_infallible)]
@@ -84,6 +84,7 @@ mod attrs;
 mod await_holding_invalid;
 mod blocks_in_conditions;
 mod bool_assert_comparison;
+mod bool_comparison;
 mod bool_to_int_with_if;
 mod booleans;
 mod borrow_deref_ref;
@@ -301,7 +302,6 @@ mod permissions_set_readonly_false;
 mod pointers_in_nomem_asm_block;
 mod precedence;
 mod ptr;
-mod ptr_offset_with_cast;
 mod pub_underscore_fields;
 mod pub_use;
 mod question_mark;
@@ -359,6 +359,7 @@ mod temporary_assignment;
 mod tests_outside_test_module;
 mod to_digit_is_some;
 mod to_string_trait_impl;
+mod toplevel_ref_arg;
 mod trailing_empty_array;
 mod trait_bounds;
 mod transmute;
@@ -474,10 +475,10 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co
     store.register_late_pass(move |_| Box::new(types::Types::new(conf)));
     store.register_late_pass(move |_| Box::new(booleans::NonminimalBool::new(conf)));
     store.register_late_pass(|_| Box::new(enum_clike::UnportableVariant));
-    store.register_late_pass(|_| Box::new(float_literal::FloatLiteral));
+    store.register_late_pass(move |_| Box::new(float_literal::FloatLiteral::new(conf)));
     store.register_late_pass(|_| Box::new(ptr::Ptr));
     store.register_late_pass(|_| Box::new(needless_bool::NeedlessBool));
-    store.register_late_pass(|_| Box::new(needless_bool::BoolComparison));
+    store.register_late_pass(|_| Box::new(bool_comparison::BoolComparison));
     store.register_late_pass(|_| Box::new(needless_for_each::NeedlessForEach));
     store.register_late_pass(|_| Box::new(misc::LintPass));
     store.register_late_pass(|_| Box::new(eta_reduction::EtaReduction));
@@ -556,6 +557,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co
     store.register_late_pass(|_| Box::new(panicking_overflow_checks::PanickingOverflowChecks));
     store.register_late_pass(|_| Box::<new_without_default::NewWithoutDefault>::default());
     store.register_late_pass(move |_| Box::new(disallowed_names::DisallowedNames::new(conf)));
+    store.register_early_pass(|| Box::new(functions::EarlyFunctions));
     store.register_late_pass(move |tcx| Box::new(functions::Functions::new(tcx, conf)));
     store.register_late_pass(move |_| Box::new(doc::Documentation::new(conf)));
     store.register_early_pass(move || Box::new(doc::Documentation::new(conf)));
@@ -590,7 +592,6 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co
     store.register_late_pass(|_| Box::new(unwrap::Unwrap));
     store.register_late_pass(move |_| Box::new(indexing_slicing::IndexingSlicing::new(conf)));
     store.register_late_pass(move |tcx| Box::new(non_copy_const::NonCopyConst::new(tcx, conf)));
-    store.register_late_pass(|_| Box::new(ptr_offset_with_cast::PtrOffsetWithCast));
     store.register_late_pass(|_| Box::new(redundant_clone::RedundantClone));
     store.register_late_pass(|_| Box::new(slow_vector_initialization::SlowVectorInit));
     store.register_late_pass(move |_| Box::new(unnecessary_wraps::UnnecessaryWraps::new(conf)));
@@ -600,7 +601,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co
     store.register_late_pass(move |_| Box::new(trait_bounds::TraitBounds::new(conf)));
     store.register_late_pass(|_| Box::new(comparison_chain::ComparisonChain));
     store.register_late_pass(move |tcx| Box::new(mut_key::MutableKeyType::new(tcx, conf)));
-    store.register_early_pass(|| Box::new(reference::DerefAddrOf));
+    store.register_late_pass(|_| Box::new(reference::DerefAddrOf));
     store.register_early_pass(|| Box::new(double_parens::DoubleParens));
     let format_args = format_args_storage.clone();
     store.register_late_pass(move |_| Box::new(format_impl::FormatImpl::new(format_args.clone())));
@@ -654,7 +655,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co
     store.register_early_pass(move || Box::new(nonstandard_macro_braces::MacroBraces::new(conf)));
     store.register_late_pass(|_| Box::<macro_use::MacroUseImports>::default());
     store.register_late_pass(|_| Box::new(pattern_type_mismatch::PatternTypeMismatch));
-    store.register_late_pass(|_| Box::new(unwrap_in_result::UnwrapInResult));
+    store.register_late_pass(|_| Box::<unwrap_in_result::UnwrapInResult>::default());
     store.register_late_pass(|_| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned));
     store.register_late_pass(|_| Box::new(async_yields_async::AsyncYieldsAsync));
     let attrs = attr_storage.clone();
@@ -742,7 +743,7 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co
     store.register_late_pass(|_| Box::new(missing_assert_message::MissingAssertMessage));
     store.register_late_pass(|_| Box::new(needless_maybe_sized::NeedlessMaybeSized));
     store.register_late_pass(|_| Box::new(redundant_async_block::RedundantAsyncBlock));
-    store.register_late_pass(|_| Box::new(let_with_type_underscore::UnderscoreTyped));
+    store.register_early_pass(|| Box::new(let_with_type_underscore::UnderscoreTyped));
     store.register_late_pass(move |_| Box::new(manual_main_separator_str::ManualMainSeparatorStr::new(conf)));
     store.register_late_pass(|_| Box::new(unnecessary_struct_initialization::UnnecessaryStruct));
     store.register_late_pass(move |_| Box::new(unnecessary_box_returns::UnnecessaryBoxReturns::new(conf)));
@@ -829,5 +830,6 @@ pub fn register_lint_passes(store: &mut rustc_lint::LintStore, conf: &'static Co
     store.register_late_pass(|_| Box::new(cloned_ref_to_slice_refs::ClonedRefToSliceRefs::new(conf)));
     store.register_late_pass(|_| Box::new(infallible_try_from::InfallibleTryFrom));
     store.register_late_pass(|_| Box::new(coerce_container_to_any::CoerceContainerToAny));
+    store.register_late_pass(|_| Box::new(toplevel_ref_arg::ToplevelRefArg));
     // 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 35c9d2fd4eb..d8b186b6787 100644
--- a/src/tools/clippy/clippy_lints/src/lifetimes.rs
+++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs
@@ -150,7 +150,7 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
         } = item.kind
         {
             check_fn_inner(cx, sig, Some(id), None, generics, item.span, true, self.msrv);
-        } else if let ItemKind::Impl(impl_) = item.kind
+        } else if let ItemKind::Impl(impl_) = &item.kind
             && !item.span.from_expansion()
         {
             report_extra_impl_lifetimes(cx, impl_);
@@ -712,8 +712,8 @@ fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<'
     let mut checker = LifetimeChecker::<middle_nested_filter::All>::new(cx, impl_.generics);
 
     walk_generics(&mut checker, impl_.generics);
-    if let Some(ref trait_ref) = impl_.of_trait {
-        walk_trait_ref(&mut checker, trait_ref);
+    if let Some(of_trait) = impl_.of_trait {
+        walk_trait_ref(&mut checker, &of_trait.trait_ref);
     }
     walk_unambig_ty(&mut checker, impl_.self_ty);
     for &item in impl_.items {
@@ -745,7 +745,7 @@ fn report_elidable_impl_lifetimes<'tcx>(
     impl_: &'tcx Impl<'_>,
     map: &FxIndexMap<LocalDefId, Vec<Usage>>,
 ) {
-    let single_usages = map
+    let (elidable_lts, usages): (Vec<_>, Vec<_>) = map
         .iter()
         .filter_map(|(def_id, usages)| {
             if let [
@@ -762,14 +762,12 @@ fn report_elidable_impl_lifetimes<'tcx>(
                 None
             }
         })
-        .collect::<Vec<_>>();
+        .unzip();
 
-    if single_usages.is_empty() {
+    if elidable_lts.is_empty() {
         return;
     }
 
-    let (elidable_lts, usages): (Vec<_>, Vec<_>) = single_usages.into_iter().unzip();
-
     report_elidable_lifetimes(cx, impl_.generics, &elidable_lts, &usages, true);
 }
 
@@ -795,9 +793,7 @@ fn report_elidable_lifetimes(
         // In principle, the result of the call to `Node::ident` could be `unwrap`ped, as `DefId` should refer to a
         // `Node::GenericParam`.
         .filter_map(|&def_id| cx.tcx.hir_node_by_def_id(def_id).ident())
-        .map(|ident| ident.to_string())
-        .collect::<Vec<_>>()
-        .join(", ");
+        .format(", ");
 
     let elidable_usages: Vec<ElidableUsage> = usages
         .iter()
@@ -860,36 +856,89 @@ fn elision_suggestions(
         .filter(|param| !param.is_elided_lifetime() && !param.is_impl_trait())
         .collect::<Vec<_>>();
 
-    let mut suggestions = if elidable_lts.len() == explicit_params.len() {
+    if !elidable_lts
+        .iter()
+        .all(|lt| explicit_params.iter().any(|param| param.def_id == *lt))
+    {
+        return None;
+    }
+
+    let mut suggestions = if elidable_lts.is_empty() {
+        vec![]
+    } else if elidable_lts.len() == explicit_params.len() {
         // if all the params are elided remove the whole generic block
         //
         // fn x<'a>() {}
         //     ^^^^
         vec![(generics.span, String::new())]
     } else {
-        elidable_lts
-            .iter()
-            .map(|&id| {
-                let pos = explicit_params.iter().position(|param| param.def_id == id)?;
-                let param = explicit_params.get(pos)?;
-
-                let span = if let Some(next) = explicit_params.get(pos + 1) {
-                    // fn x<'prev, 'a, 'next>() {}
-                    //             ^^^^
-                    param.span.until(next.span)
+        match &explicit_params[..] {
+            // no params, nothing to elide
+            [] => unreachable!("handled by `elidable_lts.is_empty()`"),
+            [param] => {
+                if elidable_lts.contains(&param.def_id) {
+                    unreachable!("handled by `elidable_lts.len() == explicit_params.len()`")
                 } else {
-                    // `pos` should be at least 1 here, because the param in position 0 would either have a `next`
-                    // param or would have taken the `elidable_lts.len() == explicit_params.len()` branch.
-                    let prev = explicit_params.get(pos - 1)?;
-
-                    // fn x<'prev, 'a>() {}
-                    //           ^^^^
-                    param.span.with_lo(prev.span.hi())
+                    unreachable!("handled by `elidable_lts.is_empty()`")
+                }
+            },
+            [_, _, ..] => {
+                // Given a list like `<'a, 'b, 'c, 'd, ..>`,
+                //
+                // If there is a cluster of elidable lifetimes at the beginning, say `'a` and `'b`, we should
+                // suggest removing them _and_ the trailing comma. The span for that is `a.span.until(c.span)`:
+                // <'a, 'b, 'c, 'd, ..> => <'a, 'b, 'c, 'd, ..>
+                //  ^^  ^^                  ^^^^^^^^
+                //
+                // And since we know that `'c` isn't elidable--otherwise it would've been in the cluster--we can go
+                // over all the lifetimes after it, and for each elidable one, add a suggestion spanning the
+                // lifetime itself and the comma before, because each individual suggestion is guaranteed to leave
+                // the list valid:
+                // <.., 'c, 'd, 'e, 'f, 'g, ..> => <.., 'c, 'd, 'e, 'f, 'g, ..>
+                //          ^^      ^^  ^^                ^^^^    ^^^^^^^^
+                //
+                // In case there is no such starting cluster, we only need to do the second part of the algorithm:
+                // <'a, 'b, 'c, 'd, 'e, 'f, 'g, ..> => <'a, 'b , 'c, 'd, 'e, 'f, 'g, ..>
+                //      ^^  ^^      ^^  ^^                ^^^^^^^^^    ^^^^^^^^
+
+                // Split off the starting cluster
+                // TODO: use `slice::split_once` once stabilized (github.com/rust-lang/rust/issues/112811):
+                // ```
+                // let Some(split) = explicit_params.split_once(|param| !elidable_lts.contains(&param.def_id)) else {
+                //     // there were no lifetime param that couldn't be elided
+                //     unreachable!("handled by `elidable_lts.len() == explicit_params.len()`")
+                // };
+                // match split { /* .. */ }
+                // ```
+                let Some(split_pos) = explicit_params
+                    .iter()
+                    .position(|param| !elidable_lts.contains(&param.def_id))
+                else {
+                    // there were no lifetime param that couldn't be elided
+                    unreachable!("handled by `elidable_lts.len() == explicit_params.len()`")
                 };
-
-                Some((span, String::new()))
-            })
-            .collect::<Option<Vec<_>>>()?
+                let split = explicit_params
+                    .split_at_checked(split_pos)
+                    .expect("got `split_pos` from `position` on the same Vec");
+
+                match split {
+                    ([..], []) => unreachable!("handled by `elidable_lts.len() == explicit_params.len()`"),
+                    ([], [_]) => unreachable!("handled by `explicit_params.len() == 1`"),
+                    (cluster, rest @ [rest_first, ..]) => {
+                        // the span for the cluster
+                        (cluster.first().map(|fw| fw.span.until(rest_first.span)).into_iter())
+                            // the span for the remaining lifetimes (calculations independent of the cluster)
+                            .chain(
+                                rest.array_windows()
+                                    .filter(|[_, curr]| elidable_lts.contains(&curr.def_id))
+                                    .map(|[prev, curr]| curr.span.with_lo(prev.span.hi())),
+                            )
+                            .map(|sp| (sp, String::new()))
+                            .collect()
+                    },
+                }
+            },
+        }
     };
 
     suggestions.extend(usages.iter().map(|&usage| {
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 010652e1cb9..af475c40586 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
@@ -138,9 +138,9 @@ fn is_ref_iterable<'tcx>(
             return Some((AdjustKind::None, self_ty));
         }
 
-        let res_ty = cx
-            .tcx
-            .erase_regions(EarlyBinder::bind(req_res_ty).instantiate(cx.tcx, typeck.node_args(call_expr.hir_id)));
+        let res_ty = cx.tcx.erase_and_anonymize_regions(
+            EarlyBinder::bind(req_res_ty).instantiate(cx.tcx, typeck.node_args(call_expr.hir_id)),
+        );
         let mutbl = if let ty::Ref(_, _, mutbl) = *req_self_ty.kind() {
             Some(mutbl)
         } else {
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 797ff1f3986..74c0b178018 100644
--- a/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs
@@ -1,10 +1,12 @@
 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, TyKind};
 use rustc_ast::Label;
 use rustc_errors::Applicability;
-use rustc_hir as hir;
+use rustc_hir::{
+    self as hir, Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, FnRetTy,
+    FnSig, Node, TyKind,
+};
 use rustc_lint::{LateContext, LintContext};
 use rustc_span::sym;
 
@@ -29,6 +31,10 @@ pub(super) fn check<'tcx>(
         return;
     }
 
+    if is_inside_unawaited_async_block(cx, expr) {
+        return;
+    }
+
     if expr.span.in_external_macro(cx.sess().source_map()) || is_from_proc_macro(cx, expr) {
         return;
     }
@@ -60,6 +66,43 @@ pub(super) fn check<'tcx>(
     }
 }
 
+/// Check if the given expression is inside an async block that is not being awaited.
+/// This helps avoid false positives when async blocks are spawned or assigned to variables.
+fn is_inside_unawaited_async_block(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    let current_hir_id = expr.hir_id;
+    for (_, parent_node) in cx.tcx.hir_parent_iter(current_hir_id) {
+        if let Node::Expr(Expr {
+            kind:
+                ExprKind::Closure(Closure {
+                    kind:
+                        ClosureKind::Coroutine(CoroutineKind::Desugared(
+                            CoroutineDesugaring::Async,
+                            CoroutineSource::Block | CoroutineSource::Closure,
+                        )),
+                    ..
+                }),
+            ..
+        }) = parent_node
+        {
+            return !is_async_block_awaited(cx, expr);
+        }
+    }
+    false
+}
+
+fn is_async_block_awaited(cx: &LateContext<'_>, async_expr: &Expr<'_>) -> bool {
+    for (_, parent_node) in cx.tcx.hir_parent_iter(async_expr.hir_id) {
+        if let Node::Expr(Expr {
+            kind: ExprKind::Match(_, _, hir::MatchSource::AwaitDesugar),
+            ..
+        }) = parent_node
+        {
+            return true;
+        }
+    }
+    false
+}
+
 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 {
@@ -67,8 +110,8 @@ fn get_parent_fn_ret_ty<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option
             // 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(_),
+                    ExprKind::Closure(Closure {
+                        kind: ClosureKind::Coroutine(_),
                         ..
                     }),
                 ..
@@ -90,7 +133,7 @@ fn get_parent_fn_ret_ty<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option
                 ..
             })
             | Node::Expr(Expr {
-                kind: ExprKind::Closure(hir::Closure { fn_decl: decl, .. }),
+                kind: ExprKind::Closure(Closure { fn_decl: decl, .. }),
                 ..
             }) => return Some(decl.output),
             _ => (),
diff --git a/src/tools/clippy/clippy_lints/src/loops/missing_spin_loop.rs b/src/tools/clippy/clippy_lints/src/loops/missing_spin_loop.rs
index a9944d64ce2..8a2d0036203 100644
--- a/src/tools/clippy/clippy_lints/src/loops/missing_spin_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/missing_spin_loop.rs
@@ -1,10 +1,10 @@
 use super::MISSING_SPIN_LOOP;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::std_or_core;
+use clippy_utils::ty::is_type_diagnostic_item;
 use rustc_errors::Applicability;
 use rustc_hir::{Block, Expr, ExprKind};
 use rustc_lint::LateContext;
-use rustc_middle::ty;
 use rustc_span::sym;
 
 fn unpack_cond<'tcx>(cond: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> {
@@ -39,8 +39,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, body: &'
     ) = body.kind
         && let ExprKind::MethodCall(method, callee, ..) = unpack_cond(cond).kind
         && [sym::load, sym::compare_exchange, sym::compare_exchange_weak].contains(&method.ident.name)
-        && let ty::Adt(def, _args) = cx.typeck_results().expr_ty(callee).kind()
-        && cx.tcx.is_diagnostic_item(sym::AtomicBool, def.did())
+        && let callee_ty = cx.typeck_results().expr_ty(callee)
+        && is_type_diagnostic_item(cx, callee_ty, sym::AtomicBool)
         && let Some(std_or_core) = std_or_core(cx)
     {
         span_lint_and_sugg(
diff --git a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs
index 7bb684d65bb..11edb929d70 100644
--- a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs
@@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet;
 use clippy_utils::ty::has_iter_method;
 use clippy_utils::visitors::is_local_used;
-use clippy_utils::{SpanlessEq, contains_name, higher, is_integer_const, sugg};
+use clippy_utils::{SpanlessEq, contains_name, higher, is_integer_const, peel_hir_expr_while, sugg};
 use rustc_ast::ast;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
 use rustc_errors::Applicability;
@@ -253,12 +253,38 @@ struct VarVisitor<'a, 'tcx> {
 
 impl<'tcx> VarVisitor<'_, 'tcx> {
     fn check(&mut self, idx: &'tcx Expr<'_>, seqexpr: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) -> bool {
-        let index_used_directly = matches!(idx.kind, ExprKind::Path(_));
+        let mut used_cnt = 0;
+        // It is `true` if all indices are direct
+        let mut index_used_directly = true;
+
+        // Handle initial index
+        if is_local_used(self.cx, idx, self.var) {
+            used_cnt += 1;
+            index_used_directly &= matches!(idx.kind, ExprKind::Path(_));
+        }
+        // Handle nested indices
+        let seqexpr = peel_hir_expr_while(seqexpr, |e| {
+            if let ExprKind::Index(e, idx, _) = e.kind {
+                if is_local_used(self.cx, idx, self.var) {
+                    used_cnt += 1;
+                    index_used_directly &= matches!(idx.kind, ExprKind::Path(_));
+                }
+                Some(e)
+            } else {
+                None
+            }
+        });
+
+        match used_cnt {
+            0 => return true,
+            n if n > 1 => self.nonindex = true, // Optimize code like `a[i][i]`
+            _ => {},
+        }
+
         if let ExprKind::Path(ref seqpath) = seqexpr.kind
             // the indexed container is referenced by a name
             && let QPath::Resolved(None, seqvar) = *seqpath
             && seqvar.segments.len() == 1
-            && is_local_used(self.cx, idx, self.var)
         {
             if self.prefer_mutable {
                 self.indexed_mut.insert(seqvar.segments[0].ident.name);
@@ -312,7 +338,6 @@ impl<'tcx> VarVisitor<'_, 'tcx> {
 impl<'tcx> Visitor<'tcx> for VarVisitor<'_, 'tcx> {
     fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
         if let ExprKind::MethodCall(meth, args_0, [args_1, ..], _) = &expr.kind
-            // a range index op
             && let Some(trait_id) = self
                 .cx
                 .typeck_results()
diff --git a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs
index 2ccff768097..544c3c34d02 100644
--- a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs
@@ -22,7 +22,10 @@ pub(super) fn check<'tcx>(
     for_loop: Option<&ForLoop<'_>>,
 ) {
     match never_loop_block(cx, block, &mut Vec::new(), loop_id) {
-        NeverLoopResult::Diverging { ref break_spans } => {
+        NeverLoopResult::Diverging {
+            ref break_spans,
+            ref never_spans,
+        } => {
             span_lint_and_then(cx, NEVER_LOOP, span, "this loop never actually loops", |diag| {
                 if let Some(ForLoop {
                     arg: iterator,
@@ -34,12 +37,16 @@ pub(super) fn check<'tcx>(
                 {
                     // If the block contains a break or continue, or if the loop has a label, `MachineApplicable` is not
                     // appropriate.
-                    let app = if !contains_any_break_or_continue(block) && label.is_none() {
+                    let mut app = if !contains_any_break_or_continue(block) && label.is_none() {
                         Applicability::MachineApplicable
                     } else {
                         Applicability::Unspecified
                     };
 
+                    if !never_spans.is_empty() {
+                        app = Applicability::HasPlaceholders;
+                    }
+
                     let mut suggestions = vec![(
                         for_span.with_hi(iterator.span.hi()),
                         for_to_if_let_sugg(cx, iterator, pat),
@@ -51,6 +58,13 @@ pub(super) fn check<'tcx>(
                         suggestions,
                         app,
                     );
+
+                    for span in never_spans {
+                        diag.span_help(
+                            *span,
+                            "this code is unreachable. Consider moving the reachable parts out",
+                        );
+                    }
                 }
             });
         },
@@ -77,13 +91,16 @@ fn contains_any_break_or_continue(block: &Block<'_>) -> bool {
 /// The first two bits of information are in this enum, and the last part is in the
 /// `local_labels` variable, which contains a list of `(block_id, reachable)` pairs ordered by
 /// scope.
-#[derive(Clone)]
+#[derive(Clone, Debug)]
 enum NeverLoopResult {
     /// A continue may occur for the main loop.
     MayContinueMainLoop,
     /// We have not encountered any main loop continue,
     /// but we are diverging (subsequent control flow is not reachable)
-    Diverging { break_spans: Vec<Span> },
+    Diverging {
+        break_spans: Vec<Span>,
+        never_spans: Vec<Span>,
+    },
     /// We have not encountered any main loop continue,
     /// and subsequent control flow is (possibly) reachable
     Normal,
@@ -128,14 +145,18 @@ fn combine_branches(b1: NeverLoopResult, b2: NeverLoopResult) -> NeverLoopResult
         (
             NeverLoopResult::Diverging {
                 break_spans: mut break_spans1,
+                never_spans: mut never_spans1,
             },
             NeverLoopResult::Diverging {
                 break_spans: mut break_spans2,
+                never_spans: mut never_spans2,
             },
         ) => {
             break_spans1.append(&mut break_spans2);
+            never_spans1.append(&mut never_spans2);
             NeverLoopResult::Diverging {
                 break_spans: break_spans1,
+                never_spans: never_spans1,
             }
         },
     }
@@ -207,6 +228,8 @@ fn all_spans_after_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> Vec<Span> {
         }
 
         return vec![stmt.span];
+    } else if let Node::Block(_) = cx.tcx.parent_hir_node(expr.hir_id) {
+        return vec![expr.span];
     }
 
     vec![]
@@ -270,10 +293,13 @@ fn never_loop_expr<'tcx>(
         ExprKind::Match(e, arms, _) => {
             let e = never_loop_expr(cx, e, local_labels, main_loop_id);
             combine_seq(e, || {
-                arms.iter()
-                    .fold(NeverLoopResult::Diverging { break_spans: vec![] }, |a, b| {
-                        combine_branches(a, never_loop_expr(cx, b.body, local_labels, main_loop_id))
-                    })
+                arms.iter().fold(
+                    NeverLoopResult::Diverging {
+                        break_spans: vec![],
+                        never_spans: vec![],
+                    },
+                    |a, b| combine_branches(a, never_loop_expr(cx, b.body, local_labels, main_loop_id)),
+                )
             })
         },
         ExprKind::Block(b, _) => {
@@ -296,6 +322,7 @@ fn never_loop_expr<'tcx>(
             } else {
                 NeverLoopResult::Diverging {
                     break_spans: all_spans_after_expr(cx, expr),
+                    never_spans: vec![],
                 }
             }
         },
@@ -306,7 +333,10 @@ fn never_loop_expr<'tcx>(
             combine_seq(first, || {
                 // checks if break targets a block instead of a loop
                 mark_block_as_reachable(expr, local_labels);
-                NeverLoopResult::Diverging { break_spans: vec![] }
+                NeverLoopResult::Diverging {
+                    break_spans: vec![],
+                    never_spans: vec![],
+                }
             })
         },
         ExprKind::Break(dest, e) => {
@@ -322,11 +352,15 @@ fn never_loop_expr<'tcx>(
                     } else {
                         all_spans_after_expr(cx, expr)
                     },
+                    never_spans: vec![],
                 }
             })
         },
         ExprKind::Become(e) => combine_seq(never_loop_expr(cx, e, local_labels, main_loop_id), || {
-            NeverLoopResult::Diverging { break_spans: vec![] }
+            NeverLoopResult::Diverging {
+                break_spans: vec![],
+                never_spans: vec![],
+            }
         }),
         ExprKind::InlineAsm(asm) => combine_seq_many(asm.operands.iter().map(|(o, _)| match o {
             InlineAsmOperand::In { expr, .. } | InlineAsmOperand::InOut { expr, .. } => {
@@ -356,7 +390,10 @@ fn never_loop_expr<'tcx>(
     };
     let result = combine_seq(result, || {
         if cx.typeck_results().expr_ty(expr).is_never() {
-            NeverLoopResult::Diverging { break_spans: vec![] }
+            NeverLoopResult::Diverging {
+                break_spans: vec![],
+                never_spans: all_spans_after_expr(cx, expr),
+            }
         } else {
             NeverLoopResult::Normal
         }
diff --git a/src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs b/src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs
index 51e21aa9734..13b93d2c009 100644
--- a/src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/unused_enumerate_index.rs
@@ -1,12 +1,12 @@
 use super::UNUSED_ENUMERATE_INDEX;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet;
+use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::{pat_is_wild, sugg};
 use rustc_errors::Applicability;
 use rustc_hir::def::DefKind;
 use rustc_hir::{Expr, ExprKind, Pat, PatKind};
 use rustc_lint::LateContext;
-use rustc_middle::ty;
 use rustc_span::sym;
 
 /// Checks for the `UNUSED_ENUMERATE_INDEX` lint.
@@ -17,8 +17,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>, arg: &Expr<'_
         && let ExprKind::MethodCall(_method, self_arg, [], _) = arg.kind
         && let ty = cx.typeck_results().expr_ty(arg)
         && pat_is_wild(cx, &index.kind, body)
-        && let ty::Adt(base, _) = *ty.kind()
-        && cx.tcx.is_diagnostic_item(sym::Enumerate, base.did())
+        && is_type_diagnostic_item(cx, ty, sym::Enumerate)
         && let Some((DefKind::AssocFn, call_id)) = cx.typeck_results().type_dependent_def(arg.hir_id)
         && cx.tcx.is_diagnostic_item(sym::enumerate_method, call_id)
     {
diff --git a/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs b/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs
index 9071c9c95f9..c5acaf09993 100644
--- a/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs
+++ b/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs
@@ -5,10 +5,12 @@ use itertools::Itertools;
 use rustc_hir::def_id::LocalDefId;
 use rustc_hir::intravisit::{Visitor, walk_block, walk_expr, walk_stmt};
 use rustc_hir::{BlockCheckMode, Expr, ExprKind, HirId, Stmt, UnsafeSource};
+use rustc_hir::attrs::AttributeKind;
+use rustc_hir::find_attr;
 use rustc_lint::{LateContext, LateLintPass, Level, LintContext};
 use rustc_middle::lint::LevelAndSource;
 use rustc_session::impl_lint_pass;
-use rustc_span::{Span, SyntaxContext, sym};
+use rustc_span::{Span, SyntaxContext};
 use std::collections::BTreeMap;
 use std::collections::btree_map::Entry;
 
@@ -146,7 +148,8 @@ struct BodyVisitor<'a, 'tcx> {
 }
 
 fn is_public_macro(cx: &LateContext<'_>, def_id: LocalDefId) -> bool {
-    (cx.effective_visibilities.is_exported(def_id) || cx.tcx.has_attr(def_id, sym::macro_export))
+    ( cx.effective_visibilities.is_exported(def_id) ||
+        find_attr!(cx.tcx.get_all_attrs(def_id), AttributeKind::MacroExport{..}) )
         && !cx.tcx.is_doc_hidden(def_id)
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/manual_abs_diff.rs b/src/tools/clippy/clippy_lints/src/manual_abs_diff.rs
index 288f27db8ca..5814b6815a1 100644
--- a/src/tools/clippy/clippy_lints/src/manual_abs_diff.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_abs_diff.rs
@@ -4,8 +4,8 @@ use clippy_utils::higher::If;
 use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::HasSession as _;
 use clippy_utils::sugg::Sugg;
-use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{eq_expr_value, peel_blocks, peel_middle_ty_refs, span_contains_comment};
+use clippy_utils::ty::{is_type_diagnostic_item, peel_and_count_ty_refs};
+use clippy_utils::{eq_expr_value, peel_blocks, span_contains_comment};
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
@@ -107,7 +107,7 @@ impl ManualAbsDiff {
             |ty| is_type_diagnostic_item(cx, ty, sym::Duration) && self.msrv.meets(cx, msrvs::DURATION_ABS_DIFF);
 
         let a_ty = cx.typeck_results().expr_ty(a).peel_refs();
-        let (b_ty, b_n_refs) = peel_middle_ty_refs(cx.typeck_results().expr_ty(b));
+        let (b_ty, b_n_refs, _) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(b));
 
         (a_ty == b_ty && (is_int(a_ty) || is_duration(a_ty))).then_some((a_ty, b_n_refs))
     }
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 ba1ad599e11..bee3b19b597 100644
--- a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs
@@ -4,7 +4,7 @@ use rustc_errors::Applicability;
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{
     Block, Body, Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, FnDecl,
-    FnRetTy, GenericBound, ImplItem, Item, Node, OpaqueTy, TraitRef, Ty, TyKind,
+    FnRetTy, GenericBound, Node, OpaqueTy, TraitRef, Ty, TyKind,
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::middle::resolve_bound_vars::ResolvedArg;
@@ -60,8 +60,11 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
             && let ExprKind::Block(block, _) = body.value.kind
             && block.stmts.is_empty()
             && let Some(closure_body) = desugared_async_block(cx, block)
-            && let Node::Item(Item {vis_span, ..}) | Node::ImplItem(ImplItem {vis_span, ..}) =
-                cx.tcx.hir_node_by_def_id(fn_def_id)
+            && let Some(vis_span_opt) = match cx.tcx.hir_node_by_def_id(fn_def_id) {
+                Node::Item(item) => Some(Some(item.vis_span)),
+                Node::ImplItem(impl_item) => Some(impl_item.vis_span()),
+                _ => None,
+            }
             && !span.from_expansion()
         {
             let header_span = span.with_hi(ret_ty.span.hi());
@@ -72,7 +75,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
                 header_span,
                 "this function can be simplified using the `async fn` syntax",
                 |diag| {
-                    if let Some(vis_snip) = vis_span.get_source_text(cx)
+                    if let Some(vis_span) = vis_span_opt
+                        && let Some(vis_snip) = vis_span.get_source_text(cx)
                         && let Some(header_snip) = header_span.get_source_text(cx)
                         && let Some(ret_pos) = position_before_rarrow(&header_snip)
                         && let Some((_, ret_snip)) = suggested_ret(cx, output)
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 ac8c88f0205..2eebb2430fd 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
@@ -97,11 +97,12 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck {
             return;
         }
 
-        if let Some(macro_call) = matching_root_macro_call(cx, expr.span, sym::matches_macro) {
-            if let ExprKind::Match(recv, [arm, ..], _) = expr.kind {
-                let range = check_pat(&arm.pat.kind);
-                check_is_ascii(cx, macro_call.span, recv, &range, None);
-            }
+        let (arg, span, range) = if let Some(macro_call) = matching_root_macro_call(cx, expr.span, sym::matches_macro)
+            && let ExprKind::Match(recv, [arm, ..], _) = expr.kind
+        {
+            let recv = peel_ref_operators(cx, recv);
+            let range = check_pat(&arm.pat.kind);
+            (recv, macro_call.span, range)
         } else if let ExprKind::MethodCall(path, receiver, [arg], ..) = expr.kind
             && path.ident.name == sym::contains
             && let Some(higher::Range {
@@ -112,10 +113,14 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck {
             && !matches!(cx.typeck_results().expr_ty(arg).peel_refs().kind(), ty::Param(_))
         {
             let arg = peel_ref_operators(cx, arg);
-            let ty_sugg = get_ty_sugg(cx, arg);
             let range = check_expr_range(start, end);
-            check_is_ascii(cx, expr.span, arg, &range, ty_sugg);
-        }
+            (arg, expr.span, range)
+        } else {
+            return;
+        };
+
+        let ty_sugg = get_ty_sugg(cx, arg);
+        check_is_ascii(cx, span, arg, &range, ty_sugg);
     }
 }
 
@@ -146,9 +151,8 @@ fn check_is_ascii(
         CharRange::HexDigit => "is_ascii_hexdigit",
         CharRange::Otherwise | CharRange::LowerHexLetter | CharRange::UpperHexLetter => return,
     };
-    let default_snip = "..";
     let mut app = Applicability::MachineApplicable;
-    let recv = Sugg::hir_with_context(cx, recv, span.ctxt(), default_snip, &mut app).maybe_paren();
+    let recv = Sugg::hir_with_context(cx, recv, span.ctxt(), "_", &mut app).maybe_paren();
     let mut suggestion = vec![(span, format!("{recv}.{sugg}()"))];
     if let Some((ty_span, ty)) = ty_sugg {
         suggestion.push((ty_span, format!("{recv}: {ty}")));
@@ -182,7 +186,7 @@ fn check_pat(pat_kind: &PatKind<'_>) -> CharRange {
                 CharRange::Otherwise
             }
         },
-        PatKind::Range(Some(start), Some(end), kind) if *kind == RangeEnd::Included => check_range(start, end),
+        PatKind::Range(Some(start), Some(end), RangeEnd::Included) => check_range(start, end),
         _ => CharRange::Otherwise,
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/manual_let_else.rs b/src/tools/clippy/clippy_lints/src/manual_let_else.rs
index 1f9a943f13d..2705ef20b79 100644
--- a/src/tools/clippy/clippy_lints/src/manual_let_else.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_let_else.rs
@@ -49,7 +49,7 @@ declare_clippy_lint! {
 }
 
 impl<'tcx> QuestionMark {
-    pub(crate) fn check_manual_let_else(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) {
+    pub(crate) fn check_manual_let_else(&self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) {
         if let StmtKind::Let(local) = stmt.kind
             && let Some(init) = local.init
             && local.els.is_none()
@@ -287,7 +287,7 @@ fn replace_in_pattern(
                 }
                 return or_pat;
             },
-            PatKind::Struct(path, fields, has_dot_dot) => {
+            PatKind::Struct(path, fields, dot_dot) => {
                 let fields = fields
                     .iter()
                     .map(|fld| {
@@ -311,7 +311,7 @@ fn replace_in_pattern(
                     .collect::<Vec<_>>();
                 let fields_string = fields.join(", ");
 
-                let dot_dot_str = if has_dot_dot { " .." } else { "" };
+                let dot_dot_str = if dot_dot.is_some() { " .." } else { "" };
                 let (sn_pth, _) = snippet_with_context(cx, path.span(), span.ctxt(), "", app);
                 return format!("{sn_pth} {{ {fields_string}{dot_dot_str} }}");
             },
diff --git a/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs b/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs
index 922db174e3d..b036e78cded 100644
--- a/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_option_as_slice.rs
@@ -1,7 +1,7 @@
 use clippy_config::Conf;
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
 use clippy_utils::msrvs::Msrv;
-use clippy_utils::{is_none_arm, msrvs, peel_hir_expr_refs, sym};
+use clippy_utils::{is_none_pattern, msrvs, peel_hir_expr_refs, sym};
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{Arm, Expr, ExprKind, LangItem, Pat, PatKind, QPath, is_range_literal};
@@ -60,8 +60,8 @@ impl LateLintPass<'_> for ManualOptionAsSlice {
         }
         match expr.kind {
             ExprKind::Match(scrutinee, [arm1, arm2], _) => {
-                if is_none_arm(cx, arm2) && check_arms(cx, arm2, arm1)
-                    || is_none_arm(cx, arm1) && check_arms(cx, arm1, arm2)
+                if is_none_pattern(cx, arm2.pat) && check_arms(cx, arm2, arm1)
+                    || is_none_pattern(cx, arm1.pat) && check_arms(cx, arm1, arm2)
                 {
                     check_as_ref(cx, scrutinee, span, self.msrv);
                 }
diff --git a/src/tools/clippy/clippy_lints/src/manual_retain.rs b/src/tools/clippy/clippy_lints/src/manual_retain.rs
index 98e8b1f5cf9..7fb88763e64 100644
--- a/src/tools/clippy/clippy_lints/src/manual_retain.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_retain.rs
@@ -123,8 +123,8 @@ fn check_iter(
 ) {
     if let hir::ExprKind::MethodCall(_, filter_expr, [], _) = &target_expr.kind
         && let Some(copied_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id)
-        && (cx.tcx.is_diagnostic_item(sym::iter_copied, copied_def_id)
-            || cx.tcx.is_diagnostic_item(sym::iter_cloned, copied_def_id))
+        && let Some(copied_name) = cx.tcx.get_diagnostic_name(copied_def_id)
+        && matches!(copied_name, sym::iter_copied | sym::iter_cloned)
         && let hir::ExprKind::MethodCall(_, iter_expr, [_], _) = &filter_expr.kind
         && let Some(filter_def_id) = cx.typeck_results().type_dependent_def_id(filter_expr.hir_id)
         && cx.tcx.is_diagnostic_item(sym::iter_filter, filter_def_id)
@@ -243,9 +243,9 @@ fn make_sugg(
 }
 
 fn match_acceptable_sym(cx: &LateContext<'_>, collect_def_id: DefId) -> bool {
-    ACCEPTABLE_METHODS
-        .iter()
-        .any(|&method| cx.tcx.is_diagnostic_item(method, collect_def_id))
+    cx.tcx
+        .get_diagnostic_name(collect_def_id)
+        .is_some_and(|collect_name| ACCEPTABLE_METHODS.contains(&collect_name))
 }
 
 fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: Msrv) -> bool {
diff --git a/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs b/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs
index 0c09a47c965..de12fa29d02 100644
--- a/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs
@@ -2,6 +2,7 @@ use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_with_context;
+use clippy_utils::ty::peel_and_count_ty_refs;
 use clippy_utils::{expr_or_init, is_in_const_context, std_or_core};
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind};
@@ -102,7 +103,7 @@ fn simplify_half<'tcx>(
         && let ExprKind::MethodCall(method_path, receiver, [], _) = expr1.kind
         && method_path.ident.name == sym::len
         && let receiver_ty = cx.typeck_results().expr_ty(receiver)
-        && let (receiver_ty, refs_count) = clippy_utils::ty::walk_ptrs_ty_depth(receiver_ty)
+        && let (receiver_ty, refs_count, _) = peel_and_count_ty_refs(receiver_ty)
         && let ty::Slice(ty1) = receiver_ty.kind()
         // expr2 is `size_of::<T2>()`?
         && let ExprKind::Call(func, []) = expr2.kind
diff --git a/src/tools/clippy/clippy_lints/src/manual_strip.rs b/src/tools/clippy/clippy_lints/src/manual_strip.rs
index 6bf43a1c6d4..07cce4046ca 100644
--- a/src/tools/clippy/clippy_lints/src/manual_strip.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_strip.rs
@@ -75,12 +75,10 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip {
             && let ExprKind::Path(target_path) = &target_arg.kind
             && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(cond.hir_id)
         {
-            let strip_kind = if cx.tcx.is_diagnostic_item(sym::str_starts_with, method_def_id) {
-                StripKind::Prefix
-            } else if cx.tcx.is_diagnostic_item(sym::str_ends_with, method_def_id) {
-                StripKind::Suffix
-            } else {
-                return;
+            let strip_kind = match cx.tcx.get_diagnostic_name(method_def_id) {
+                Some(sym::str_starts_with) => StripKind::Prefix,
+                Some(sym::str_ends_with) => StripKind::Suffix,
+                _ => return,
             };
             let target_res = cx.qpath_res(target_path, target_arg.hir_id);
             if target_res == Res::Err {
diff --git a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs
index af6a1b07a49..39e5289c62a 100644
--- a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs
@@ -116,8 +116,10 @@ fn is_unit_expression(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
 /// The expression inside a closure may or may not have surrounding braces and
 /// semicolons, which causes problems when generating a suggestion. Given an
 /// expression that evaluates to '()' or '!', recursively remove useless braces
-/// and semi-colons until is suitable for including in the suggestion template
-fn reduce_unit_expression(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Span> {
+/// and semi-colons until is suitable for including in the suggestion template.
+/// The `bool` is `true` when the resulting `span` needs to be enclosed in an
+/// `unsafe` block.
+fn reduce_unit_expression(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<(Span, bool)> {
     if !is_unit_expression(cx, expr) {
         return None;
     }
@@ -125,22 +127,24 @@ fn reduce_unit_expression(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<
     match expr.kind {
         hir::ExprKind::Call(_, _) | hir::ExprKind::MethodCall(..) => {
             // Calls can't be reduced any more
-            Some(expr.span)
+            Some((expr.span, false))
         },
         hir::ExprKind::Block(block, _) => {
+            let is_unsafe = matches!(block.rules, hir::BlockCheckMode::UnsafeBlock(_));
             match (block.stmts, block.expr.as_ref()) {
                 ([], Some(inner_expr)) => {
                     // If block only contains an expression,
                     // reduce `{ X }` to `X`
                     reduce_unit_expression(cx, inner_expr)
+                        .map(|(span, inner_is_unsafe)| (span, inner_is_unsafe || is_unsafe))
                 },
                 ([inner_stmt], None) => {
                     // If block only contains statements,
                     // reduce `{ X; }` to `X` or `X;`
                     match inner_stmt.kind {
-                        hir::StmtKind::Let(local) => Some(local.span),
-                        hir::StmtKind::Expr(e) => Some(e.span),
-                        hir::StmtKind::Semi(..) => Some(inner_stmt.span),
+                        hir::StmtKind::Let(local) => Some((local.span, is_unsafe)),
+                        hir::StmtKind::Expr(e) => Some((e.span, is_unsafe)),
+                        hir::StmtKind::Semi(..) => Some((inner_stmt.span, is_unsafe)),
                         hir::StmtKind::Item(..) => None,
                     }
                 },
@@ -228,10 +232,11 @@ fn lint_map_unit_fn(
         let msg = suggestion_msg("closure", map_type);
 
         span_lint_and_then(cx, lint, expr.span, msg, |diag| {
-            if let Some(reduced_expr_span) = reduce_unit_expression(cx, closure_expr) {
+            if let Some((reduced_expr_span, is_unsafe)) = reduce_unit_expression(cx, closure_expr) {
                 let mut applicability = Applicability::MachineApplicable;
+                let (prefix_is_unsafe, suffix_is_unsafe) = if is_unsafe { ("unsafe { ", " }") } else { ("", "") };
                 let suggestion = format!(
-                    "if let {0}({1}) = {2} {{ {3} }}",
+                    "if let {0}({1}) = {2} {{ {prefix_is_unsafe}{3}{suffix_is_unsafe} }}",
                     variant,
                     snippet_with_applicability(cx, binding.pat.span, "_", &mut applicability),
                     snippet_with_applicability(cx, var_arg.span, "_", &mut applicability),
diff --git a/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs b/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs
index 5b50efad3e4..aaf559fc443 100644
--- a/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs
@@ -4,20 +4,22 @@ use clippy_utils::msrvs::Msrv;
 use clippy_utils::source::snippet;
 use clippy_utils::visitors::is_local_used;
 use clippy_utils::{
-    SpanlessEq, is_res_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt, peel_ref_operators,
+    SpanlessEq, get_ref_operators, is_res_lang_ctor, is_unit_expr, path_to_local, peel_blocks_with_stmt,
+    peel_ref_operators,
 };
+use rustc_ast::BorrowKind;
 use rustc_errors::MultiSpan;
 use rustc_hir::LangItem::OptionNone;
-use rustc_hir::{Arm, Expr, HirId, Pat, PatExpr, PatExprKind, PatKind};
+use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatExpr, PatExprKind, PatKind};
 use rustc_lint::LateContext;
 use rustc_span::Span;
 
 use super::{COLLAPSIBLE_MATCH, pat_contains_disallowed_or};
 
-pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], msrv: Msrv) {
+pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>], msrv: Msrv) {
     if let Some(els_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) {
         for arm in arms {
-            check_arm(cx, true, arm.pat, arm.body, arm.guard, Some(els_arm.body), msrv);
+            check_arm(cx, true, arm.pat, expr, arm.body, arm.guard, Some(els_arm.body), msrv);
         }
     }
 }
@@ -27,15 +29,18 @@ pub(super) fn check_if_let<'tcx>(
     pat: &'tcx Pat<'_>,
     body: &'tcx Expr<'_>,
     else_expr: Option<&'tcx Expr<'_>>,
+    let_expr: &'tcx Expr<'_>,
     msrv: Msrv,
 ) {
-    check_arm(cx, false, pat, body, None, else_expr, msrv);
+    check_arm(cx, false, pat, let_expr, body, None, else_expr, msrv);
 }
 
+#[allow(clippy::too_many_arguments)]
 fn check_arm<'tcx>(
     cx: &LateContext<'tcx>,
     outer_is_match: bool,
     outer_pat: &'tcx Pat<'tcx>,
+    outer_cond: &'tcx Expr<'tcx>,
     outer_then_body: &'tcx Expr<'tcx>,
     outer_guard: Option<&'tcx Expr<'tcx>>,
     outer_else_body: Option<&'tcx Expr<'tcx>>,
@@ -82,6 +87,9 @@ fn check_arm<'tcx>(
             },
             IfLetOrMatch::Match(_, arms, ..) => !arms.iter().any(|arm| is_local_used(cx, arm, binding_id)),
         }
+        // Check if the inner expression contains any borrows/dereferences
+        && let ref_types = get_ref_operators(cx, inner_scrutinee)
+        && let Some(method) = build_ref_method_chain(ref_types)
     {
         let msg = format!(
             "this `{}` can be collapsed into the outer `{}`",
@@ -103,6 +111,10 @@ fn check_arm<'tcx>(
             let mut help_span = MultiSpan::from_spans(vec![binding_span, inner_then_pat.span]);
             help_span.push_span_label(binding_span, "replace this binding");
             help_span.push_span_label(inner_then_pat.span, format!("with this pattern{replace_msg}"));
+            if !method.is_empty() {
+                let outer_cond_msg = format!("use: `{}{}`", snippet(cx, outer_cond.span, ".."), method);
+                help_span.push_span_label(outer_cond.span, outer_cond_msg);
+            }
             diag.span_help(
                 help_span,
                 "the outer pattern can be modified to include the inner pattern",
@@ -148,3 +160,30 @@ fn find_pat_binding_and_is_innermost_parent_pat_struct(pat: &Pat<'_>, hir_id: Hi
     });
     (span, is_innermost_parent_pat_struct)
 }
+
+/// Builds a chain of reference-manipulation method calls (e.g., `.as_ref()`, `.as_mut()`,
+/// `.copied()`) based on reference operators
+fn build_ref_method_chain(expr: Vec<&Expr<'_>>) -> Option<String> {
+    let mut req_method_calls = String::new();
+
+    for ref_operator in expr {
+        match ref_operator.kind {
+            ExprKind::AddrOf(BorrowKind::Raw, _, _) => {
+                return None;
+            },
+            ExprKind::AddrOf(_, m, _) if m.is_mut() => {
+                req_method_calls.push_str(".as_mut()");
+            },
+            ExprKind::AddrOf(_, _, _) => {
+                req_method_calls.push_str(".as_ref()");
+            },
+            // Deref operator is the only operator that this function should have received
+            ExprKind::Unary(_, _) => {
+                req_method_calls.push_str(".copied()");
+            },
+            _ => (),
+        }
+    }
+
+    Some(req_method_calls)
+}
diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs b/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs
index edbb556fd97..a8490d6aa7d 100644
--- a/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/manual_ok_err.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::{indent_of, reindent_multiline};
 use clippy_utils::sugg::Sugg;
-use clippy_utils::ty::{option_arg_ty, peel_mid_ty_refs_is_mutable};
+use clippy_utils::ty::{option_arg_ty, peel_and_count_ty_refs};
 use clippy_utils::{get_parent_expr, is_res_lang_ctor, path_res, peel_blocks, span_contains_comment};
 use rustc_ast::{BindingMode, Mutability};
 use rustc_errors::Applicability;
@@ -135,15 +135,11 @@ fn apply_lint(cx: &LateContext<'_>, expr: &Expr<'_>, scrutinee: &Expr<'_>, is_ok
     let scrut = Sugg::hir_with_applicability(cx, scrutinee, "..", &mut app).maybe_paren();
 
     let scrutinee_ty = cx.typeck_results().expr_ty(scrutinee);
-    let (_, n_ref, mutability) = peel_mid_ty_refs_is_mutable(scrutinee_ty);
-    let prefix = if n_ref > 0 {
-        if mutability == Mutability::Mut {
-            ".as_mut()"
-        } else {
-            ".as_ref()"
-        }
-    } else {
-        ""
+    let (_, _, mutability) = peel_and_count_ty_refs(scrutinee_ty);
+    let prefix = match mutability {
+        Some(Mutability::Mut) => ".as_mut()",
+        Some(Mutability::Not) => ".as_ref()",
+        None => "",
     };
 
     let sugg = format!("{scrut}{prefix}.{method}()");
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 dbae71bbb1b..d4bfdb7e440 100644
--- a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs
@@ -2,7 +2,7 @@ use crate::map_unit_fn::OPTION_MAP_UNIT_FN;
 use crate::matches::MATCH_AS_REF;
 use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
 use clippy_utils::sugg::Sugg;
-use clippy_utils::ty::{is_copy, is_type_diagnostic_item, peel_mid_ty_refs_is_mutable, type_is_unsafe_function};
+use clippy_utils::ty::{is_copy, is_type_diagnostic_item, is_unsafe_fn, peel_and_count_ty_refs};
 use clippy_utils::{
     CaptureKind, can_move_expr_to_closure, expr_requires_coercion, is_else_clause, is_lint_allowed, is_res_lang_ctor,
     path_res, path_to_local_id, peel_blocks, peel_hir_expr_refs, peel_hir_expr_while,
@@ -30,8 +30,9 @@ pub(super) fn check_with<'tcx, F>(
 where
     F: Fn(&LateContext<'tcx>, &'tcx Pat<'_>, &'tcx Expr<'_>, SyntaxContext) -> Option<SomeExpr<'tcx>>,
 {
-    let (scrutinee_ty, ty_ref_count, ty_mutability) =
-        peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee));
+    let (scrutinee_ty, ty_ref_count, ty_mutability) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(scrutinee));
+    let ty_mutability = ty_mutability.unwrap_or(Mutability::Mut);
+
     if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::Option)
         && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::Option))
     {
@@ -191,7 +192,7 @@ fn can_pass_as_func<'tcx>(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Ex
         ExprKind::Call(func, [arg])
             if path_to_local_id(arg, binding)
                 && cx.typeck_results().expr_adjustments(arg).is_empty()
-                && !type_is_unsafe_function(cx, cx.typeck_results().expr_ty(func).peel_refs()) =>
+                && !is_unsafe_fn(cx, cx.typeck_results().expr_ty(func).peel_refs()) =>
         {
             Some(func)
         },
diff --git a/src/tools/clippy/clippy_lints/src/matches/match_bool.rs b/src/tools/clippy/clippy_lints/src/matches/match_bool.rs
index b90cf6357c5..a2c8741f4f7 100644
--- a/src/tools/clippy/clippy_lints/src/matches/match_bool.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/match_bool.rs
@@ -12,7 +12,11 @@ use super::MATCH_BOOL;
 
 pub(crate) fn check(cx: &LateContext<'_>, scrutinee: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
     // Type of expression is `bool`.
-    if *cx.typeck_results().expr_ty(scrutinee).kind() == ty::Bool {
+    if *cx.typeck_results().expr_ty(scrutinee).kind() == ty::Bool
+        && arms
+            .iter()
+            .all(|arm| arm.pat.walk_short(|p| !matches!(p.kind, PatKind::Binding(..))))
+    {
         span_lint_and_then(
             cx,
             MATCH_BOOL,
diff --git a/src/tools/clippy/clippy_lints/src/matches/match_ref_pats.rs b/src/tools/clippy/clippy_lints/src/matches/match_ref_pats.rs
index 5445ee1f042..5934ec40993 100644
--- a/src/tools/clippy/clippy_lints/src/matches/match_ref_pats.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/match_ref_pats.rs
@@ -17,6 +17,11 @@ where
         return;
     }
 
+    // `!` cannot be deref-ed
+    if cx.typeck_results().expr_ty(scrutinee).is_never() {
+        return;
+    }
+
     let (first_sugg, msg, title);
     let ctxt = expr.span.ctxt();
     let mut app = Applicability::Unspecified;
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 8b4c1700051..eb8b16e1561 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
@@ -54,7 +54,7 @@ impl<'tcx> Visitor<'tcx> for MatchExprVisitor<'_, 'tcx> {
 }
 
 impl MatchExprVisitor<'_, '_> {
-    fn case_altered(&mut self, segment_ident: Symbol, receiver: &Expr<'_>) -> ControlFlow<CaseMethod> {
+    fn case_altered(&self, segment_ident: Symbol, 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();
 
diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs
index c128fc40b73..6f49c552411 100644
--- a/src/tools/clippy/clippy_lints/src/matches/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs
@@ -1073,7 +1073,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
                 significant_drop_in_scrutinee::check_match(cx, expr, ex, arms, source);
             }
 
-            collapsible_match::check_match(cx, arms, self.msrv);
+            collapsible_match::check_match(cx, ex, arms, self.msrv);
             if !from_expansion {
                 // These don't depend on a relationship between multiple arms
                 match_wild_err_arm::check(cx, ex, arms);
@@ -1137,7 +1137,14 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
                 match_ref_pats::check(cx, ex, arms.iter().map(|el| el.pat), expr);
             }
         } else if let Some(if_let) = higher::IfLet::hir(cx, expr) {
-            collapsible_match::check_if_let(cx, if_let.let_pat, if_let.if_then, if_let.if_else, self.msrv);
+            collapsible_match::check_if_let(
+                cx,
+                if_let.let_pat,
+                if_let.if_then,
+                if_let.if_else,
+                if_let.let_expr,
+                self.msrv,
+            );
             significant_drop_in_scrutinee::check_if_let(cx, expr, if_let.let_expr, if_let.if_then, if_let.if_else);
             if !from_expansion {
                 if let Some(else_expr) = if_let.if_else {
diff --git a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs
index b04db03f8d2..3a2097c3df2 100644
--- a/src/tools/clippy/clippy_lints/src/matches/needless_match.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/needless_match.rs
@@ -1,7 +1,7 @@
 use super::NEEDLESS_MATCH;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::ty::{is_type_diagnostic_item, same_type_and_consts};
+use clippy_utils::ty::{is_type_diagnostic_item, same_type_modulo_regions};
 use clippy_utils::{
     SpanlessEq, eq_expr_value, get_parent_expr_for_hir, higher, is_else_clause, is_res_lang_ctor, over, path_res,
     peel_blocks_with_stmt,
@@ -122,7 +122,7 @@ fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_>
         // Compare match_expr ty with local in `let local = match match_expr {..}`
         Node::LetStmt(local) => {
             let results = cx.typeck_results();
-            return same_type_and_consts(results.node_type(local.hir_id), results.expr_ty(expr));
+            return same_type_modulo_regions(results.node_type(local.hir_id), results.expr_ty(expr));
         },
         // compare match_expr ty with RetTy in `fn foo() -> RetTy`
         Node::Item(item) => {
@@ -133,7 +133,7 @@ fn expr_ty_matches_p_ty(cx: &LateContext<'_>, expr: &Expr<'_>, p_expr: &Expr<'_>
                     .instantiate_identity()
                     .output()
                     .skip_binder();
-                return same_type_and_consts(output, cx.typeck_results().expr_ty(expr));
+                return same_type_modulo_regions(output, cx.typeck_results().expr_ty(expr));
             }
         },
         // check the parent expr for this whole block `{ match match_expr {..} }`
diff --git a/src/tools/clippy/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs b/src/tools/clippy/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs
index 2154cd5b24a..1130d82ab78 100644
--- a/src/tools/clippy/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs
@@ -7,7 +7,7 @@ use super::REST_PAT_IN_FULLY_BOUND_STRUCTS;
 
 pub(crate) fn check(cx: &LateContext<'_>, pat: &Pat<'_>) {
     if !pat.span.from_expansion()
-        && let PatKind::Struct(QPath::Resolved(_, path), fields, true) = pat.kind
+        && let PatKind::Struct(QPath::Resolved(_, path), fields, Some(dotdot)) = pat.kind
         && let Some(def_id) = path.res.opt_def_id()
         && let ty = cx.tcx.type_of(def_id).instantiate_identity()
         && let ty::Adt(def, _) = ty.kind()
@@ -15,14 +15,18 @@ pub(crate) fn check(cx: &LateContext<'_>, pat: &Pat<'_>) {
         && fields.len() == def.non_enum_variant().fields.len()
         && !def.non_enum_variant().is_field_list_non_exhaustive()
     {
-        #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
         span_lint_and_then(
             cx,
             REST_PAT_IN_FULLY_BOUND_STRUCTS,
             pat.span,
             "unnecessary use of `..` pattern in struct binding. All fields were already bound",
             |diag| {
-                diag.help("consider removing `..` from this binding");
+                diag.span_suggestion_verbose(
+                    dotdot,
+                    "consider removing `..` from this binding",
+                    "",
+                    rustc_errors::Applicability::MachineApplicable,
+                );
             },
         );
     }
diff --git a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs
index 027dd7ce053..81fecc87256 100644
--- a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs
@@ -226,11 +226,12 @@ impl<'a, 'tcx> SigDropChecker<'a, 'tcx> {
     }
 }
 
-#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Default)]
 enum SigDropHolder {
     /// No values with significant drop present in this expression.
     ///
     /// Expressions that we've emitted lints do not count.
+    #[default]
     None,
     /// Some field in this expression references to values with significant drop.
     ///
@@ -244,12 +245,6 @@ enum SigDropHolder {
     Moved,
 }
 
-impl Default for SigDropHolder {
-    fn default() -> Self {
-        Self::None
-    }
-}
-
 struct SigDropHelper<'a, 'tcx> {
     cx: &'a LateContext<'tcx>,
     parent_expr: Option<&'tcx Expr<'tcx>>,
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 bcf079b7007..83939d32579 100644
--- a/src/tools/clippy/clippy_lints/src/matches/single_match.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/single_match.rs
@@ -2,10 +2,8 @@ use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::{
     SpanRangeExt, expr_block, snippet, snippet_block_with_context, snippet_with_applicability, snippet_with_context,
 };
-use clippy_utils::ty::implements_trait;
-use clippy_utils::{
-    is_lint_allowed, is_unit_expr, peel_blocks, peel_hir_pat_refs, peel_middle_ty_refs, peel_n_hir_expr_refs,
-};
+use clippy_utils::ty::{implements_trait, peel_and_count_ty_refs};
+use clippy_utils::{is_lint_allowed, is_unit_expr, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs};
 use core::ops::ControlFlow;
 use rustc_arena::DroplessArena;
 use rustc_errors::{Applicability, Diag};
@@ -133,7 +131,7 @@ fn report_single_pattern(
 
     let (pat, pat_ref_count) = peel_hir_pat_refs(arm.pat);
     let (msg, sugg) = if let PatKind::Expr(_) = pat.kind
-        && let (ty, ty_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(ex))
+        && let (ty, ty_ref_count, _) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(ex))
         && let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait()
         && let Some(pe_trait_id) = cx.tcx.lang_items().eq_trait()
         && (ty.is_integral()
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 ff7769af1df..af90cb5e673 100644
--- a/src/tools/clippy/clippy_lints/src/matches/try_err.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/try_err.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::ty::option_arg_ty;
 use clippy_utils::{get_parent_expr, is_res_lang_ctor, path_res};
 use rustc_errors::Applicability;
 use rustc_hir::LangItem::ResultErr;
@@ -28,25 +28,15 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine
         && is_res_lang_ctor(cx, path_res(cx, err_fun), ResultErr)
         && let Some(return_ty) = find_return_type(cx, &expr.kind)
     {
-        let prefix;
-        let suffix;
-        let err_ty;
-
-        if let Some(ty) = result_error_type(cx, return_ty) {
-            prefix = "Err(";
-            suffix = ")";
-            err_ty = ty;
+        let (prefix, suffix, err_ty) = if let Some(ty) = result_error_type(cx, return_ty) {
+            ("Err(", ")", ty)
         } else if let Some(ty) = poll_result_error_type(cx, return_ty) {
-            prefix = "Poll::Ready(Err(";
-            suffix = "))";
-            err_ty = ty;
+            ("Poll::Ready(Err(", "))", ty)
         } else if let Some(ty) = poll_option_result_error_type(cx, return_ty) {
-            prefix = "Poll::Ready(Some(Err(";
-            suffix = ")))";
-            err_ty = ty;
+            ("Poll::Ready(Some(Err(", ")))", ty)
         } else {
             return;
-        }
+        };
 
         span_lint_and_then(
             cx,
@@ -88,8 +78,8 @@ fn find_return_type<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx ExprKind<'_>) -> O
 
 /// Extracts the error type from Result<T, E>.
 fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
-    if let ty::Adt(_, subst) = ty.kind()
-        && is_type_diagnostic_item(cx, ty, sym::Result)
+    if let ty::Adt(def, subst) = ty.kind()
+        && cx.tcx.is_diagnostic_item(sym::Result, def.did())
     {
         Some(subst.type_at(1))
     } else {
@@ -101,11 +91,9 @@ fn result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'t
 fn poll_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<Ty<'tcx>> {
     if let ty::Adt(def, subst) = ty.kind()
         && cx.tcx.lang_items().get(LangItem::Poll) == Some(def.did())
-        && let ready_ty = subst.type_at(0)
-        && let ty::Adt(ready_def, ready_subst) = ready_ty.kind()
-        && cx.tcx.is_diagnostic_item(sym::Result, ready_def.did())
     {
-        Some(ready_subst.type_at(1))
+        let ready_ty = subst.type_at(0);
+        result_error_type(cx, ready_ty)
     } else {
         None
     }
@@ -116,13 +104,9 @@ fn poll_option_result_error_type<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) ->
     if let ty::Adt(def, subst) = ty.kind()
         && cx.tcx.lang_items().get(LangItem::Poll) == Some(def.did())
         && let ready_ty = subst.type_at(0)
-        && let ty::Adt(ready_def, ready_subst) = ready_ty.kind()
-        && cx.tcx.is_diagnostic_item(sym::Option, ready_def.did())
-        && let some_ty = ready_subst.type_at(0)
-        && let ty::Adt(some_def, some_subst) = some_ty.kind()
-        && cx.tcx.is_diagnostic_item(sym::Result, some_def.did())
+        && let Some(some_ty) = option_arg_ty(cx, ready_ty)
     {
-        Some(some_subst.type_at(1))
+        result_error_type(cx, some_ty)
     } else {
         None
     }
diff --git a/src/tools/clippy/clippy_lints/src/mem_replace.rs b/src/tools/clippy/clippy_lints/src/mem_replace.rs
index 28efd2038b3..e39916f733d 100644
--- a/src/tools/clippy/clippy_lints/src/mem_replace.rs
+++ b/src/tools/clippy/clippy_lints/src/mem_replace.rs
@@ -215,7 +215,8 @@ fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'
         && let ExprKind::Path(ref repl_func_qpath) = repl_func.kind
         && let Some(repl_def_id) = cx.qpath_res(repl_func_qpath, repl_func.hir_id).opt_def_id()
     {
-        if cx.tcx.is_diagnostic_item(sym::mem_uninitialized, repl_def_id) {
+        let repl_name = cx.tcx.get_diagnostic_name(repl_def_id);
+        if repl_name == Some(sym::mem_uninitialized) {
             let Some(top_crate) = std_or_core(cx) else { return };
             let mut applicability = Applicability::MachineApplicable;
             span_lint_and_sugg(
@@ -230,9 +231,7 @@ fn check_replace_with_uninit(cx: &LateContext<'_>, src: &Expr<'_>, dest: &Expr<'
                 ),
                 applicability,
             );
-        } else if cx.tcx.is_diagnostic_item(sym::mem_zeroed, repl_def_id)
-            && !cx.typeck_results().expr_ty(src).is_primitive()
-        {
+        } else if repl_name == Some(sym::mem_zeroed) && !cx.typeck_results().expr_ty(src).is_primitive() {
             span_lint_and_help(
                 cx,
                 MEM_REPLACE_WITH_UNINIT,
diff --git a/src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs b/src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs
index 96e2de0dc1c..65583c6a981 100644
--- a/src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/clone_on_ref_ptr.rs
@@ -24,9 +24,10 @@ pub(super) fn check(
         && let Some(name) = cx.tcx.get_diagnostic_name(adt.did())
     {
         let caller_type = match name {
-            sym::Rc => "Rc",
-            sym::Arc => "Arc",
-            sym::RcWeak | sym::ArcWeak => "Weak",
+            sym::Rc => "std::rc::Rc",
+            sym::Arc => "std::sync::Arc",
+            sym::RcWeak => "std::rc::Weak",
+            sym::ArcWeak => "std::sync::Weak",
             _ => return,
         };
         span_lint_and_then(
diff --git a/src/tools/clippy/clippy_lints/src/methods/double_ended_iterator_last.rs b/src/tools/clippy/clippy_lints/src/methods/double_ended_iterator_last.rs
index 6d841853fbe..578865c3291 100644
--- a/src/tools/clippy/clippy_lints/src/methods/double_ended_iterator_last.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/double_ended_iterator_last.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::ty::{has_non_owning_mutable_access, implements_trait};
-use clippy_utils::{is_mutable, is_trait_method, path_to_local, sym};
+use clippy_utils::{is_mutable, is_trait_method, path_to_local_with_projections, sym};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, Node, PatKind};
 use rustc_lint::LateContext;
@@ -37,7 +37,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, self_expr: &'_ Exp
         // TODO: Change this to lint only when the referred iterator is not used later. If it is used later,
         // changing to `next_back()` may change its behavior.
         if !(is_mutable(cx, self_expr) || self_type.is_ref()) {
-            if let Some(hir_id) = path_to_local(self_expr)
+            if let Some(hir_id) = path_to_local_with_projections(self_expr)
                 && let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
                 && let PatKind::Binding(_, _, ident, _) = pat.kind
             {
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 6e5da5bda8c..818e26f8aa1 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
@@ -15,7 +15,6 @@ use std::ops::ControlFlow;
 use super::EXPECT_FUN_CALL;
 
 /// Checks for the `EXPECT_FUN_CALL` lint.
-#[allow(clippy::too_many_lines)]
 pub(super) fn check<'tcx>(
     cx: &LateContext<'tcx>,
     format_args_storage: &FormatArgsStorage,
@@ -25,43 +24,6 @@ pub(super) fn check<'tcx>(
     receiver: &'tcx hir::Expr<'tcx>,
     args: &'tcx [hir::Expr<'tcx>],
 ) {
-    // Strip `{}`, `&`, `as_ref()` and `as_str()` off `arg` until we're left with either a `String` or
-    // `&str`
-    fn get_arg_root<'a>(cx: &LateContext<'_>, arg: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> {
-        let mut arg_root = peel_blocks(arg);
-        loop {
-            arg_root = match &arg_root.kind {
-                hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr) => expr,
-                hir::ExprKind::MethodCall(method_name, receiver, [], ..) => {
-                    if (method_name.ident.name == sym::as_str || method_name.ident.name == sym::as_ref) && {
-                        let arg_type = cx.typeck_results().expr_ty(receiver);
-                        let base_type = arg_type.peel_refs();
-                        base_type.is_str() || is_type_lang_item(cx, base_type, hir::LangItem::String)
-                    } {
-                        receiver
-                    } else {
-                        break;
-                    }
-                },
-                _ => break,
-            };
-        }
-        arg_root
-    }
-
-    fn contains_call<'a>(cx: &LateContext<'a>, arg: &'a hir::Expr<'a>) -> bool {
-        for_each_expr(cx, arg, |expr| {
-            if matches!(expr.kind, hir::ExprKind::MethodCall { .. } | hir::ExprKind::Call { .. })
-                && !is_inside_always_const_context(cx.tcx, expr.hir_id)
-            {
-                ControlFlow::Break(())
-            } else {
-                ControlFlow::Continue(())
-            }
-        })
-        .is_some()
-    }
-
     if name == sym::expect
         && let [arg] = args
         && let arg_root = get_arg_root(cx, arg)
@@ -114,3 +76,40 @@ pub(super) fn check<'tcx>(
         );
     }
 }
+
+/// Strip `{}`, `&`, `as_ref()` and `as_str()` off `arg` until we're left with either a `String` or
+/// `&str`
+fn get_arg_root<'a>(cx: &LateContext<'_>, arg: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> {
+    let mut arg_root = peel_blocks(arg);
+    loop {
+        arg_root = match &arg_root.kind {
+            hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, expr) => expr,
+            hir::ExprKind::MethodCall(method_name, receiver, [], ..) => {
+                if (method_name.ident.name == sym::as_str || method_name.ident.name == sym::as_ref) && {
+                    let arg_type = cx.typeck_results().expr_ty(receiver);
+                    let base_type = arg_type.peel_refs();
+                    base_type.is_str() || is_type_lang_item(cx, base_type, hir::LangItem::String)
+                } {
+                    receiver
+                } else {
+                    break;
+                }
+            },
+            _ => break,
+        };
+    }
+    arg_root
+}
+
+fn contains_call<'a>(cx: &LateContext<'a>, arg: &'a hir::Expr<'a>) -> bool {
+    for_each_expr(cx, arg, |expr| {
+        if matches!(expr.kind, hir::ExprKind::MethodCall { .. } | hir::ExprKind::Call { .. })
+            && !is_inside_always_const_context(cx.tcx, expr.hir_id)
+        {
+            ControlFlow::Break(())
+        } else {
+            ControlFlow::Continue(())
+        }
+    })
+    .is_some()
+}
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 4dd54cf1974..2da0f8341b1 100644
--- a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs
@@ -106,7 +106,7 @@ enum CheckResult<'tcx> {
 
 impl<'tcx> OffendingFilterExpr<'tcx> {
     pub fn check_map_call(
-        &mut self,
+        &self,
         cx: &LateContext<'tcx>,
         map_body: &'tcx Body<'tcx>,
         map_param_id: HirId,
@@ -233,18 +233,16 @@ 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 {
-                Some(Self::IsSome {
+            match (cx.tcx.get_diagnostic_name(recv_ty.did()), path.ident.name) {
+                (Some(sym::Option), sym::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 {
-                Some(Self::IsOk {
+                }),
+                (Some(sym::Result), sym::is_ok) => Some(Self::IsOk {
                     receiver,
                     side_effect_expr_span,
-                })
-            } else {
-                None
+                }),
+                _ => None,
             }
         } else if matching_root_macro_call(cx, expr.span, sym::matches_macro).is_some()
             // we know for a fact that the wildcard pattern is the second arm
@@ -413,7 +411,7 @@ fn is_find_or_filter<'a>(
         }
 
         && let PatKind::Binding(_, filter_param_id, _, None) = filter_pat.kind
-        && let Some(mut offending_expr) = OffendingFilterExpr::hir(cx, filter_body.value, filter_param_id)
+        && let Some(offending_expr) = OffendingFilterExpr::hir(cx, filter_body.value, filter_param_id)
 
         && let ExprKind::Closure(&Closure { body: map_body_id, .. }) = map_arg.kind
         && let map_body = cx.tcx.hir_body(map_body_id)
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 6c1a14fc882..72f83b245a0 100644
--- a/src/tools/clippy/clippy_lints/src/methods/filter_next.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/filter_next.rs
@@ -1,4 +1,5 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
+use clippy_utils::path_to_local_with_projections;
 use clippy_utils::source::snippet;
 use clippy_utils::ty::implements_trait;
 use rustc_ast::{BindingMode, Mutability};
@@ -9,21 +10,6 @@ use rustc_span::sym;
 
 use super::FILTER_NEXT;
 
-fn path_to_local(expr: &hir::Expr<'_>) -> Option<hir::HirId> {
-    match expr.kind {
-        hir::ExprKind::Field(f, _) => path_to_local(f),
-        hir::ExprKind::Index(recv, _, _) => path_to_local(recv),
-        hir::ExprKind::Path(hir::QPath::Resolved(
-            _,
-            hir::Path {
-                res: rustc_hir::def::Res::Local(local),
-                ..
-            },
-        )) => Some(*local),
-        _ => None,
-    }
-}
-
 /// lint use of `filter().next()` for `Iterators`
 pub(super) fn check<'tcx>(
     cx: &LateContext<'tcx>,
@@ -44,7 +30,7 @@ pub(super) fn check<'tcx>(
             let iter_snippet = snippet(cx, recv.span, "..");
             // add note if not multi-line
             span_lint_and_then(cx, FILTER_NEXT, expr.span, msg, |diag| {
-                let (applicability, pat) = if let Some(id) = path_to_local(recv)
+                let (applicability, pat) = if let Some(id) = path_to_local_with_projections(recv)
                     && let hir::Node::Pat(pat) = cx.tcx.hir_node(id)
                     && let hir::PatKind::Binding(BindingMode(_, Mutability::Not), _, ident, _) = pat.kind
                 {
diff --git a/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs b/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs
index 8a976d1b4dc..0ba84919395 100644
--- a/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/implicit_clone.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_context;
-use clippy_utils::ty::implements_trait;
-use clippy_utils::{is_diag_item_method, is_diag_trait_item, peel_middle_ty_refs, sym};
+use clippy_utils::ty::{implements_trait, peel_and_count_ty_refs};
+use clippy_utils::{is_diag_item_method, is_diag_trait_item, sym};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
@@ -14,7 +14,7 @@ pub fn check(cx: &LateContext<'_>, method_name: Symbol, expr: &hir::Expr<'_>, re
         && is_clone_like(cx, method_name, method_def_id)
         && let return_type = cx.typeck_results().expr_ty(expr)
         && let input_type = cx.typeck_results().expr_ty(recv)
-        && let (input_type, ref_count) = peel_middle_ty_refs(input_type)
+        && let (input_type, ref_count, _) = peel_and_count_ty_refs(input_type)
         && !(ref_count > 0 && is_diag_trait_item(cx, method_def_id, sym::ToOwned))
         && let Some(ty_name) = input_type.ty_adt_def().map(|adt_def| cx.tcx.item_name(adt_def.did()))
         && return_type == input_type
diff --git a/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs b/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs
index 4ed7de81ea3..47195fdd65f 100644
--- a/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/inefficient_to_string.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::ty::{is_type_lang_item, walk_ptrs_ty_depth};
+use clippy_utils::ty::{is_type_lang_item, peel_and_count_ty_refs};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
@@ -24,7 +24,7 @@ pub fn check(
         && let Some(args) = cx.typeck_results().node_args_opt(expr.hir_id)
         && let arg_ty = cx.typeck_results().expr_ty_adjusted(receiver)
         && let self_ty = args.type_at(0)
-        && let (deref_self_ty, deref_count) = walk_ptrs_ty_depth(self_ty)
+        && let (deref_self_ty, deref_count, _) = peel_and_count_ty_refs(self_ty)
         && deref_count >= 1
         && specializes_tostring(cx, deref_self_ty)
     {
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 9a62b719a8f..fa8f9d640ee 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
@@ -24,32 +24,29 @@ fn get_iterator_length<'tcx>(cx: &LateContext<'tcx>, iter: &'tcx Expr<'tcx>) ->
     let ty::Adt(adt, substs) = cx.typeck_results().expr_ty(iter).kind() else {
         return None;
     };
-    let did = adt.did();
 
-    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)
-    } else if cx.tcx.is_diagnostic_item(sym::SliceIter, did)
-        && let ExprKind::MethodCall(_, recv, ..) = iter.kind
-    {
-        if let ty::Array(_, len) = cx.typeck_results().expr_ty(recv).peel_refs().kind() {
-            // For slice::Iter<'_, T>, the receiver might be an array literal: [1,2,3].iter().skip(..)
-            len.try_to_target_usize(cx.tcx).map(u128::from)
-        } else if let Some(args) = VecArgs::hir(cx, expr_or_init(cx, recv)) {
-            match args {
-                VecArgs::Vec(vec) => vec.len().try_into().ok(),
-                VecArgs::Repeat(_, len) => expr_as_u128(cx, len),
+    match cx.tcx.get_diagnostic_name(adt.did()) {
+        Some(sym::ArrayIntoIter) => {
+            // 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)
+        },
+        Some(sym::SliceIter) if let ExprKind::MethodCall(_, recv, ..) = iter.kind => {
+            if let ty::Array(_, len) = cx.typeck_results().expr_ty(recv).peel_refs().kind() {
+                // For slice::Iter<'_, T>, the receiver might be an array literal: [1,2,3].iter().skip(..)
+                len.try_to_target_usize(cx.tcx).map(u128::from)
+            } else if let Some(args) = VecArgs::hir(cx, expr_or_init(cx, recv)) {
+                match args {
+                    VecArgs::Vec(vec) => vec.len().try_into().ok(),
+                    VecArgs::Repeat(_, len) => expr_as_u128(cx, len),
+                }
+            } else {
+                None
             }
-        } else {
-            None
-        }
-    } else if cx.tcx.is_diagnostic_item(sym::IterEmpty, did) {
-        Some(0)
-    } else if cx.tcx.is_diagnostic_item(sym::IterOnce, did) {
-        Some(1)
-    } else {
-        None
+        },
+        Some(sym::IterEmpty) => Some(0),
+        Some(sym::IterOnce) => Some(1),
+        _ => None,
     }
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs
index f851ebe91f3..d43dc23a86b 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs
@@ -10,8 +10,7 @@ use rustc_middle::mir::{FakeReadCause, Mutability};
 use rustc_middle::ty::{self, BorrowKind};
 use rustc_span::{Symbol, sym};
 
-use super::ITER_OVEREAGER_CLONED;
-use crate::redundant_clone::REDUNDANT_CLONE;
+use super::{ITER_OVEREAGER_CLONED, REDUNDANT_ITER_CLONED};
 
 #[derive(Clone, Copy)]
 pub(super) enum Op<'a> {
@@ -96,7 +95,7 @@ pub(super) fn check<'tcx>(
         }
 
         let (lint, msg, trailing_clone) = match op {
-            Op::RmCloned | Op::NeedlessMove(_) => (REDUNDANT_CLONE, "unneeded cloning of iterator items", ""),
+            Op::RmCloned | Op::NeedlessMove(_) => (REDUNDANT_ITER_CLONED, "unneeded cloning of iterator items", ""),
             Op::LaterCloned | Op::FixClosure(_, _) => (
                 ITER_OVEREAGER_CLONED,
                 "unnecessarily eager cloning of iterator items",
diff --git a/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs b/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs
index f7bb8c1d696..750f933330a 100644
--- a/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs
@@ -50,10 +50,10 @@ fn try_get_caller_ty_name_and_method_name(
         }
     } else {
         if let ty::Adt(adt, _) = cx.typeck_results().expr_ty(caller_expr).kind() {
-            if cx.tcx.is_diagnostic_item(sym::Option, adt.did()) {
-                return Some(("Option", "and_then"));
-            } else if cx.tcx.is_diagnostic_item(sym::Result, adt.did()) {
-                return Some(("Result", "and_then"));
+            match cx.tcx.get_diagnostic_name(adt.did()) {
+                Some(sym::Option) => return Some(("Option", "and_then")),
+                Some(sym::Result) => return Some(("Result", "and_then")),
+                _ => {},
             }
         }
         None
diff --git a/src/tools/clippy/clippy_lints/src/methods/map_identity.rs b/src/tools/clippy/clippy_lints/src/methods/map_identity.rs
index 98def66ca14..a98cfff8bfb 100644
--- a/src/tools/clippy/clippy_lints/src/methods/map_identity.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/map_identity.rs
@@ -1,14 +1,16 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{is_expr_untyped_identity_function, is_trait_method, path_to_local};
-use rustc_ast::BindingMode;
+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::{is_copy, is_type_diagnostic_item};
+use clippy_utils::{is_expr_untyped_identity_function, is_mutable, is_trait_method, path_to_local_with_projections};
 use rustc_errors::Applicability;
-use rustc_hir::{self as hir, Node, PatKind};
-use rustc_lint::LateContext;
+use rustc_hir::{self as hir, ExprKind, Node, PatKind};
+use rustc_lint::{LateContext, LintContext};
 use rustc_span::{Span, Symbol, sym};
 
 use super::MAP_IDENTITY;
 
+const MSG: &str = "unnecessary map of the identity function";
+
 pub(super) fn check(
     cx: &LateContext<'_>,
     expr: &hir::Expr<'_>,
@@ -23,26 +25,70 @@ pub(super) fn check(
         || is_type_diagnostic_item(cx, caller_ty, sym::Result)
         || is_type_diagnostic_item(cx, caller_ty, sym::Option))
         && is_expr_untyped_identity_function(cx, map_arg)
-        && let Some(sugg_span) = expr.span.trim_start(caller.span)
+        && let Some(call_span) = expr.span.trim_start(caller.span)
     {
-        // If the result of `.map(identity)` is used as a mutable reference,
-        // the caller must not be an immutable binding.
-        if cx.typeck_results().expr_ty_adjusted(expr).is_mutable_ptr()
-            && let Some(hir_id) = path_to_local(caller)
-            && let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
-            && !matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..))
-        {
-            return;
-        }
+        let main_sugg = (call_span, String::new());
+        let mut app = if is_copy(cx, caller_ty) {
+            // there is technically a behavioral change here for `Copy` iterators, where
+            // `iter.map(|x| x).next()` would mutate a temporary copy of the iterator and
+            // changing it to `iter.next()` mutates iter directly
+            Applicability::Unspecified
+        } else {
+            Applicability::MachineApplicable
+        };
+
+        let needs_to_be_mutable = cx.typeck_results().expr_ty_adjusted(expr).is_mutable_ptr();
+        if needs_to_be_mutable && !is_mutable(cx, caller) {
+            if let Some(hir_id) = path_to_local_with_projections(caller)
+                && let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
+                && let PatKind::Binding(_, _, ident, _) = pat.kind
+            {
+                // We can reach the binding -- suggest making it mutable
+                let suggs = vec![main_sugg, (ident.span.shrink_to_lo(), String::from("mut "))];
+
+                let ident = snippet_with_applicability(cx.sess(), ident.span, "_", &mut app);
 
-        span_lint_and_sugg(
-            cx,
-            MAP_IDENTITY,
-            sugg_span,
-            "unnecessary map of the identity function",
-            format!("remove the call to `{name}`"),
-            String::new(),
-            Applicability::MachineApplicable,
-        );
+                span_lint_and_then(cx, MAP_IDENTITY, call_span, MSG, |diag| {
+                    diag.multipart_suggestion(
+                        format!("remove the call to `{name}`, and make `{ident}` mutable"),
+                        suggs,
+                        app,
+                    );
+                });
+            } else {
+                // If we can't make the binding mutable, prevent the suggestion from being automatically applied,
+                // and add a complementary help message.
+                app = Applicability::Unspecified;
+
+                let method_requiring_mut = if let Node::Expr(expr) = cx.tcx.parent_hir_node(expr.hir_id)
+                    && let ExprKind::MethodCall(method, ..) = expr.kind
+                {
+                    Some(method.ident)
+                } else {
+                    None
+                };
+
+                span_lint_and_then(cx, MAP_IDENTITY, call_span, MSG, |diag| {
+                    diag.span_suggestion(main_sugg.0, format!("remove the call to `{name}`"), main_sugg.1, app);
+
+                    let note = if let Some(method_requiring_mut) = method_requiring_mut {
+                        format!("this must be made mutable to use `{method_requiring_mut}`")
+                    } else {
+                        "this must be made mutable".to_string()
+                    };
+                    diag.span_note(caller.span, note);
+                });
+            }
+        } else {
+            span_lint_and_sugg(
+                cx,
+                MAP_IDENTITY,
+                main_sugg.0,
+                MSG,
+                format!("remove the call to `{name}`"),
+                main_sugg.1,
+                app,
+            );
+        }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs
index 49ca81dafc2..8679689c8ad 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs
@@ -91,6 +91,7 @@ mod or_fun_call;
 mod or_then_unwrap;
 mod path_buf_push_overwrite;
 mod path_ends_with_ext;
+mod ptr_offset_with_cast;
 mod range_zip_with_len;
 mod read_line_without_trim;
 mod readonly_write_lock;
@@ -1727,6 +1728,43 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
+    /// Checks for usage of the `offset` pointer method with a `usize` casted to an
+    /// `isize`.
+    ///
+    /// ### Why is this bad?
+    /// If we’re always increasing the pointer address, we can avoid the numeric
+    /// cast by using the `add` method instead.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// let vec = vec![b'a', b'b', b'c'];
+    /// let ptr = vec.as_ptr();
+    /// let offset = 1_usize;
+    ///
+    /// unsafe {
+    ///     ptr.offset(offset as isize);
+    /// }
+    /// ```
+    ///
+    /// Could be written:
+    ///
+    /// ```no_run
+    /// let vec = vec![b'a', b'b', b'c'];
+    /// let ptr = vec.as_ptr();
+    /// let offset = 1_usize;
+    ///
+    /// unsafe {
+    ///     ptr.add(offset);
+    /// }
+    /// ```
+    #[clippy::version = "1.30.0"]
+    pub PTR_OFFSET_WITH_CAST,
+    complexity,
+    "unneeded pointer offset cast"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
     /// Checks for `FileType::is_file()`.
     ///
     /// ### Why restrict this?
@@ -4576,6 +4614,31 @@ declare_clippy_lint! {
     "hardcoded localhost IP address"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for calls to `Iterator::cloned` where the original value could be used
+    /// instead.
+    ///
+    /// ### Why is this bad?
+    /// It is not always possible for the compiler to eliminate useless allocations and
+    /// deallocations generated by redundant `clone()`s.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// let x = vec![String::new()];
+    /// let _ = x.iter().cloned().map(|x| x.len());
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// let x = vec![String::new()];
+    /// let _ = x.iter().map(|x| x.len());
+    /// ```
+    #[clippy::version = "1.90.0"]
+    pub REDUNDANT_ITER_CLONED,
+    perf,
+    "detects redundant calls to `Iterator::cloned`"
+}
+
 #[expect(clippy::struct_excessive_bools)]
 pub struct Methods {
     avoid_breaking_exported_api: bool,
@@ -4665,6 +4728,7 @@ impl_lint_pass!(Methods => [
     UNINIT_ASSUMED_INIT,
     MANUAL_SATURATING_ARITHMETIC,
     ZST_OFFSET,
+    PTR_OFFSET_WITH_CAST,
     FILETYPE_IS_FILE,
     OPTION_AS_REF_DEREF,
     UNNECESSARY_LAZY_EVALUATIONS,
@@ -4755,6 +4819,7 @@ impl_lint_pass!(Methods => [
     IO_OTHER_ERROR,
     SWAP_WITH_TEMPORARY,
     IP_CONSTANT,
+    REDUNDANT_ITER_CLONED,
 ]);
 
 /// Extracts a method call name, args, and `Span` of the method name.
@@ -4960,10 +5025,7 @@ impl Methods {
         // Handle method calls whose receiver and arguments may not come from expansion
         if let Some((name, recv, args, span, call_span)) = method_call(expr) {
             match (name, args) {
-                (
-                    sym::add | sym::offset | sym::sub | sym::wrapping_offset | sym::wrapping_add | sym::wrapping_sub,
-                    [_arg],
-                ) => {
+                (sym::add | sym::sub | sym::wrapping_add | sym::wrapping_sub, [_arg]) => {
                     zst_offset::check(cx, expr, recv);
                 },
                 (sym::all, [arg]) => {
@@ -5334,6 +5396,11 @@ impl Methods {
                     },
                     _ => iter_nth_zero::check(cx, expr, recv, n_arg),
                 },
+                (sym::offset | sym::wrapping_offset, [arg]) => {
+                    zst_offset::check(cx, expr, recv);
+
+                    ptr_offset_with_cast::check(cx, name, expr, recv, arg, self.msrv);
+                },
                 (sym::ok_or_else, [arg]) => {
                     unnecessary_lazy_eval::check(cx, expr, recv, arg, "ok_or");
                 },
diff --git a/src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs b/src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs
index 4235af882b0..9d2c5e6232d 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mut_mutex_lock.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::expr_custom_deref_adjustment;
-use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable};
+use clippy_utils::ty::{is_type_diagnostic_item, peel_and_count_ty_refs};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, Mutability};
 use rustc_lint::LateContext;
@@ -10,8 +10,7 @@ use super::MUT_MUTEX_LOCK;
 
 pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>, recv: &'tcx Expr<'tcx>, name_span: Span) {
     if matches!(expr_custom_deref_adjustment(cx, recv), None | Some(Mutability::Mut))
-        && let (_, ref_depth, Mutability::Mut) = peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(recv))
-        && ref_depth >= 1
+        && let (_, _, Some(Mutability::Mut)) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(recv))
         && let Some(method_id) = cx.typeck_results().type_dependent_def_id(ex.hir_id)
         && let Some(impl_id) = cx.tcx.impl_of_assoc(method_id)
         && is_type_diagnostic_item(cx, cx.tcx.type_of(impl_id).instantiate_identity(), sym::Mutex)
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 63ee922acfa..906ead16fd0 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
@@ -38,17 +38,13 @@ pub(super) fn check(
     ];
 
     let is_deref = match map_arg.kind {
-        hir::ExprKind::Path(ref expr_qpath) => {
-            cx.qpath_res(expr_qpath, map_arg.hir_id)
-                .opt_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
-                            .iter()
-                            .any(|&sym| cx.tcx.is_diagnostic_item(sym, fun_def_id))
-                })
-        },
+        hir::ExprKind::Path(ref expr_qpath) => cx
+            .qpath_res(expr_qpath, map_arg.hir_id)
+            .opt_def_id()
+            .and_then(|fun_def_id| cx.tcx.get_diagnostic_name(fun_def_id))
+            .is_some_and(|fun_name| {
+                matches!(fun_name, sym::deref_method | sym::deref_mut_method) || deref_aliases.contains(&fun_name)
+            }),
         hir::ExprKind::Closure(&hir::Closure { body, .. }) => {
             let closure_body = cx.tcx.hir_body(body);
             let closure_expr = peel_blocks(closure_body.value);
@@ -63,13 +59,11 @@ pub(super) fn check(
                             .map(|x| &x.kind)
                             .collect::<Box<[_]>>()
                         && let [ty::adjustment::Adjust::Deref(None), ty::adjustment::Adjust::Borrow(_)] = *adj
+                        && let method_did = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id).unwrap()
+                        && let Some(method_name) = cx.tcx.get_diagnostic_name(method_did)
                     {
-                        let method_did = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id).unwrap();
-                        cx.tcx.is_diagnostic_item(sym::deref_method, method_did)
-                            || cx.tcx.is_diagnostic_item(sym::deref_mut_method, method_did)
-                            || deref_aliases
-                                .iter()
-                                .any(|&sym| cx.tcx.is_diagnostic_item(sym, method_did))
+                        matches!(method_name, sym::deref_method | sym::deref_mut_method)
+                            || deref_aliases.contains(&method_name)
                     } else {
                         false
                     }
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 04f0e3c0479..71b2f251ede 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
@@ -2,6 +2,7 @@ use std::ops::ControlFlow;
 
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::eager_or_lazy::switch_to_lazy_eval;
+use clippy_utils::higher::VecArgs;
 use clippy_utils::source::snippet_with_context;
 use clippy_utils::ty::{expr_type_is_certain, implements_trait, is_type_diagnostic_item};
 use clippy_utils::visitors::for_each_expr;
@@ -97,6 +98,12 @@ pub(super) fn check<'tcx>(
             return false;
         }
 
+        // `.unwrap_or(vec![])` is as readable as `.unwrap_or_default()`. And if the expression is a
+        // non-empty `Vec`, then it will not be a default value anyway. Bail out in all cases.
+        if call_expr.and_then(|call_expr| VecArgs::hir(cx, call_expr)).is_some() {
+            return false;
+        }
+
         // needs to target Default::default in particular or be *::new and have a Default impl
         // available
         if (is_new(fun) && output_type_implements_default(fun))
diff --git a/src/tools/clippy/clippy_lints/src/methods/or_then_unwrap.rs b/src/tools/clippy/clippy_lints/src/methods/or_then_unwrap.rs
index 3e64e15dc86..1a760ea733d 100644
--- a/src/tools/clippy/clippy_lints/src/methods/or_then_unwrap.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/or_then_unwrap.rs
@@ -62,7 +62,7 @@ fn get_content_if_ctor_matches(cx: &LateContext<'_>, expr: &Expr<'_>, item: Lang
     if let ExprKind::Call(some_expr, [arg]) = expr.kind
         && is_res_lang_ctor(cx, path_res(cx, some_expr), item)
     {
-        Some(arg.span)
+        Some(arg.span.source_callsite())
     } else {
         None
     }
diff --git a/src/tools/clippy/clippy_lints/src/methods/ptr_offset_with_cast.rs b/src/tools/clippy/clippy_lints/src/methods/ptr_offset_with_cast.rs
new file mode 100644
index 00000000000..d19d3b8eb89
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/methods/ptr_offset_with_cast.rs
@@ -0,0 +1,82 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::msrvs::{self, Msrv};
+use clippy_utils::sym;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_span::Symbol;
+use std::fmt;
+
+use super::PTR_OFFSET_WITH_CAST;
+
+pub(super) fn check(
+    cx: &LateContext<'_>,
+    method: Symbol,
+    expr: &Expr<'_>,
+    recv: &Expr<'_>,
+    arg: &Expr<'_>,
+    msrv: Msrv,
+) {
+    // `pointer::add` and `pointer::wrapping_add` are only stable since 1.26.0. These functions
+    // became const-stable in 1.61.0, the same version that `pointer::offset` became const-stable.
+    if !msrv.meets(cx, msrvs::POINTER_ADD_SUB_METHODS) {
+        return;
+    }
+
+    let method = match method {
+        sym::offset => Method::Offset,
+        sym::wrapping_offset => Method::WrappingOffset,
+        _ => return,
+    };
+
+    if !cx.typeck_results().expr_ty_adjusted(recv).is_raw_ptr() {
+        return;
+    }
+
+    // Check if the argument to the method call is a cast from usize.
+    let cast_lhs_expr = match arg.kind {
+        ExprKind::Cast(lhs, _) if cx.typeck_results().expr_ty(lhs).is_usize() => lhs,
+        _ => return,
+    };
+
+    let ExprKind::MethodCall(method_name, _, _, _) = expr.kind else {
+        return;
+    };
+
+    let msg = format!("use of `{method}` with a `usize` casted to an `isize`");
+    span_lint_and_then(cx, PTR_OFFSET_WITH_CAST, expr.span, msg, |diag| {
+        diag.multipart_suggestion(
+            format!("use `{}` instead", method.suggestion()),
+            vec![
+                (method_name.ident.span, method.suggestion().to_string()),
+                (arg.span.with_lo(cast_lhs_expr.span.hi()), String::new()),
+            ],
+            Applicability::MachineApplicable,
+        );
+    });
+}
+
+#[derive(Copy, Clone)]
+enum Method {
+    Offset,
+    WrappingOffset,
+}
+
+impl Method {
+    #[must_use]
+    fn suggestion(self) -> &'static str {
+        match self {
+            Self::Offset => "add",
+            Self::WrappingOffset => "wrapping_add",
+        }
+    }
+}
+
+impl fmt::Display for Method {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            Self::Offset => write!(f, "offset"),
+            Self::WrappingOffset => write!(f, "wrapping_offset"),
+        }
+    }
+}
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 407f2e80aff..6738bbf0a12 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
@@ -31,8 +31,8 @@ fn parse_fails_on_trailing_newline(ty: Ty<'_>) -> bool {
 }
 
 pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<'_>) {
-    if let Some(recv_adt) = cx.typeck_results().expr_ty(recv).ty_adt_def()
-        && cx.tcx.is_diagnostic_item(sym::Stdin, recv_adt.did())
+    let recv_ty = cx.typeck_results().expr_ty(recv);
+    if is_type_diagnostic_item(cx, recv_ty, sym::Stdin)
         && let ExprKind::Path(QPath::Resolved(_, path)) = arg.peel_borrows().kind
         && let Res::Local(local_id) = path.res
     {
diff --git a/src/tools/clippy/clippy_lints/src/methods/single_char_add_str.rs b/src/tools/clippy/clippy_lints/src/methods/single_char_add_str.rs
index ccdf5529d53..ef3d7acdc01 100644
--- a/src/tools/clippy/clippy_lints/src/methods/single_char_add_str.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/single_char_add_str.rs
@@ -5,10 +5,10 @@ use rustc_span::sym;
 
 pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, receiver: &hir::Expr<'_>, args: &[hir::Expr<'_>]) {
     if let Some(fn_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
-        if cx.tcx.is_diagnostic_item(sym::string_push_str, fn_def_id) {
-            single_char_push_string::check(cx, expr, receiver, args);
-        } else if cx.tcx.is_diagnostic_item(sym::string_insert_str, fn_def_id) {
-            single_char_insert_string::check(cx, expr, receiver, args);
+        match cx.tcx.get_diagnostic_name(fn_def_id) {
+            Some(sym::string_push_str) => single_char_push_string::check(cx, expr, receiver, args),
+            Some(sym::string_insert_str) => single_char_insert_string::check(cx, expr, receiver, args),
+            _ => {},
         }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs
index ce7aefed01f..ffc237e3c24 100644
--- a/src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/suspicious_to_owned.rs
@@ -1,11 +1,11 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_diag_trait_item;
 use clippy_utils::source::snippet_with_context;
+use clippy_utils::ty::is_type_diagnostic_item;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
 use rustc_middle::ty::print::with_forced_trimmed_paths;
-use rustc_middle::ty::{self};
 use rustc_span::sym;
 
 use super::SUSPICIOUS_TO_OWNED;
@@ -14,8 +14,7 @@ pub fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) -
     if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
         && is_diag_trait_item(cx, method_def_id, sym::ToOwned)
         && let input_type = cx.typeck_results().expr_ty(expr)
-        && let ty::Adt(adt, _) = cx.typeck_results().expr_ty(expr).kind()
-        && cx.tcx.is_diagnostic_item(sym::Cow, adt.did())
+        && is_type_diagnostic_item(cx, input_type, sym::Cow)
     {
         let mut app = Applicability::MaybeIncorrect;
         let recv_snip = snippet_with_context(cx, recv.span, expr.span.ctxt(), "..", &mut app).0;
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 413881d5ec9..b87d81b7102 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
@@ -22,7 +22,8 @@ pub(super) fn check<'tcx>(
     let typeck_results = cx.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))
+        && let Some(fn_name) = cx.tcx.get_diagnostic_name(id)
+        && matches!(fn_name, sym::cmp_ord_min | sym::cmp_ord_max)
     {
         if let Some((left, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(recv)
             && let Some((right, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(arg)
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 c1f4904af7c..640931a8289 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
@@ -3,11 +3,12 @@ use super::unnecessary_iter_cloned::{self, is_into_iter};
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::{SpanRangeExt, snippet};
-use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item};
+use clippy_utils::ty::{
+    get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item, peel_and_count_ty_refs,
+};
 use clippy_utils::visitors::find_all_ret_expressions;
 use clippy_utils::{
-    fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, is_expr_temporary_value, peel_middle_ty_refs,
-    return_ty, sym,
+    fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, is_expr_temporary_value, return_ty, sym,
 };
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
@@ -119,8 +120,8 @@ fn check_addr_of_expr(
                 },
             ] = adjustments[..]
         && let receiver_ty = cx.typeck_results().expr_ty(receiver)
-        && let (target_ty, n_target_refs) = peel_middle_ty_refs(*target_ty)
-        && let (receiver_ty, n_receiver_refs) = peel_middle_ty_refs(receiver_ty)
+        && let (target_ty, n_target_refs, _) = peel_and_count_ty_refs(*target_ty)
+        && let (receiver_ty, n_receiver_refs, _) = peel_and_count_ty_refs(receiver_ty)
         // Only flag cases satisfying at least one of the following three conditions:
         // * the referent and receiver types are distinct
         // * the referent/receiver type is a copyable array
@@ -385,7 +386,7 @@ fn check_other_call_arg<'tcx>(
         && let fn_sig = cx.tcx.fn_sig(callee_def_id).instantiate_identity().skip_binder()
         && let Some(i) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == maybe_arg.hir_id)
         && let Some(input) = fn_sig.inputs().get(i)
-        && let (input, n_refs) = peel_middle_ty_refs(*input)
+        && let (input, n_refs, _) = peel_and_count_ty_refs(*input)
         && let (trait_predicates, _) = get_input_traits_and_projections(cx, callee_def_id, input)
         && let Some(sized_def_id) = cx.tcx.lang_items().sized_trait()
         && let Some(meta_sized_def_id) = cx.tcx.lang_items().meta_sized_trait()
diff --git a/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs b/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs
index af466fe091c..af4ade3cc0f 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unused_enumerate_index.rs
@@ -1,10 +1,10 @@
 use clippy_utils::diagnostics::span_lint_hir_and_then;
 use clippy_utils::source::{SpanRangeExt, snippet};
+use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::{expr_or_init, is_trait_method, pat_is_wild};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, FnDecl, PatKind, TyKind};
 use rustc_lint::LateContext;
-use rustc_middle::ty::AdtDef;
 use rustc_span::{Span, sym};
 
 use crate::loops::UNUSED_ENUMERATE_INDEX;
@@ -39,9 +39,8 @@ use crate::loops::UNUSED_ENUMERATE_INDEX;
 /// * `closure_arg`: The argument to the map function call containing the closure/function to apply
 pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>, closure_arg: &Expr<'_>) {
     let recv_ty = cx.typeck_results().expr_ty(recv);
-    if let Some(recv_ty_defid) = recv_ty.ty_adt_def().map(AdtDef::did)
-        // If we call a method on a `std::iter::Enumerate` instance
-        && cx.tcx.is_diagnostic_item(sym::Enumerate, recv_ty_defid)
+    // If we call a method on a `std::iter::Enumerate` instance
+    if is_type_diagnostic_item(cx, recv_ty, sym::Enumerate)
         // If we are calling a method of the `Iterator` trait
         && is_trait_method(cx, call_expr, sym::Iterator)
         // And the map argument is a closure
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 38fad239f67..e56f4b80d01 100644
--- a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::ty::{implements_trait, should_call_clone_as_function, walk_ptrs_ty_depth};
+use clippy_utils::ty::{implements_trait, peel_and_count_ty_refs, should_call_clone_as_function};
 use clippy_utils::{get_parent_expr, is_diag_trait_item, path_to_local_id, peel_blocks, strip_pat_refs};
 use rustc_errors::Applicability;
 use rustc_hir::{self as hir, LangItem};
@@ -50,8 +50,8 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: Symbo
         // check if the type after `as_ref` or `as_mut` is the same as before
         let rcv_ty = cx.typeck_results().expr_ty(recvr);
         let res_ty = cx.typeck_results().expr_ty(expr);
-        let (base_res_ty, res_depth) = walk_ptrs_ty_depth(res_ty);
-        let (base_rcv_ty, rcv_depth) = walk_ptrs_ty_depth(rcv_ty);
+        let (base_res_ty, res_depth, _) = peel_and_count_ty_refs(res_ty);
+        let (base_rcv_ty, rcv_depth, _) = peel_and_count_ty_refs(rcv_ty);
         if base_rcv_ty == base_res_ty && rcv_depth >= res_depth {
             if let Some(parent) = get_parent_expr(cx, expr) {
                 // allow the `as_ref` or `as_mut` if it is followed by another method call
diff --git a/src/tools/clippy/clippy_lints/src/min_ident_chars.rs b/src/tools/clippy/clippy_lints/src/min_ident_chars.rs
index dbce29a8631..1ceee836732 100644
--- a/src/tools/clippy/clippy_lints/src/min_ident_chars.rs
+++ b/src/tools/clippy/clippy_lints/src/min_ident_chars.rs
@@ -5,8 +5,8 @@ use rustc_data_structures::fx::FxHashSet;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::intravisit::{Visitor, walk_item, walk_trait_item};
 use rustc_hir::{
-    GenericParamKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, ItemLocalId, Node, Pat, PatKind, TraitItem,
-    UsePath,
+    GenericParamKind, HirId, Impl, ImplItem, ImplItemImplKind, ImplItemKind, Item, ItemKind, ItemLocalId, Node, Pat,
+    PatKind, TraitItem, UsePath,
 };
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_session::impl_lint_pass;
@@ -256,7 +256,11 @@ fn is_not_in_trait_impl(cx: &LateContext<'_>, pat: &Pat<'_>, ident: Ident) -> bo
 }
 
 fn get_param_name(impl_item: &ImplItem<'_>, cx: &LateContext<'_>, ident: Ident) -> Option<Symbol> {
-    if let Some(trait_item_def_id) = impl_item.trait_item_def_id {
+    if let ImplItemImplKind::Trait {
+        trait_item_def_id: Ok(trait_item_def_id),
+        ..
+    } = impl_item.impl_kind
+    {
         let trait_param_names = cx.tcx.fn_arg_idents(trait_item_def_id);
 
         let ImplItemKind::Fn(_, body_id) = impl_item.kind else {
diff --git a/src/tools/clippy/clippy_lints/src/misc.rs b/src/tools/clippy/clippy_lints/src/misc.rs
index 09ee6f7037c..19e9910dfe9 100644
--- a/src/tools/clippy/clippy_lints/src/misc.rs
+++ b/src/tools/clippy/clippy_lints/src/misc.rs
@@ -1,57 +1,11 @@
-use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir, span_lint_hir_and_then};
-use clippy_utils::source::{snippet, snippet_with_context};
+use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
 use clippy_utils::sugg::Sugg;
-use clippy_utils::{
-    SpanlessEq, fulfill_or_allowed, get_parent_expr, in_automatically_derived, is_lint_allowed, iter_input_pats,
-    last_path_segment,
-};
+use clippy_utils::{SpanlessEq, fulfill_or_allowed, get_parent_expr, in_automatically_derived, last_path_segment};
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
-use rustc_hir::intravisit::FnKind;
-use rustc_hir::{
-    BinOpKind, BindingMode, Body, ByRef, Expr, ExprKind, FnDecl, Mutability, PatKind, QPath, Stmt, StmtKind,
-};
+use rustc_hir::{BinOpKind, Expr, ExprKind, QPath, Stmt, StmtKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_session::declare_lint_pass;
-use rustc_span::Span;
-use rustc_span::def_id::LocalDefId;
-
-use crate::ref_patterns::REF_PATTERNS;
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for function arguments and let bindings denoted as
-    /// `ref`.
-    ///
-    /// ### Why is this bad?
-    /// The `ref` declaration makes the function take an owned
-    /// value, but turns the argument into a reference (which means that the value
-    /// is destroyed when exiting the function). This adds not much value: either
-    /// take a reference type, or take an owned value and create references in the
-    /// body.
-    ///
-    /// For let bindings, `let x = &foo;` is preferred over `let ref x = foo`. The
-    /// type of `x` is more obvious with the former.
-    ///
-    /// ### Known problems
-    /// If the argument is dereferenced within the function,
-    /// removing the `ref` will lead to errors. This can be fixed by removing the
-    /// dereferences, e.g., changing `*x` to `x` within the function.
-    ///
-    /// ### Example
-    /// ```no_run
-    /// fn foo(ref _x: u8) {}
-    /// ```
-    ///
-    /// Use instead:
-    /// ```no_run
-    /// fn foo(_x: &u8) {}
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub TOPLEVEL_REF_ARG,
-    style,
-    "an entire binding declared as `ref`, in a function argument or a `let` statement"
-}
 
 declare_clippy_lint! {
     /// ### What it does
@@ -140,79 +94,13 @@ declare_clippy_lint! {
 }
 
 declare_lint_pass!(LintPass => [
-    TOPLEVEL_REF_ARG,
     USED_UNDERSCORE_BINDING,
     USED_UNDERSCORE_ITEMS,
     SHORT_CIRCUIT_STATEMENT,
 ]);
 
 impl<'tcx> LateLintPass<'tcx> for LintPass {
-    fn check_fn(
-        &mut self,
-        cx: &LateContext<'tcx>,
-        k: FnKind<'tcx>,
-        decl: &'tcx FnDecl<'_>,
-        body: &'tcx Body<'_>,
-        _: Span,
-        _: LocalDefId,
-    ) {
-        if !matches!(k, FnKind::Closure) {
-            for arg in iter_input_pats(decl, body) {
-                if let PatKind::Binding(BindingMode(ByRef::Yes(_), _), ..) = arg.pat.kind
-                    && is_lint_allowed(cx, REF_PATTERNS, arg.pat.hir_id)
-                    && !arg.span.in_external_macro(cx.tcx.sess.source_map())
-                {
-                    span_lint_hir(
-                        cx,
-                        TOPLEVEL_REF_ARG,
-                        arg.hir_id,
-                        arg.pat.span,
-                        "`ref` directly on a function parameter does not prevent taking ownership of the passed argument. \
-                        Consider using a reference type instead",
-                    );
-                }
-            }
-        }
-    }
-
     fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
-        if let StmtKind::Let(local) = stmt.kind
-            && let PatKind::Binding(BindingMode(ByRef::Yes(mutabl), _), .., name, None) = local.pat.kind
-            && let Some(init) = local.init
-            // Do not emit if clippy::ref_patterns is not allowed to avoid having two lints for the same issue.
-            && is_lint_allowed(cx, REF_PATTERNS, local.pat.hir_id)
-            && !stmt.span.in_external_macro(cx.tcx.sess.source_map())
-        {
-            let ctxt = local.span.ctxt();
-            let mut app = Applicability::MachineApplicable;
-            let sugg_init = Sugg::hir_with_context(cx, init, ctxt, "..", &mut app);
-            let (mutopt, initref) = if mutabl == Mutability::Mut {
-                ("mut ", sugg_init.mut_addr())
-            } else {
-                ("", sugg_init.addr())
-            };
-            let tyopt = if let Some(ty) = local.ty {
-                let ty_snip = snippet_with_context(cx, ty.span, ctxt, "_", &mut app).0;
-                format!(": &{mutopt}{ty_snip}")
-            } else {
-                String::new()
-            };
-            span_lint_hir_and_then(
-                cx,
-                TOPLEVEL_REF_ARG,
-                init.hir_id,
-                local.pat.span,
-                "`ref` on an entire `let` pattern is discouraged, take a reference with `&` instead",
-                |diag| {
-                    diag.span_suggestion(
-                        stmt.span,
-                        "try",
-                        format!("let {name}{tyopt} = {initref};", name = snippet(cx, name.span, ".."),),
-                        app,
-                    );
-                },
-            );
-        }
         if let StmtKind::Semi(expr) = stmt.kind
             && let ExprKind::Binary(binop, a, b) = &expr.kind
             && matches!(binop.node, BinOpKind::And | BinOpKind::Or)
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 f880f1f329f..f988323a8c1 100644
--- a/src/tools/clippy/clippy_lints/src/misc_early/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/misc_early/mod.rs
@@ -7,12 +7,9 @@ mod unneeded_field_pattern;
 mod unneeded_wildcard_pattern;
 mod zero_prefixed_literal;
 
-use clippy_utils::diagnostics::span_lint;
 use clippy_utils::source::snippet_opt;
-use rustc_ast::ast::{Expr, ExprKind, Generics, LitFloatType, LitIntType, LitKind, NodeId, Pat, PatKind};
+use rustc_ast::ast::{Expr, ExprKind, Generics, LitFloatType, LitIntType, LitKind, Pat};
 use rustc_ast::token;
-use rustc_ast::visit::FnKind;
-use rustc_data_structures::fx::FxHashMap;
 use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
 use rustc_session::declare_lint_pass;
 use rustc_span::Span;
@@ -62,29 +59,6 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for function arguments having the similar names
-    /// differing by an underscore.
-    ///
-    /// ### Why is this bad?
-    /// It affects code readability.
-    ///
-    /// ### Example
-    /// ```no_run
-    /// fn foo(a: i32, _a: i32) {}
-    /// ```
-    ///
-    /// Use instead:
-    /// ```no_run
-    /// fn bar(a: i32, _b: i32) {}
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub DUPLICATE_UNDERSCORE_ARGUMENT,
-    style,
-    "function arguments having names which only differ by an underscore"
-}
-
-declare_clippy_lint! {
-    /// ### What it does
     /// Warns on hexadecimal literals with mixed-case letter
     /// digits.
     ///
@@ -330,7 +304,6 @@ declare_clippy_lint! {
 
 declare_lint_pass!(MiscEarlyLints => [
     UNNEEDED_FIELD_PATTERN,
-    DUPLICATE_UNDERSCORE_ARGUMENT,
     MIXED_CASE_HEX_LITERALS,
     UNSEPARATED_LITERAL_SUFFIX,
     SEPARATED_LITERAL_SUFFIX,
@@ -359,32 +332,6 @@ impl EarlyLintPass for MiscEarlyLints {
         unneeded_wildcard_pattern::check(cx, pat);
     }
 
-    fn check_fn(&mut self, cx: &EarlyContext<'_>, fn_kind: FnKind<'_>, _: Span, _: NodeId) {
-        let mut registered_names: FxHashMap<String, Span> = FxHashMap::default();
-
-        for arg in &fn_kind.decl().inputs {
-            if let PatKind::Ident(_, ident, None) = arg.pat.kind {
-                let arg_name = ident.to_string();
-
-                if let Some(arg_name) = arg_name.strip_prefix('_') {
-                    if let Some(correspondence) = registered_names.get(arg_name) {
-                        span_lint(
-                            cx,
-                            DUPLICATE_UNDERSCORE_ARGUMENT,
-                            *correspondence,
-                            format!(
-                                "`{arg_name}` already exists, having another argument having almost the same \
-                                 name makes code comprehension and documentation more difficult"
-                            ),
-                        );
-                    }
-                } else {
-                    registered_names.insert(arg_name, arg.pat.span);
-                }
-            }
-        }
-    }
-
     fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
         if expr.span.in_external_macro(cx.sess().source_map()) {
             return;
@@ -404,7 +351,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().is_some_and(|c| c.is_ascii_digit()) => snip,
+            Some(snip) if snip.starts_with(|c: char| c.is_ascii_digit()) => snip,
             _ => return,
         };
 
diff --git a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
index a6be7581c9a..a63ad978626 100644
--- a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
@@ -158,13 +158,23 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
         let mir = cx.tcx.optimized_mir(def_id);
 
         if let Ok(()) = is_min_const_fn(cx, mir, self.msrv)
-            && let hir::Node::Item(hir::Item { vis_span, .. }) | hir::Node::ImplItem(hir::ImplItem { vis_span, .. }) =
-                cx.tcx.hir_node_by_def_id(def_id)
+            && let node = cx.tcx.hir_node_by_def_id(def_id)
+            && let Some((item_span, vis_span_opt)) = match node {
+                hir::Node::Item(item) => Some((item.span, Some(item.vis_span))),
+                hir::Node::ImplItem(impl_item) => Some((impl_item.span, impl_item.vis_span())),
+                _ => None,
+            }
         {
-            let suggestion = if vis_span.is_empty() { "const " } else { " const" };
+            let (sugg_span, suggestion) = if let Some(vis_span) = vis_span_opt
+                && !vis_span.is_empty()
+            {
+                (vis_span.shrink_to_hi(), " const")
+            } else {
+                (item_span.shrink_to_lo(), "const ")
+            };
             span_lint_and_then(cx, MISSING_CONST_FOR_FN, span, "this could be a `const fn`", |diag| {
                 diag.span_suggestion_verbose(
-                    vis_span.shrink_to_hi(),
+                    sugg_span,
                     "make the function `const`",
                     suggestion,
                     Applicability::MachineApplicable,
diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs
index 7772051eb5c..1c62caa1c82 100644
--- a/src/tools/clippy/clippy_lints/src/missing_doc.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs
@@ -16,7 +16,7 @@ use rustc_hir::Attribute;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::LocalDefId;
 use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_middle::ty::Visibility;
+use rustc_middle::ty::{AssocContainer, Visibility};
 use rustc_session::impl_lint_pass;
 use rustc_span::def_id::CRATE_DEF_ID;
 use rustc_span::symbol::kw;
@@ -246,12 +246,11 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
 
     fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
         // If the method is an impl for a trait, don't doc.
-        if let Some(cid) = cx.tcx.associated_item(impl_item.owner_id).impl_container(cx.tcx) {
-            if cx.tcx.impl_trait_ref(cid).is_some() {
+        match cx.tcx.associated_item(impl_item.owner_id).container {
+            AssocContainer::Trait | AssocContainer::TraitImpl(_) => {
                 note_prev_span_then_ret!(self.prev_span, impl_item.span);
-            }
-        } else {
-            note_prev_span_then_ret!(self.prev_span, impl_item.span);
+            },
+            AssocContainer::InherentImpl => {},
         }
 
         let (article, desc) = cx.tcx.article_and_description(impl_item.owner_id.to_def_id());
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 18e2b384a46..8822b32b1c3 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
@@ -198,8 +198,8 @@ fn check_struct<'tcx>(
 impl<'tcx> LateLintPass<'tcx> for MissingFieldsInDebug {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
         // is this an `impl Debug for X` block?
-        if let ItemKind::Impl(Impl { of_trait: Some(trait_ref), self_ty, .. }) = item.kind
-            && let Res::Def(DefKind::Trait, trait_def_id) = trait_ref.path.res
+        if let ItemKind::Impl(Impl { of_trait: Some(of_trait), self_ty, .. }) = item.kind
+            && let Res::Def(DefKind::Trait, trait_def_id) = of_trait.trait_ref.path.res
             && let TyKind::Path(QPath::Resolved(_, self_path)) = &self_ty.kind
             // make sure that the self type is either a struct, an enum or a union
             // this prevents ICEs such as when self is a type parameter or a primitive type
diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs
index d02952eb487..6323e728666 100644
--- a/src/tools/clippy/clippy_lints/src/missing_inline.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs
@@ -1,9 +1,9 @@
-use clippy_utils::diagnostics::span_lint;
+use clippy_utils::diagnostics::{span_lint, span_lint_hir};
 use rustc_hir::attrs::AttributeKind;
 use rustc_hir::def_id::DefId;
 use rustc_hir::{self as hir, Attribute, find_attr};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_middle::ty::AssocItemContainer;
+use rustc_middle::ty::AssocContainer;
 use rustc_session::declare_lint_pass;
 use rustc_span::Span;
 
@@ -64,14 +64,20 @@ declare_clippy_lint! {
     "detects missing `#[inline]` attribute for public callables (functions, trait methods, methods...)"
 }
 
-fn check_missing_inline_attrs(cx: &LateContext<'_>, attrs: &[Attribute], sp: Span, desc: &'static str) {
+fn check_missing_inline_attrs(
+    cx: &LateContext<'_>,
+    attrs: &[Attribute],
+    sp: Span,
+    desc: &'static str,
+    hir_id: Option<hir::HirId>,
+) {
     if !find_attr!(attrs, AttributeKind::Inline(..)) {
-        span_lint(
-            cx,
-            MISSING_INLINE_IN_PUBLIC_ITEMS,
-            sp,
-            format!("missing `#[inline]` for {desc}"),
-        );
+        let msg = format!("missing `#[inline]` for {desc}");
+        if let Some(hir_id) = hir_id {
+            span_lint_hir(cx, MISSING_INLINE_IN_PUBLIC_ITEMS, hir_id, sp, msg);
+        } else {
+            span_lint(cx, MISSING_INLINE_IN_PUBLIC_ITEMS, sp, msg);
+        }
     }
 }
 
@@ -103,17 +109,9 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline {
 
                 let desc = "a function";
                 let attrs = cx.tcx.hir_attrs(it.hir_id());
-                check_missing_inline_attrs(cx, attrs, it.span, desc);
+                check_missing_inline_attrs(cx, attrs, it.span, desc, None);
             },
-            hir::ItemKind::Trait(
-                ref _constness,
-                ref _is_auto,
-                ref _unsafe,
-                _ident,
-                _generics,
-                _bounds,
-                trait_items,
-            ) => {
+            hir::ItemKind::Trait(.., trait_items) => {
                 // note: we need to check if the trait is exported so we can't use
                 // `LateLintPass::check_trait_item` here.
                 for &tit in trait_items {
@@ -127,7 +125,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline {
                                 let desc = "a default trait method";
                                 let item = cx.tcx.hir_trait_item(tit);
                                 let attrs = cx.tcx.hir_attrs(item.hir_id());
-                                check_missing_inline_attrs(cx, attrs, item.span, desc);
+                                check_missing_inline_attrs(cx, attrs, item.span, desc, Some(tit.hir_id()));
                             }
                         },
                     }
@@ -168,8 +166,9 @@ 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 {
-            AssocItemContainer::Trait => Some(container_id),
-            AssocItemContainer::Impl => cx.tcx.impl_trait_ref(container_id).map(|t| t.skip_binder().def_id),
+            AssocContainer::Trait => Some(container_id),
+            AssocContainer::TraitImpl(_) => cx.tcx.impl_trait_ref(container_id).map(|t| t.skip_binder().def_id),
+            AssocContainer::InherentImpl => None,
         };
 
         if let Some(trait_def_id) = trait_def_id
@@ -182,7 +181,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline {
         }
 
         let attrs = cx.tcx.hir_attrs(impl_item.hir_id());
-        check_missing_inline_attrs(cx, attrs, impl_item.span, desc);
+        check_missing_inline_attrs(cx, attrs, impl_item.span, desc, None);
     }
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs b/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs
index 399bf4e1806..8e9400e9d58 100644
--- a/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs
@@ -61,16 +61,16 @@ impl<'tcx> LateLintPass<'tcx> for MissingTraitMethods {
         if !is_lint_allowed(cx, MISSING_TRAIT_METHODS, item.hir_id())
             && span_is_local(item.span)
             && let ItemKind::Impl(Impl {
-                of_trait: Some(trait_ref),
+                of_trait: Some(of_trait),
                 ..
             }) = item.kind
-            && let Some(trait_id) = trait_ref.trait_def_id()
+            && let Some(trait_id) = of_trait.trait_ref.trait_def_id()
         {
             let trait_item_ids: DefIdSet = cx
                 .tcx
                 .associated_items(item.owner_id)
                 .in_definition_order()
-                .filter_map(|assoc_item| assoc_item.trait_item_def_id)
+                .filter_map(|assoc_item| assoc_item.expect_trait_impl().ok())
                 .collect();
 
             for assoc in cx
diff --git a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs
index a489c0a4a5a..3b44d4b60d3 100644
--- a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs
+++ b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs
@@ -134,7 +134,7 @@ impl<'tcx> DivergenceVisitor<'_, 'tcx> {
         }
     }
 
-    fn report_diverging_sub_expr(&mut self, e: &Expr<'_>) {
+    fn report_diverging_sub_expr(&self, e: &Expr<'_>) {
         if let Some(macro_call) = root_macro_call_first_node(self.cx, e)
             && self.cx.tcx.is_diagnostic_item(sym::todo_macro, macro_call.def_id)
         {
diff --git a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs
index c6c27e22b90..bc5e72270f4 100644
--- a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs
+++ b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs
@@ -1,3 +1,4 @@
+use clippy_utils::desugar_await;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::visitors::{Descend, Visitable, for_each_expr};
 use core::ops::ControlFlow::Continue;
@@ -97,6 +98,13 @@ fn collect_unsafe_exprs<'tcx>(
 ) {
     for_each_expr(cx, node, |expr| {
         match expr.kind {
+            // The `await` itself will desugar to two unsafe calls, but we should ignore those.
+            // Instead, check the expression that is `await`ed
+            _ if let Some(e) = desugar_await(expr) => {
+                collect_unsafe_exprs(cx, e, unsafe_ops);
+                return Continue(Descend::No);
+            },
+
             ExprKind::InlineAsm(_) => unsafe_ops.push(("inline assembly used here", expr.span)),
 
             ExprKind::Field(e, _) => {
diff --git a/src/tools/clippy/clippy_lints/src/mut_reference.rs b/src/tools/clippy/clippy_lints/src/mut_reference.rs
index 31f51b45754..ec93ef97cfa 100644
--- a/src/tools/clippy/clippy_lints/src/mut_reference.rs
+++ b/src/tools/clippy/clippy_lints/src/mut_reference.rs
@@ -1,4 +1,6 @@
-use clippy_utils::diagnostics::span_lint;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::sugg::Sugg;
+use rustc_errors::Applicability;
 use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::{self, Ty};
@@ -83,13 +85,18 @@ fn check_arguments<'tcx>(
         let parameters = type_definition.fn_sig(cx.tcx).skip_binder().inputs();
         for (argument, parameter) in iter::zip(arguments, parameters) {
             if let ty::Ref(_, _, Mutability::Not) | ty::RawPtr(_, Mutability::Not) = parameter.kind()
-                && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _) = argument.kind
+                && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, arg) = argument.kind
             {
-                span_lint(
+                let mut applicability = Applicability::MachineApplicable;
+                let sugg = Sugg::hir_with_applicability(cx, arg, "_", &mut applicability).addr();
+                span_lint_and_sugg(
                     cx,
                     UNNECESSARY_MUT_PASSED,
                     argument.span,
                     format!("the {fn_kind} `{name}` doesn't need a mutable reference"),
+                    "remove this `mut`",
+                    sugg.to_string(),
+                    applicability,
                 );
             }
         }
diff --git a/src/tools/clippy/clippy_lints/src/needless_bool.rs b/src/tools/clippy/clippy_lints/src/needless_bool.rs
index fa5afcc0087..854e927aa2f 100644
--- a/src/tools/clippy/clippy_lints/src/needless_bool.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_bool.rs
@@ -2,16 +2,14 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::{
-    SpanlessEq, get_parent_expr, higher, is_block_like, is_else_clause, is_expn_of, is_parent_stmt,
-    is_receiver_of_method_call, peel_blocks, peel_blocks_with_stmt, span_contains_comment, sym,
+    SpanlessEq, get_parent_expr, higher, is_block_like, is_else_clause, is_parent_stmt, is_receiver_of_method_call,
+    peel_blocks, peel_blocks_with_stmt, span_contains_comment,
 };
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
-use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
+use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
-use rustc_span::Span;
-use rustc_span::source_map::Spanned;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -52,31 +50,6 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for expressions of the form `x == true`,
-    /// `x != true` and order comparisons such as `x < true` (or vice versa) and
-    /// suggest using the variable directly.
-    ///
-    /// ### Why is this bad?
-    /// Unnecessary code.
-    ///
-    /// ### Example
-    /// ```rust,ignore
-    /// if x == true {}
-    /// if y == false {}
-    /// ```
-    /// use `x` directly:
-    /// ```rust,ignore
-    /// if x {}
-    /// if !y {}
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub BOOL_COMPARISON,
-    complexity,
-    "comparing a variable to a boolean, e.g., `if x == true` or `if x != true`"
-}
-
-declare_clippy_lint! {
-    /// ### What it does
     /// Checks for expressions of the form `if c { x = true } else { x = false }`
     /// (or vice versa) and suggest assigning the variable directly from the
     /// condition.
@@ -166,7 +139,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool {
                     applicability,
                 );
             };
-            if let Some((a, b)) = fetch_bool_block(then).and_then(|a| Some((a, fetch_bool_block(else_expr)?))) {
+            if let Some(a) = fetch_bool_block(then)
+                && let Some(b) = fetch_bool_block(else_expr)
+            {
                 match (a, b) {
                     (RetBool(true), RetBool(true)) | (Bool(true), Bool(true)) => {
                         span_lint(
@@ -222,201 +197,6 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool {
     }
 }
 
-declare_lint_pass!(BoolComparison => [BOOL_COMPARISON]);
-
-impl<'tcx> LateLintPass<'tcx> for BoolComparison {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
-        if e.span.from_expansion() {
-            return;
-        }
-
-        if let ExprKind::Binary(Spanned { node, .. }, ..) = e.kind {
-            let ignore_case = None::<(fn(_) -> _, &str)>;
-            let ignore_no_literal = None::<(fn(_, _) -> _, &str)>;
-            match node {
-                BinOpKind::Eq => {
-                    let true_case = Some((|h| h, "equality checks against true are unnecessary"));
-                    let false_case = Some((
-                        |h: Sugg<'tcx>| !h,
-                        "equality checks against false can be replaced by a negation",
-                    ));
-                    check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal);
-                },
-                BinOpKind::Ne => {
-                    let true_case = Some((
-                        |h: Sugg<'tcx>| !h,
-                        "inequality checks against true can be replaced by a negation",
-                    ));
-                    let false_case = Some((|h| h, "inequality checks against false are unnecessary"));
-                    check_comparison(cx, e, true_case, false_case, true_case, false_case, ignore_no_literal);
-                },
-                BinOpKind::Lt => check_comparison(
-                    cx,
-                    e,
-                    ignore_case,
-                    Some((|h| h, "greater than checks against false are unnecessary")),
-                    Some((
-                        |h: Sugg<'tcx>| !h,
-                        "less than comparison against true can be replaced by a negation",
-                    )),
-                    ignore_case,
-                    Some((
-                        |l: Sugg<'tcx>, r: Sugg<'tcx>| (!l).bit_and(&r),
-                        "order comparisons between booleans can be simplified",
-                    )),
-                ),
-                BinOpKind::Gt => check_comparison(
-                    cx,
-                    e,
-                    Some((
-                        |h: Sugg<'tcx>| !h,
-                        "less than comparison against true can be replaced by a negation",
-                    )),
-                    ignore_case,
-                    ignore_case,
-                    Some((|h| h, "greater than checks against false are unnecessary")),
-                    Some((
-                        |l: Sugg<'tcx>, r: Sugg<'tcx>| l.bit_and(&(!r)),
-                        "order comparisons between booleans can be simplified",
-                    )),
-                ),
-                _ => (),
-            }
-        }
-    }
-}
-
-struct ExpressionInfoWithSpan {
-    one_side_is_unary_not: bool,
-    left_span: Span,
-    right_span: Span,
-}
-
-fn is_unary_not(e: &Expr<'_>) -> (bool, Span) {
-    if let ExprKind::Unary(UnOp::Not, operand) = e.kind {
-        return (true, operand.span);
-    }
-    (false, e.span)
-}
-
-fn one_side_is_unary_not<'tcx>(left_side: &'tcx Expr<'_>, right_side: &'tcx Expr<'_>) -> ExpressionInfoWithSpan {
-    let left = is_unary_not(left_side);
-    let right = is_unary_not(right_side);
-
-    ExpressionInfoWithSpan {
-        one_side_is_unary_not: left.0 != right.0,
-        left_span: left.1,
-        right_span: right.1,
-    }
-}
-
-fn check_comparison<'a, 'tcx>(
-    cx: &LateContext<'tcx>,
-    e: &'tcx Expr<'_>,
-    left_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>,
-    left_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>,
-    right_true: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>,
-    right_false: Option<(impl FnOnce(Sugg<'a>) -> Sugg<'a>, &'static str)>,
-    no_literal: Option<(impl FnOnce(Sugg<'a>, Sugg<'a>) -> Sugg<'a>, &'static str)>,
-) {
-    if let ExprKind::Binary(op, left_side, right_side) = e.kind {
-        let (l_ty, r_ty) = (
-            cx.typeck_results().expr_ty(left_side),
-            cx.typeck_results().expr_ty(right_side),
-        );
-        if is_expn_of(left_side.span, sym::cfg).is_some() || is_expn_of(right_side.span, sym::cfg).is_some() {
-            return;
-        }
-        if l_ty.is_bool() && r_ty.is_bool() {
-            let mut applicability = Applicability::MachineApplicable;
-            // Eliminate parentheses in `e` by using the lo pos of lhs and hi pos of rhs,
-            // calling `source_callsite` make sure macros are handled correctly, see issue #9907
-            let binop_span = left_side
-                .span
-                .source_callsite()
-                .with_hi(right_side.span.source_callsite().hi());
-
-            if op.node == BinOpKind::Eq {
-                let expression_info = one_side_is_unary_not(left_side, right_side);
-                if expression_info.one_side_is_unary_not {
-                    span_lint_and_sugg(
-                        cx,
-                        BOOL_COMPARISON,
-                        binop_span,
-                        "this comparison might be written more concisely",
-                        "try simplifying it as shown",
-                        format!(
-                            "{} != {}",
-                            snippet_with_applicability(
-                                cx,
-                                expression_info.left_span.source_callsite(),
-                                "..",
-                                &mut applicability
-                            ),
-                            snippet_with_applicability(
-                                cx,
-                                expression_info.right_span.source_callsite(),
-                                "..",
-                                &mut applicability
-                            )
-                        ),
-                        applicability,
-                    );
-                }
-            }
-
-            match (fetch_bool_expr(left_side), fetch_bool_expr(right_side)) {
-                (Some(true), None) => left_true.map_or((), |(h, m)| {
-                    suggest_bool_comparison(cx, binop_span, right_side, applicability, m, h);
-                }),
-                (None, Some(true)) => right_true.map_or((), |(h, m)| {
-                    suggest_bool_comparison(cx, binop_span, left_side, applicability, m, h);
-                }),
-                (Some(false), None) => left_false.map_or((), |(h, m)| {
-                    suggest_bool_comparison(cx, binop_span, right_side, applicability, m, h);
-                }),
-                (None, Some(false)) => right_false.map_or((), |(h, m)| {
-                    suggest_bool_comparison(cx, binop_span, left_side, applicability, m, h);
-                }),
-                (None, None) => no_literal.map_or((), |(h, m)| {
-                    let left_side = Sugg::hir_with_applicability(cx, left_side, "..", &mut applicability);
-                    let right_side = Sugg::hir_with_applicability(cx, right_side, "..", &mut applicability);
-                    span_lint_and_sugg(
-                        cx,
-                        BOOL_COMPARISON,
-                        binop_span,
-                        m,
-                        "try simplifying it as shown",
-                        h(left_side, right_side).into_string(),
-                        applicability,
-                    );
-                }),
-                _ => (),
-            }
-        }
-    }
-}
-
-fn suggest_bool_comparison<'a, 'tcx>(
-    cx: &LateContext<'tcx>,
-    span: Span,
-    expr: &Expr<'_>,
-    mut app: Applicability,
-    message: &'static str,
-    conv_hint: impl FnOnce(Sugg<'a>) -> Sugg<'a>,
-) {
-    let hint = Sugg::hir_with_context(cx, expr, span.ctxt(), "..", &mut app);
-    span_lint_and_sugg(
-        cx,
-        BOOL_COMPARISON,
-        span,
-        message,
-        "try simplifying it as shown",
-        conv_hint(hint).into_string(),
-        app,
-    );
-}
-
 enum Expression {
     Bool(bool),
     RetBool(bool),
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 120a4b98a65..c7c4976aeb7 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
@@ -59,7 +59,7 @@ declare_clippy_lint! {
 
 pub struct NeedlessBorrowsForGenericArgs<'tcx> {
     /// Stack of (body owner, `PossibleBorrowerMap`) pairs. Used by
-    /// `needless_borrow_impl_arg_position` to determine when a borrowed expression can instead
+    /// [`needless_borrow_count`] to determine when a borrowed expression can instead
     /// be moved.
     possible_borrowers: Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
 
diff --git a/src/tools/clippy/clippy_lints/src/needless_for_each.rs b/src/tools/clippy/clippy_lints/src/needless_for_each.rs
index a67545e419c..3a6ccc2bca9 100644
--- a/src/tools/clippy/clippy_lints/src/needless_for_each.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_for_each.rs
@@ -1,6 +1,6 @@
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{Visitor, walk_expr};
-use rustc_hir::{Block, BlockCheckMode, Closure, Expr, ExprKind, Stmt, StmtKind};
+use rustc_hir::{Block, BlockCheckMode, Closure, Expr, ExprKind, Stmt, StmtKind, TyKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
 use rustc_span::Span;
@@ -70,12 +70,24 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach {
             && has_iter_method(cx, cx.typeck_results().expr_ty(iter_recv)).is_some()
             // Skip the lint if the body is not block because this is simpler than `for` loop.
             // e.g. `v.iter().for_each(f)` is simpler and clearer than using `for` loop.
-            && let ExprKind::Closure(&Closure { body, .. }) = for_each_arg.kind
+            && let ExprKind::Closure(&Closure { body, fn_decl, .. }) = for_each_arg.kind
             && let body = cx.tcx.hir_body(body)
             // Skip the lint if the body is not safe, so as not to suggest `for … in … unsafe {}`
             // and suggesting `for … in … { unsafe { } }` is a little ugly.
             && !matches!(body.value.kind, ExprKind::Block(Block { rules: BlockCheckMode::UnsafeBlock(_), .. }, ..))
         {
+            let mut applicability = Applicability::MachineApplicable;
+
+            // If any closure parameter has an explicit type specified, applying the lint would necessarily
+            // remove that specification, possibly breaking type inference
+            if fn_decl
+                .inputs
+                .iter()
+                .any(|input| matches!(input.kind, TyKind::Infer(..)))
+            {
+                applicability = Applicability::MaybeIncorrect;
+            }
+
             let mut ret_collector = RetCollector::default();
             ret_collector.visit_expr(body.value);
 
@@ -84,18 +96,16 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessForEach {
                 return;
             }
 
-            let (mut applicability, ret_suggs) = if ret_collector.spans.is_empty() {
-                (Applicability::MachineApplicable, None)
+            let ret_suggs = if ret_collector.spans.is_empty() {
+                None
             } else {
-                (
-                    Applicability::MaybeIncorrect,
-                    Some(
-                        ret_collector
-                            .spans
-                            .into_iter()
-                            .map(|span| (span, "continue".to_string()))
-                            .collect(),
-                    ),
+                applicability = Applicability::MaybeIncorrect;
+                Some(
+                    ret_collector
+                        .spans
+                        .into_iter()
+                        .map(|span| (span, "continue".to_string()))
+                        .collect(),
                 )
             };
 
diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs
index 72e6503e7e4..0d6666eed45 100644
--- a/src/tools/clippy/clippy_lints/src/no_effect.rs
+++ b/src/tools/clippy/clippy_lints/src/no_effect.rs
@@ -305,11 +305,12 @@ fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
             for e in reduced {
                 if let Some(snip) = e.span.get_source_text(cx) {
                     snippet.push_str(&snip);
-                    snippet.push(';');
+                    snippet.push_str("; ");
                 } else {
                     return;
                 }
             }
+            snippet.pop(); // remove the last space
             span_lint_hir_and_then(
                 cx,
                 UNNECESSARY_OPERATION,
diff --git a/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs b/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs
index 04b09276966..ba67dc62abb 100644
--- a/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs
+++ b/src/tools/clippy/clippy_lints/src/non_canonical_impls.rs
@@ -134,7 +134,8 @@ impl LateLintPass<'_> for NonCanonicalImpls {
             return;
         }
 
-        if cx.tcx.is_diagnostic_item(sym::Clone, trait_impl.def_id)
+        let trait_name = cx.tcx.get_diagnostic_name(trait_impl.def_id);
+        if trait_name == Some(sym::Clone)
             && let Some(copy_def_id) = cx.tcx.get_diagnostic_item(sym::Copy)
             && implements_trait(cx, trait_impl.self_ty(), copy_def_id, &[])
         {
@@ -170,12 +171,8 @@ impl LateLintPass<'_> for NonCanonicalImpls {
                     String::new(),
                     Applicability::MaybeIncorrect,
                 );
-
-                return;
             }
-        }
-
-        if cx.tcx.is_diagnostic_item(sym::PartialOrd, trait_impl.def_id)
+        } else if trait_name == Some(sym::PartialOrd)
             && impl_item.ident.name == sym::partial_cmp
             && let Some(ord_def_id) = cx.tcx.get_diagnostic_item(sym::Ord)
             && implements_trait(cx, trait_impl.self_ty(), ord_def_id, &[])
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 388c029c9ef..2fffc4244a7 100644
--- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs
+++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs
@@ -92,7 +92,7 @@ declare_clippy_lint! {
     /// ```
     #[clippy::version = "pre 1.29.0"]
     pub DECLARE_INTERIOR_MUTABLE_CONST,
-    style,
+    suspicious,
     "declaring `const` with interior mutability"
 }
 
@@ -778,7 +778,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
                     if let Node::Item(parent_item) = cx.tcx.parent_hir_node(item.hir_id())
                         && let ItemKind::Impl(impl_block) = parent_item.kind
                         && let Some(of_trait) = impl_block.of_trait
-                        && let Some(trait_id) = of_trait.trait_def_id()
+                        && let Some(trait_id) = of_trait.trait_ref.trait_def_id()
                     {
                         // Replace all instances of `<Self as Trait>::AssocType` with the
                         // unit type and check again. If the result is the same then the
diff --git a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs
index c5873589b26..1961ac1516d 100644
--- a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs
+++ b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs
@@ -248,6 +248,11 @@ impl SimilarNamesNameVisitor<'_, '_, '_> {
                 continue;
             }
 
+            // Skip similarity check if both names are exactly 3 characters
+            if count == 3 && existing_name.len == 3 {
+                continue;
+            }
+
             let dissimilar = match existing_name.len.cmp(&count) {
                 Ordering::Greater => existing_name.len - count != 1 || levenstein_not_1(interned_name, existing_str),
                 Ordering::Less => count - existing_name.len != 1 || levenstein_not_1(existing_str, interned_name),
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 23a1622f30f..cb934466bd8 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,13 +43,11 @@ 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
-                        && matches!(
-                            cx.tcx.get_diagnostic_name(adt.did()),
-                            Some(sym::FsOpenOptions | sym::DirBuilder)
-                        ))
-                        || (path.ident.name == sym::set_mode
-                            && cx.tcx.is_diagnostic_item(sym::FsPermissions, adt.did())))
+                    && matches!(
+                        (cx.tcx.get_diagnostic_name(adt.did()), path.ident.name),
+                        (Some(sym::FsOpenOptions | sym::DirBuilder), sym::mode)
+                            | (Some(sym::FsPermissions), sym::set_mode)
+                    )
                     && let ExprKind::Lit(_) = param.kind
                     && param.span.eq_ctxt(expr.span)
                     && param
diff --git a/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs b/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs
index 8ff78ec7c58..b810bc01fbd 100644
--- a/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs
+++ b/src/tools/clippy/clippy_lints/src/non_send_fields_in_send_ty.rs
@@ -83,10 +83,10 @@ impl<'tcx> LateLintPass<'tcx> for NonSendFieldInSendTy {
         if !item.span.in_external_macro(cx.tcx.sess.source_map())
             && let Some(send_trait) = cx.tcx.get_diagnostic_item(sym::Send)
             && let ItemKind::Impl(hir_impl) = &item.kind
-            && let Some(trait_ref) = &hir_impl.of_trait
-            && let Some(trait_id) = trait_ref.trait_def_id()
+            && let Some(of_trait) = &hir_impl.of_trait
+            && let Some(trait_id) = of_trait.trait_ref.trait_def_id()
             && send_trait == trait_id
-            && hir_impl.polarity == ImplPolarity::Positive
+            && of_trait.polarity == ImplPolarity::Positive
             && let Some(ty_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id)
             && let self_ty = ty_trait_ref.instantiate_identity().self_ty()
             && let ty::Adt(adt_def, impl_trait_args) = self_ty.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 ba8f6354d97..a21c361356e 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
@@ -173,7 +173,7 @@ impl Params {
     }
 
     /// Sets the `apply_lint` flag on each parameter.
-    fn flag_for_linting(&mut self) {
+    fn flag_for_linting(&self) {
         // Stores the list of parameters currently being resolved. Needed to avoid cycles.
         let mut eval_stack = Vec::new();
         for param in &self.params {
@@ -248,11 +248,13 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
                         .tcx
                         .impl_trait_ref(item.owner_id)
                         .map(EarlyBinder::instantiate_identity)
-                    && let Some(trait_item_id) = cx.tcx.associated_item(owner_id).trait_item_def_id
+                    && let Some(trait_item_id) = cx.tcx.trait_item_of(owner_id)
                 {
                     (
                         trait_item_id,
-                        FnKind::ImplTraitFn(std::ptr::from_ref(cx.tcx.erase_regions(trait_ref.args)) as usize),
+                        FnKind::ImplTraitFn(
+                            std::ptr::from_ref(cx.tcx.erase_and_anonymize_regions(trait_ref.args)) as usize
+                        ),
                         usize::from(sig.decl.implicit_self.has_implicit_self()),
                     )
                 } else {
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 466beb04b07..ea5b81aec31 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 {
         self.issue_lint(cx, expr);
     }
 
-    fn should_skip_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'tcx>) -> bool {
+    fn should_skip_expr<'tcx>(&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.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 9c6141d8222..2d303e40bd1 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
@@ -44,12 +44,28 @@ pub(super) fn check<'tcx>(
                     }
                 }
 
-                // Skip if the trait is not stable in const contexts
-                if is_in_const_context(cx)
-                    && let Some(binop_id) = cx.tcx.associated_item_def_ids(trait_id).first()
-                    && !is_stable_const_fn(cx, *binop_id, msrv)
-                {
-                    return;
+                // Skip if the trait or the implementation is not stable in const contexts
+                if is_in_const_context(cx) {
+                    if cx
+                        .tcx
+                        .associated_item_def_ids(trait_id)
+                        .first()
+                        .is_none_or(|binop_id| !is_stable_const_fn(cx, *binop_id, msrv))
+                    {
+                        return;
+                    }
+
+                    let impls = cx.tcx.non_blanket_impls_for_ty(trait_id, rty).collect::<Vec<_>>();
+                    if impls.is_empty()
+                        || impls.into_iter().any(|impl_id| {
+                            cx.tcx
+                                .associated_item_def_ids(impl_id)
+                                .first()
+                                .is_none_or(|fn_id| !is_stable_const_fn(cx, *fn_id, msrv))
+                        })
+                    {
+                        return;
+                    }
                 }
 
                 span_lint_and_then(
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 22ec4fe60fb..604f8f5da0b 100644
--- a/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs
@@ -47,14 +47,10 @@ fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool)
             (arg, arg.span)
         },
         ExprKind::Call(path, [arg])
-            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) {
-                    !is_copy(cx, typeck.expr_ty(expr))
-                } else {
-                    false
-                }
+            if path_def_id(cx, path).is_some_and(|did| match cx.tcx.get_diagnostic_name(did) {
+                Some(sym::from_str_method) => true,
+                Some(sym::from_fn) => !is_copy(cx, typeck.expr_ty(expr)),
+                _ => false,
             }) =>
         {
             (arg, arg.span)
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 e3fc8d8fea7..8f5ee390f72 100644
--- a/src/tools/clippy/clippy_lints/src/operators/erasing_op.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/erasing_op.rs
@@ -1,6 +1,6 @@
 use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::ty::same_type_and_consts;
+use clippy_utils::ty::same_type_modulo_regions;
 
 use rustc_hir::{BinOpKind, Expr};
 use rustc_lint::LateContext;
@@ -29,7 +29,7 @@ pub(super) fn check<'tcx>(
 fn different_types(tck: &TypeckResults<'_>, input: &Expr<'_>, output: &Expr<'_>) -> bool {
     let input_ty = tck.expr_ty(input).peel_refs();
     let output_ty = tck.expr_ty(output).peel_refs();
-    !same_type_and_consts(input_ty, output_ty)
+    !same_type_modulo_regions(input_ty, output_ty)
 }
 
 fn check_op<'tcx>(
diff --git a/src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs b/src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs
index 047a5a0159c..17fa8017c97 100644
--- a/src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs
@@ -34,10 +34,11 @@ pub(crate) fn check<'tcx>(
             val_r,
         ) = lhs.kind
 
-        // right hand side matches either f32::EPSILON or f64::EPSILON
+        // right hand side matches _::EPSILON
         && let ExprKind::Path(ref epsilon_path) = rhs.kind
         && let Res::Def(DefKind::AssocConst, def_id) = cx.qpath_res(epsilon_path, rhs.hir_id)
-        && ([sym::f32_epsilon, sym::f64_epsilon].into_iter().any(|sym| cx.tcx.is_diagnostic_item(sym, def_id)))
+        && let Some(sym) = cx.tcx.get_diagnostic_name(def_id)
+        && matches!(sym, sym::f16_epsilon | sym::f32_epsilon | sym::f64_epsilon | sym::f128_epsilon)
 
         // values of the subtractions on the left hand side are of the type float
         && let t_val_l = cx.typeck_results().expr_ty(val_l)
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 e6be536ca0f..9b1b063c473 100644
--- a/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs
@@ -13,7 +13,7 @@ pub struct Context {
     const_span: Option<Span>,
 }
 impl Context {
-    fn skip_expr(&mut self, e: &hir::Expr<'_>) -> bool {
+    fn skip_expr(&self, e: &hir::Expr<'_>) -> bool {
         self.expr_id.is_some() || self.const_span.is_some_and(|span| span.contains(e.span))
     }
 
diff --git a/src/tools/clippy/clippy_lints/src/operators/op_ref.rs b/src/tools/clippy/clippy_lints/src/operators/op_ref.rs
index 0a1f2625f4c..9c160ff680e 100644
--- a/src/tools/clippy/clippy_lints/src/operators/op_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/op_ref.rs
@@ -183,7 +183,7 @@ fn in_impl<'tcx>(
         && let item = cx.tcx.hir_expect_item(impl_def_id.expect_local())
         && let ItemKind::Impl(item) = &item.kind
         && let Some(of_trait) = &item.of_trait
-        && let Some(seg) = of_trait.path.segments.last()
+        && let Some(seg) = of_trait.trait_ref.path.segments.last()
         && let Res::Def(_, trait_id) = seg.res
         && trait_id == bin_op
         && let Some(generic_args) = seg.args
diff --git a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs
index 449d3da7639..43db0085f2e 100644
--- a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs
+++ b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs
@@ -1,7 +1,7 @@
 use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::is_in_test;
 use clippy_utils::macros::{is_panic, root_macro_call_first_node};
+use clippy_utils::{is_in_test, is_inside_always_const_context};
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{Expr, ExprKind, QPath};
 use rustc_lint::{LateContext, LateLintPass};
@@ -99,7 +99,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if let Some(macro_call) = root_macro_call_first_node(cx, expr) {
             if is_panic(cx, macro_call.def_id) {
-                if cx.tcx.hir_is_inside_const_context(expr.hir_id)
+                if is_inside_always_const_context(cx.tcx, expr.hir_id)
                     || self.allow_panic_in_tests && is_in_test(cx.tcx, expr.hir_id)
                 {
                     return;
@@ -140,7 +140,7 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented {
             && let Res::Def(DefKind::Fn, def_id) = expr_path.res
             && cx.tcx.is_diagnostic_item(sym::panic_any, def_id)
         {
-            if cx.tcx.hir_is_inside_const_context(expr.hir_id)
+            if is_inside_always_const_context(cx.tcx, expr.hir_id)
                 || self.allow_panic_in_tests && is_in_test(cx.tcx, expr.hir_id)
             {
                 return;
diff --git a/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs b/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs
index 301b2cd4bf2..77751e75a8e 100644
--- a/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs
+++ b/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs
@@ -34,15 +34,15 @@ 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(trait_ref),
+            of_trait: Some(of_trait),
             items: impl_items,
             ..
         }) = item.kind
             && !cx.tcx.is_automatically_derived(item.owner_id.to_def_id())
             && let Some(eq_trait) = cx.tcx.lang_items().eq_trait()
-            && trait_ref.path.res.def_id() == eq_trait
+            && of_trait.trait_ref.path.res.def_id() == eq_trait
         {
-            for impl_item in *impl_items {
+            for impl_item in impl_items {
                 if cx.tcx.item_name(impl_item.owner_id) == sym::ne {
                     span_lint_hir(
                         cx,
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 d7b4a03aa53..1b1e77bbea8 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
@@ -120,7 +120,7 @@ impl PassByRefOrValue {
         }
     }
 
-    fn check_poly_fn(&mut self, cx: &LateContext<'_>, def_id: LocalDefId, decl: &FnDecl<'_>, span: Option<Span>) {
+    fn check_poly_fn(&self, cx: &LateContext<'_>, def_id: LocalDefId, decl: &FnDecl<'_>, span: Option<Span>) {
         if self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(def_id) {
             return;
         }
diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs
index b3058c51afd..9eed46460a6 100644
--- a/src/tools/clippy/clippy_lints/src/ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/ptr.rs
@@ -237,7 +237,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
             .collect();
         let results = check_ptr_arg_usage(cx, body, &lint_args);
 
-        for (result, args) in results.iter().zip(lint_args.iter()).filter(|(r, _)| !r.skip) {
+        for (result, args) in iter::zip(&results, &lint_args).filter(|(r, _)| !r.skip) {
             span_lint_hir_and_then(cx, PTR_ARG, args.emission_id, args.span, args.build_msg(), |diag| {
                 diag.multipart_suggestion(
                     "change this to",
@@ -386,7 +386,6 @@ impl<'tcx> DerefTy<'tcx> {
     }
 }
 
-#[expect(clippy::too_many_lines)]
 fn check_fn_args<'cx, 'tcx: 'cx>(
     cx: &'cx LateContext<'tcx>,
     fn_sig: ty::FnSig<'tcx>,
@@ -413,13 +412,13 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
                     Some(sym::Vec) => (
                         [(sym::clone, ".to_owned()")].as_slice(),
                         DerefTy::Slice(
-                            name.args.and_then(|args| args.args.first()).and_then(|arg| {
-                                if let GenericArg::Type(ty) = arg {
-                                    Some(ty.span)
-                                } else {
-                                    None
-                                }
-                            }),
+                            if let Some(name_args) = name.args
+                                && let [GenericArg::Type(ty), ..] = name_args.args
+                            {
+                                Some(ty.span)
+                            } else {
+                                None
+                            },
                             args.type_at(0),
                         ),
                     ),
@@ -432,33 +431,29 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
                         DerefTy::Path,
                     ),
                     Some(sym::Cow) if mutability == Mutability::Not => {
-                        if let Some((lifetime, ty)) = name.args.and_then(|args| {
-                            if let [GenericArg::Lifetime(lifetime), ty] = args.args {
-                                return Some((lifetime, ty));
-                            }
-                            None
-                        }) {
+                        if let Some(name_args) = name.args
+                            && let [GenericArg::Lifetime(lifetime), ty] = name_args.args
+                        {
                             if let LifetimeKind::Param(param_def_id) = lifetime.kind
                                 && !lifetime.is_anonymous()
                                 && fn_sig
                                     .output()
                                     .walk()
-                                    .filter_map(|arg| {
-                                        arg.as_region().and_then(|lifetime| match lifetime.kind() {
-                                            ty::ReEarlyParam(r) => Some(
-                                                cx.tcx
-                                                    .generics_of(cx.tcx.parent(param_def_id.to_def_id()))
-                                                    .region_param(r, cx.tcx)
-                                                    .def_id,
-                                            ),
-                                            ty::ReBound(_, r) => r.kind.get_id(),
-                                            ty::ReLateParam(r) => r.kind.get_id(),
-                                            ty::ReStatic
-                                            | ty::ReVar(_)
-                                            | ty::RePlaceholder(_)
-                                            | ty::ReErased
-                                            | ty::ReError(_) => None,
-                                        })
+                                    .filter_map(ty::GenericArg::as_region)
+                                    .filter_map(|lifetime| match lifetime.kind() {
+                                        ty::ReEarlyParam(r) => Some(
+                                            cx.tcx
+                                                .generics_of(cx.tcx.parent(param_def_id.to_def_id()))
+                                                .region_param(r, cx.tcx)
+                                                .def_id,
+                                        ),
+                                        ty::ReBound(_, r) => r.kind.get_id(),
+                                        ty::ReLateParam(r) => r.kind.get_id(),
+                                        ty::ReStatic
+                                        | ty::ReVar(_)
+                                        | ty::RePlaceholder(_)
+                                        | ty::ReErased
+                                        | ty::ReError(_) => None,
                                     })
                                     .any(|def_id| def_id.as_local().is_some_and(|def_id| def_id == param_def_id))
                             {
@@ -627,12 +622,16 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &Body<'tcx>, args: &[
                         }
                     }
 
+                    // If the expression's type gets adjusted down to the deref type, we might as
+                    // well have started with that deref type -- the lint should fire
                     let deref_ty = args.deref_ty.ty(self.cx);
                     let adjusted_ty = self.cx.typeck_results().expr_ty_adjusted(e).peel_refs();
                     if adjusted_ty == deref_ty {
                         return;
                     }
 
+                    // If the expression's type is constrained by `dyn Trait`, see if the deref
+                    // type implements the trait(s) as well, and if so, the lint should fire
                     if let ty::Dynamic(preds, ..) = adjusted_ty.kind()
                         && matches_preds(self.cx, deref_ty, preds)
                     {
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
deleted file mode 100644
index d8d813f9846..00000000000
--- a/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs
+++ /dev/null
@@ -1,151 +0,0 @@
-use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
-use clippy_utils::source::SpanRangeExt;
-use clippy_utils::sym;
-use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::declare_lint_pass;
-use std::fmt;
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for usage of the `offset` pointer method with a `usize` casted to an
-    /// `isize`.
-    ///
-    /// ### Why is this bad?
-    /// If we’re always increasing the pointer address, we can avoid the numeric
-    /// cast by using the `add` method instead.
-    ///
-    /// ### Example
-    /// ```no_run
-    /// let vec = vec![b'a', b'b', b'c'];
-    /// let ptr = vec.as_ptr();
-    /// let offset = 1_usize;
-    ///
-    /// unsafe {
-    ///     ptr.offset(offset as isize);
-    /// }
-    /// ```
-    ///
-    /// Could be written:
-    ///
-    /// ```no_run
-    /// let vec = vec![b'a', b'b', b'c'];
-    /// let ptr = vec.as_ptr();
-    /// let offset = 1_usize;
-    ///
-    /// unsafe {
-    ///     ptr.add(offset);
-    /// }
-    /// ```
-    #[clippy::version = "1.30.0"]
-    pub PTR_OFFSET_WITH_CAST,
-    complexity,
-    "unneeded pointer offset cast"
-}
-
-declare_lint_pass!(PtrOffsetWithCast => [PTR_OFFSET_WITH_CAST]);
-
-impl<'tcx> LateLintPass<'tcx> for PtrOffsetWithCast {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        // Check if the expressions is a ptr.offset or ptr.wrapping_offset method call
-        let Some((receiver_expr, arg_expr, method)) = expr_as_ptr_offset_call(cx, expr) else {
-            return;
-        };
-
-        // Check if the argument to the method call is a cast from usize
-        let Some(cast_lhs_expr) = expr_as_cast_from_usize(cx, arg_expr) else {
-            return;
-        };
-
-        let msg = format!("use of `{method}` with a `usize` casted to an `isize`");
-        if let Some(sugg) = build_suggestion(cx, method, receiver_expr, cast_lhs_expr) {
-            span_lint_and_sugg(
-                cx,
-                PTR_OFFSET_WITH_CAST,
-                expr.span,
-                msg,
-                "try",
-                sugg,
-                Applicability::MachineApplicable,
-            );
-        } else {
-            span_lint(cx, PTR_OFFSET_WITH_CAST, expr.span, msg);
-        }
-    }
-}
-
-// If the given expression is a cast from a usize, return the lhs of the cast
-fn expr_as_cast_from_usize<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
-    if let ExprKind::Cast(cast_lhs_expr, _) = expr.kind
-        && is_expr_ty_usize(cx, cast_lhs_expr)
-    {
-        return Some(cast_lhs_expr);
-    }
-    None
-}
-
-// If the given expression is a ptr::offset  or ptr::wrapping_offset method call, return the
-// receiver, the arg of the method call, and the method.
-fn expr_as_ptr_offset_call<'tcx>(
-    cx: &LateContext<'tcx>,
-    expr: &'tcx Expr<'_>,
-) -> Option<(&'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Method)> {
-    if let ExprKind::MethodCall(path_segment, arg_0, [arg_1], _) = &expr.kind
-        && is_expr_ty_raw_ptr(cx, arg_0)
-    {
-        if path_segment.ident.name == sym::offset {
-            return Some((arg_0, arg_1, Method::Offset));
-        }
-        if path_segment.ident.name == sym::wrapping_offset {
-            return Some((arg_0, arg_1, Method::WrappingOffset));
-        }
-    }
-    None
-}
-
-// Is the type of the expression a usize?
-fn is_expr_ty_usize(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    cx.typeck_results().expr_ty(expr) == cx.tcx.types.usize
-}
-
-// Is the type of the expression a raw pointer?
-fn is_expr_ty_raw_ptr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    cx.typeck_results().expr_ty(expr).is_raw_ptr()
-}
-
-fn build_suggestion(
-    cx: &LateContext<'_>,
-    method: Method,
-    receiver_expr: &Expr<'_>,
-    cast_lhs_expr: &Expr<'_>,
-) -> Option<String> {
-    let receiver = receiver_expr.span.get_source_text(cx)?;
-    let cast_lhs = cast_lhs_expr.span.get_source_text(cx)?;
-    Some(format!("{receiver}.{}({cast_lhs})", method.suggestion()))
-}
-
-#[derive(Copy, Clone)]
-enum Method {
-    Offset,
-    WrappingOffset,
-}
-
-impl Method {
-    #[must_use]
-    fn suggestion(self) -> &'static str {
-        match self {
-            Self::Offset => "add",
-            Self::WrappingOffset => "wrapping_add",
-        }
-    }
-}
-
-impl fmt::Display for Method {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            Self::Offset => write!(f, "offset"),
-            Self::WrappingOffset => write!(f, "wrapping_offset"),
-        }
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs
index de12a25b03d..4aa100a50e0 100644
--- a/src/tools/clippy/clippy_lints/src/question_mark.rs
+++ b/src/tools/clippy/clippy_lints/src/question_mark.rs
@@ -8,9 +8,9 @@ use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::sugg::Sugg;
 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_res, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt,
-    span_contains_cfg, span_contains_comment, sym,
+    eq_expr_value, fn_def_id_with_node_args, 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_res, path_to_local, path_to_local_id,
+    peel_blocks, peel_blocks_with_stmt, span_contains_cfg, span_contains_comment, sym,
 };
 use rustc_errors::Applicability;
 use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk};
@@ -393,8 +393,8 @@ fn check_arm_is_none_or_err<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm: &A
                 && 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)
+                // check if `...` is `val` from binding or `val.into()`
+                && is_local_or_local_into(cx, ret_expr, ok_val)
             {
                 true
             } else {
@@ -417,6 +417,17 @@ fn check_arm_is_none_or_err<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm: &A
     }
 }
 
+/// Check if `expr` is `val` or `val.into()`
+fn is_local_or_local_into(cx: &LateContext<'_>, expr: &Expr<'_>, val: HirId) -> bool {
+    let is_into_call = fn_def_id_with_node_args(cx, expr)
+        .and_then(|(fn_def_id, _)| cx.tcx.trait_of_assoc(fn_def_id))
+        .is_some_and(|trait_def_id| cx.tcx.is_diagnostic_item(sym::Into, trait_def_id));
+    match expr.kind {
+        ExprKind::MethodCall(_, recv, [], _) | ExprKind::Call(_, [recv]) => is_into_call && path_to_local_id(recv, val),
+        _ => path_to_local_id(expr, val),
+    }
+}
+
 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))
diff --git a/src/tools/clippy/clippy_lints/src/raw_strings.rs b/src/tools/clippy/clippy_lints/src/raw_strings.rs
index 6a79cae32a5..943e662479e 100644
--- a/src/tools/clippy/clippy_lints/src/raw_strings.rs
+++ b/src/tools/clippy/clippy_lints/src/raw_strings.rs
@@ -103,15 +103,7 @@ impl EarlyLintPass for RawStrings {
 }
 
 impl RawStrings {
-    fn check_raw_string(
-        &mut self,
-        cx: &EarlyContext<'_>,
-        str: &str,
-        lit_span: Span,
-        prefix: &str,
-        max: u8,
-        descr: &str,
-    ) {
+    fn check_raw_string(&self, cx: &EarlyContext<'_>, str: &str, lit_span: Span, prefix: &str, max: u8, descr: &str) {
         if !str.contains(['\\', '"']) {
             span_lint_and_then(
                 cx,
diff --git a/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs b/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs
index acd840401c6..b8d4e7c4651 100644
--- a/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs
+++ b/src/tools/clippy/clippy_lints/src/read_zero_byte_vec.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
 use clippy_utils::higher::{VecInitKind, get_vec_init_kind};
-use clippy_utils::source::snippet;
+use clippy_utils::source::{indent_of, snippet};
 use clippy_utils::{get_enclosing_block, sym};
 
 use rustc_errors::Applicability;
@@ -83,10 +83,12 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec {
                             expr.span,
                             "reading zero byte data to `Vec`",
                             |diag| {
+                                let span = first_stmt_containing_expr(cx, expr).map_or(expr.span, |stmt| stmt.span);
+                                let indent = indent_of(cx, span).unwrap_or(0);
                                 diag.span_suggestion(
-                                    expr.span,
+                                    span.shrink_to_lo(),
                                     "try",
-                                    format!("{}.resize({len}, 0); {}", ident, snippet(cx, expr.span, "..")),
+                                    format!("{ident}.resize({len}, 0);\n{}", " ".repeat(indent)),
                                     applicability,
                                 );
                             },
@@ -100,14 +102,15 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec {
                                 expr.span,
                                 "reading zero byte data to `Vec`",
                                 |diag| {
+                                    let span = first_stmt_containing_expr(cx, expr).map_or(expr.span, |stmt| stmt.span);
+                                    let indent = indent_of(cx, span).unwrap_or(0);
                                     diag.span_suggestion(
-                                        expr.span,
+                                        span.shrink_to_lo(),
                                         "try",
                                         format!(
-                                            "{}.resize({}, 0); {}",
-                                            ident,
+                                            "{ident}.resize({}, 0);\n{}",
                                             snippet(cx, e.span, ".."),
-                                            snippet(cx, expr.span, "..")
+                                            " ".repeat(indent)
                                         ),
                                         applicability,
                                     );
@@ -130,6 +133,16 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec {
     }
 }
 
+fn first_stmt_containing_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<&'tcx hir::Stmt<'tcx>> {
+    cx.tcx.hir_parent_iter(expr.hir_id).find_map(|(_, node)| {
+        if let hir::Node::Stmt(stmt) = node {
+            Some(stmt)
+        } else {
+            None
+        }
+    })
+}
+
 struct ReadVecVisitor<'tcx> {
     local_id: HirId,
     read_zero_expr: Option<&'tcx Expr<'tcx>>,
diff --git a/src/tools/clippy/clippy_lints/src/redundant_clone.rs b/src/tools/clippy/clippy_lints/src/redundant_clone.rs
index e57b8cc2d84..de6766cbe94 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_clone.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_clone.rs
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
 use clippy_utils::fn_has_unsatisfiable_preds;
 use clippy_utils::mir::{LocalUsage, PossibleBorrowerMap, visit_local_usage};
 use clippy_utils::source::SpanRangeExt;
-use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, is_type_lang_item, walk_ptrs_ty_depth};
+use clippy_utils::ty::{has_drop, is_copy, is_type_lang_item, peel_and_count_ty_refs};
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{Body, FnDecl, LangItem, def_id};
@@ -96,14 +96,13 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone {
             let (fn_def_id, arg, arg_ty, clone_ret) =
                 unwrap_or_continue!(is_call_with_ref_arg(cx, mir, &terminator.kind));
 
+            let fn_name = cx.tcx.get_diagnostic_name(fn_def_id);
+
             let from_borrow = cx.tcx.lang_items().get(LangItem::CloneFn) == Some(fn_def_id)
-                || cx.tcx.is_diagnostic_item(sym::to_owned_method, fn_def_id)
-                || (cx.tcx.is_diagnostic_item(sym::to_string_method, fn_def_id)
-                    && is_type_lang_item(cx, arg_ty, LangItem::String));
+                || fn_name == Some(sym::to_owned_method)
+                || (fn_name == Some(sym::to_string_method) && is_type_lang_item(cx, arg_ty, LangItem::String));
 
-            let from_deref = !from_borrow
-                && (cx.tcx.is_diagnostic_item(sym::path_to_pathbuf, fn_def_id)
-                    || cx.tcx.is_diagnostic_item(sym::os_str_to_os_string, fn_def_id));
+            let from_deref = !from_borrow && matches!(fn_name, Some(sym::path_to_pathbuf | sym::os_str_to_os_string));
 
             if !from_borrow && !from_deref {
                 continue;
@@ -148,8 +147,9 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone {
                     is_call_with_ref_arg(cx, mir, &pred_terminator.kind)
                     && res == cloned
                     && cx.tcx.is_diagnostic_item(sym::deref_method, pred_fn_def_id)
-                    && (is_type_diagnostic_item(cx, pred_arg_ty, sym::PathBuf)
-                        || is_type_diagnostic_item(cx, pred_arg_ty, sym::OsString))
+                    && let ty::Adt(pred_arg_def, _) = pred_arg_ty.kind()
+                    && let Some(pred_arg_name) = cx.tcx.get_diagnostic_name(pred_arg_def.did())
+                    && matches!(pred_arg_name, sym::PathBuf | sym::OsString)
                 {
                     (pred_arg, res)
                 } else {
@@ -263,7 +263,7 @@ fn is_call_with_ref_arg<'tcx>(
         && args.len() == 1
         && let mir::Operand::Move(mir::Place { local, .. }) = &args[0].node
         && let ty::FnDef(def_id, _) = *func.ty(mir, cx.tcx).kind()
-        && let (inner_ty, 1) = walk_ptrs_ty_depth(args[0].node.ty(mir, cx.tcx))
+        && let (inner_ty, 1, _) = peel_and_count_ty_refs(args[0].node.ty(mir, cx.tcx))
         && !is_copy(cx, inner_ty)
     {
         Some((def_id, *local, inner_ty, destination.as_local()?))
diff --git a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs
index 3828aff4164..0c1c664f111 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_pub_crate.rs
@@ -7,7 +7,6 @@ use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
 use rustc_session::impl_lint_pass;
 use rustc_span::def_id::CRATE_DEF_ID;
-use rustc_span::hygiene::MacroKind;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -89,8 +88,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate {
 // We ignore macro exports. And `ListStem` uses, which aren't interesting.
 fn is_ignorable_export<'tcx>(item: &'tcx Item<'tcx>) -> bool {
     if let ItemKind::Use(path, kind) = item.kind {
-        let ignore = matches!(path.res.macro_ns, Some(Res::Def(DefKind::Macro(MacroKind::Bang), _)))
-            || kind == UseKind::ListStem;
+        let ignore = matches!(path.res.macro_ns, Some(Res::Def(DefKind::Macro(_), _))) || kind == UseKind::ListStem;
         if ignore {
             return true;
         }
diff --git a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs
index 324a05cdcc0..a358eff2ce5 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::get_parent_expr;
 use clippy_utils::source::snippet_with_context;
-use clippy_utils::ty::is_type_lang_item;
-use clippy_utils::{get_parent_expr, peel_middle_ty_refs};
+use clippy_utils::ty::{is_type_lang_item, peel_and_count_ty_refs};
 use rustc_ast::util::parser::ExprPrecedence;
 use rustc_errors::Applicability;
 use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability};
@@ -82,8 +82,8 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing {
             && let ExprKind::Index(indexed, range, _) = addressee.kind
             && is_type_lang_item(cx, cx.typeck_results().expr_ty_adjusted(range), LangItem::RangeFull)
         {
-            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 (expr_ty, expr_ref_count, _) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(expr));
+            let (indexed_ty, indexed_ref_count, _) = peel_and_count_ty_refs(cx.typeck_results().expr_ty(indexed));
             let parent_expr = get_parent_expr(cx, expr);
             let needs_parens_for_prefix =
                 parent_expr.is_some_and(|parent| cx.precedence(parent) > ExprPrecedence::Prefix);
diff --git a/src/tools/clippy/clippy_lints/src/reference.rs b/src/tools/clippy/clippy_lints/src/reference.rs
index 4bff37216ed..3bbcad12a31 100644
--- a/src/tools/clippy/clippy_lints/src/reference.rs
+++ b/src/tools/clippy/clippy_lints/src/reference.rs
@@ -1,10 +1,11 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::{SpanRangeExt, snippet_with_applicability};
-use rustc_ast::ast::{Expr, ExprKind, Mutability, UnOp};
+use clippy_utils::source::snippet;
+use clippy_utils::sugg::{Sugg, has_enclosing_paren};
+use clippy_utils::ty::adjust_derefs_manually_drop;
 use rustc_errors::Applicability;
-use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_hir::{Expr, ExprKind, HirId, Node, UnOp};
+use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
-use rustc_span::{BytePos, Span};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -37,17 +38,12 @@ declare_clippy_lint! {
 
 declare_lint_pass!(DerefAddrOf => [DEREF_ADDROF]);
 
-fn without_parens(mut e: &Expr) -> &Expr {
-    while let ExprKind::Paren(ref child_e) = e.kind {
-        e = child_e;
-    }
-    e
-}
-
-impl EarlyLintPass for DerefAddrOf {
-    fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) {
-        if let ExprKind::Unary(UnOp::Deref, ref deref_target) = e.kind
-            && let ExprKind::AddrOf(_, ref mutability, ref addrof_target) = without_parens(deref_target).kind
+impl LateLintPass<'_> for DerefAddrOf {
+    fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) {
+        if !e.span.from_expansion()
+            && let ExprKind::Unary(UnOp::Deref, deref_target) = e.kind
+            && !deref_target.span.from_expansion()
+            && let ExprKind::AddrOf(_, _, addrof_target) = deref_target.kind
             // NOTE(tesuji): `*&` forces rustc to const-promote the array to `.rodata` section.
             // See #12854 for details.
             && !matches!(addrof_target.kind, ExprKind::Array(_))
@@ -55,57 +51,82 @@ impl EarlyLintPass for DerefAddrOf {
             && !addrof_target.span.from_expansion()
         {
             let mut applicability = Applicability::MachineApplicable;
-            let sugg = if e.span.from_expansion() {
-                if let Some(macro_source) = e.span.get_source_text(cx) {
-                    // Remove leading whitespace from the given span
-                    // e.g: ` $visitor` turns into `$visitor`
-                    let trim_leading_whitespaces = |span: Span| {
-                        span.get_source_text(cx)
-                            .and_then(|snip| {
-                                #[expect(clippy::cast_possible_truncation)]
-                                snip.find(|c: char| !c.is_whitespace())
-                                    .map(|pos| span.lo() + BytePos(pos as u32))
-                            })
-                            .map_or(span, |start_no_whitespace| e.span.with_lo(start_no_whitespace))
-                    };
+            let mut sugg = || Sugg::hir_with_applicability(cx, addrof_target, "_", &mut applicability);
 
-                    let mut generate_snippet = |pattern: &str| {
-                        #[expect(clippy::cast_possible_truncation)]
-                        macro_source.rfind(pattern).map(|pattern_pos| {
-                            let rpos = pattern_pos + pattern.len();
-                            let span_after_ref = e.span.with_lo(BytePos(e.span.lo().0 + rpos as u32));
-                            let span = trim_leading_whitespaces(span_after_ref);
-                            snippet_with_applicability(cx, span, "_", &mut applicability)
-                        })
-                    };
+            // If this expression is an explicit `DerefMut` of a `ManuallyDrop` reached through a
+            // union, we may remove the reference if we are at the point where the implicit
+            // dereference would take place. Otherwise, we should not lint.
+            let sugg = match is_manually_drop_through_union(cx, e.hir_id, addrof_target) {
+                ManuallyDropThroughUnion::Directly => sugg().deref(),
+                ManuallyDropThroughUnion::Indirect => return,
+                ManuallyDropThroughUnion::No => sugg(),
+            };
+
+            let sugg = if has_enclosing_paren(snippet(cx, e.span, "")) {
+                sugg.maybe_paren()
+            } else {
+                sugg
+            };
+
+            span_lint_and_sugg(
+                cx,
+                DEREF_ADDROF,
+                e.span,
+                "immediately dereferencing a reference",
+                "try",
+                sugg.to_string(),
+                applicability,
+            );
+        }
+    }
+}
+
+/// Is this a `ManuallyDrop` reached through a union, and when is `DerefMut` called on it?
+enum ManuallyDropThroughUnion {
+    /// `ManuallyDrop` reached through a union and immediately explicitely dereferenced
+    Directly,
+    /// `ManuallyDrop` reached through a union, and dereferenced later on
+    Indirect,
+    /// Any other situation
+    No,
+}
 
-                    if *mutability == Mutability::Mut {
-                        generate_snippet("mut")
+/// Check if `addrof_target` is part of an access to a `ManuallyDrop` entity reached through a
+/// union, and when it is dereferenced using `DerefMut` starting from `expr_id` and going up.
+fn is_manually_drop_through_union(
+    cx: &LateContext<'_>,
+    expr_id: HirId,
+    addrof_target: &Expr<'_>,
+) -> ManuallyDropThroughUnion {
+    if is_reached_through_union(cx, addrof_target) {
+        let typeck = cx.typeck_results();
+        for (idx, id) in std::iter::once(expr_id)
+            .chain(cx.tcx.hir_parent_id_iter(expr_id))
+            .enumerate()
+        {
+            if let Node::Expr(expr) = cx.tcx.hir_node(id) {
+                if adjust_derefs_manually_drop(typeck.expr_adjustments(expr), typeck.expr_ty(expr)) {
+                    return if idx == 0 {
+                        ManuallyDropThroughUnion::Directly
                     } else {
-                        generate_snippet("&")
-                    }
-                } else {
-                    Some(snippet_with_applicability(cx, e.span, "_", &mut applicability))
+                        ManuallyDropThroughUnion::Indirect
+                    };
                 }
             } else {
-                Some(snippet_with_applicability(
-                    cx,
-                    addrof_target.span,
-                    "_",
-                    &mut applicability,
-                ))
-            };
-            if let Some(sugg) = sugg {
-                span_lint_and_sugg(
-                    cx,
-                    DEREF_ADDROF,
-                    e.span,
-                    "immediately dereferencing a reference",
-                    "try",
-                    sugg.to_string(),
-                    applicability,
-                );
+                break;
             }
         }
     }
+    ManuallyDropThroughUnion::No
+}
+
+/// Checks whether `expr` denotes an object reached through a union
+fn is_reached_through_union(cx: &LateContext<'_>, mut expr: &Expr<'_>) -> bool {
+    while let ExprKind::Field(parent, _) | ExprKind::Index(parent, _, _) = expr.kind {
+        if cx.typeck_results().expr_ty_adjusted(parent).is_union() {
+            return true;
+        }
+        expr = parent;
+    }
+    false
 }
diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs
deleted file mode 100644
index e0c93153a77..00000000000
--- a/src/tools/clippy/clippy_lints/src/returns.rs
+++ /dev/null
@@ -1,513 +0,0 @@
-use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
-use clippy_utils::source::{SpanRangeExt, snippet_with_context};
-use clippy_utils::sugg::has_enclosing_paren;
-use clippy_utils::visitors::for_each_expr;
-use clippy_utils::{
-    binary_expr_needs_parentheses, fn_def_id, is_from_proc_macro, is_inside_let_else, is_res_lang_ctor,
-    leaks_droppable_temporary_with_limited_lifetime, path_res, path_to_local_id, span_contains_cfg,
-    span_find_starting_semi, sym,
-};
-use core::ops::ControlFlow;
-use rustc_ast::MetaItemInner;
-use rustc_errors::Applicability;
-use rustc_hir::LangItem::ResultErr;
-use rustc_hir::intravisit::FnKind;
-use rustc_hir::{
-    Block, Body, Expr, ExprKind, FnDecl, HirId, ItemKind, LangItem, MatchSource, Node, OwnerNode, PatKind, QPath, Stmt,
-    StmtKind,
-};
-use rustc_lint::{LateContext, LateLintPass, Level, LintContext};
-use rustc_middle::ty::adjustment::Adjust;
-use rustc_middle::ty::{self, GenericArgKind, Ty};
-use rustc_session::declare_lint_pass;
-use rustc_span::def_id::LocalDefId;
-use rustc_span::edition::Edition;
-use rustc_span::{BytePos, Pos, Span};
-use std::borrow::Cow;
-use std::fmt::Display;
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for `let`-bindings, which are subsequently
-    /// returned.
-    ///
-    /// ### Why is this bad?
-    /// It is just extraneous code. Remove it to make your code
-    /// more rusty.
-    ///
-    /// ### Known problems
-    /// In the case of some temporaries, e.g. locks, eliding the variable binding could lead
-    /// to deadlocks. See [this issue](https://github.com/rust-lang/rust/issues/37612).
-    /// This could become relevant if the code is later changed to use the code that would have been
-    /// bound without first assigning it to a let-binding.
-    ///
-    /// ### Example
-    /// ```no_run
-    /// fn foo() -> String {
-    ///     let x = String::new();
-    ///     x
-    /// }
-    /// ```
-    /// instead, use
-    /// ```no_run
-    /// fn foo() -> String {
-    ///     String::new()
-    /// }
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub LET_AND_RETURN,
-    style,
-    "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block"
-}
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for return statements at the end of a block.
-    ///
-    /// ### Why is this bad?
-    /// Removing the `return` and semicolon will make the code
-    /// more rusty.
-    ///
-    /// ### Example
-    /// ```no_run
-    /// fn foo(x: usize) -> usize {
-    ///     return x;
-    /// }
-    /// ```
-    /// simplify to
-    /// ```no_run
-    /// fn foo(x: usize) -> usize {
-    ///     x
-    /// }
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub NEEDLESS_RETURN,
-    // This lint requires some special handling in `check_final_expr` for `#[expect]`.
-    // This handling needs to be updated if the group gets changed. This should also
-    // be caught by tests.
-    style,
-    "using a return statement like `return expr;` where an expression would suffice"
-}
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for return statements on `Err` paired with the `?` operator.
-    ///
-    /// ### Why is this bad?
-    /// The `return` is unnecessary.
-    ///
-    /// Returns may be used to add attributes to the return expression. Return
-    /// statements with attributes are therefore be accepted by this lint.
-    ///
-    /// ### Example
-    /// ```rust,ignore
-    /// fn foo(x: usize) -> Result<(), Box<dyn Error>> {
-    ///     if x == 0 {
-    ///         return Err(...)?;
-    ///     }
-    ///     Ok(())
-    /// }
-    /// ```
-    /// simplify to
-    /// ```rust,ignore
-    /// fn foo(x: usize) -> Result<(), Box<dyn Error>> {
-    ///     if x == 0 {
-    ///         Err(...)?;
-    ///     }
-    ///     Ok(())
-    /// }
-    /// ```
-    /// if paired with `try_err`, use instead:
-    /// ```rust,ignore
-    /// fn foo(x: usize) -> Result<(), Box<dyn Error>> {
-    ///     if x == 0 {
-    ///         return Err(...);
-    ///     }
-    ///     Ok(())
-    /// }
-    /// ```
-    #[clippy::version = "1.73.0"]
-    pub NEEDLESS_RETURN_WITH_QUESTION_MARK,
-    style,
-    "using a return statement like `return Err(expr)?;` where removing it would suffice"
-}
-
-#[derive(PartialEq, Eq)]
-enum RetReplacement<'tcx> {
-    Empty,
-    Block,
-    Unit,
-    NeedsPar(Cow<'tcx, str>, Applicability),
-    Expr(Cow<'tcx, str>, Applicability),
-}
-
-impl RetReplacement<'_> {
-    fn sugg_help(&self) -> &'static str {
-        match self {
-            Self::Empty | Self::Expr(..) => "remove `return`",
-            Self::Block => "replace `return` with an empty block",
-            Self::Unit => "replace `return` with a unit value",
-            Self::NeedsPar(..) => "remove `return` and wrap the sequence with parentheses",
-        }
-    }
-
-    fn applicability(&self) -> Applicability {
-        match self {
-            Self::Expr(_, ap) | Self::NeedsPar(_, ap) => *ap,
-            _ => Applicability::MachineApplicable,
-        }
-    }
-}
-
-impl Display for RetReplacement<'_> {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
-        match self {
-            Self::Empty => write!(f, ""),
-            Self::Block => write!(f, "{{}}"),
-            Self::Unit => write!(f, "()"),
-            Self::NeedsPar(inner, _) => write!(f, "({inner})"),
-            Self::Expr(inner, _) => write!(f, "{inner}"),
-        }
-    }
-}
-
-declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN, NEEDLESS_RETURN_WITH_QUESTION_MARK]);
-
-/// Checks if a return statement is "needed" in the middle of a block, or if it can be removed. This
-/// is the case when the enclosing block expression is coerced to some other type, which only works
-/// because of the never-ness of `return` expressions
-fn stmt_needs_never_type(cx: &LateContext<'_>, stmt_hir_id: HirId) -> bool {
-    cx.tcx
-        .hir_parent_iter(stmt_hir_id)
-        .find_map(|(_, node)| if let Node::Expr(expr) = node { Some(expr) } else { None })
-        .is_some_and(|e| {
-            cx.typeck_results()
-                .expr_adjustments(e)
-                .iter()
-                .any(|adjust| adjust.target != cx.tcx.types.unit && matches!(adjust.kind, Adjust::NeverToAny))
-        })
-}
-
-impl<'tcx> LateLintPass<'tcx> for Return {
-    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
-        if !stmt.span.in_external_macro(cx.sess().source_map())
-            && let StmtKind::Semi(expr) = stmt.kind
-            && let ExprKind::Ret(Some(ret)) = expr.kind
-            // return Err(...)? desugars to a match
-            // over a Err(...).branch()
-            // which breaks down to a branch call, with the callee being
-            // the constructor of the Err variant
-            && let ExprKind::Match(maybe_cons, _, MatchSource::TryDesugar(_)) = ret.kind
-            && let ExprKind::Call(_, [maybe_result_err]) = maybe_cons.kind
-            && let ExprKind::Call(maybe_constr, _) = maybe_result_err.kind
-            && is_res_lang_ctor(cx, path_res(cx, maybe_constr), ResultErr)
-
-            // Ensure this is not the final stmt, otherwise removing it would cause a compile error
-            && let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir_get_parent_item(expr.hir_id))
-            && let ItemKind::Fn { body, .. } = item.kind
-            && let block = cx.tcx.hir_body(body).value
-            && let ExprKind::Block(block, _) = block.kind
-            && !is_inside_let_else(cx.tcx, expr)
-            && let [.., final_stmt] = block.stmts
-            && final_stmt.hir_id != stmt.hir_id
-            && !is_from_proc_macro(cx, expr)
-            && !stmt_needs_never_type(cx, stmt.hir_id)
-        {
-            span_lint_and_sugg(
-                cx,
-                NEEDLESS_RETURN_WITH_QUESTION_MARK,
-                expr.span.until(ret.span),
-                "unneeded `return` statement with `?` operator",
-                "remove it",
-                String::new(),
-                Applicability::MachineApplicable,
-            );
-        }
-    }
-
-    fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) {
-        // we need both a let-binding stmt and an expr
-        if let Some(retexpr) = block.expr
-            && let Some(stmt) = block.stmts.iter().last()
-            && let StmtKind::Let(local) = &stmt.kind
-            && local.ty.is_none()
-            && cx.tcx.hir_attrs(local.hir_id).is_empty()
-            && let Some(initexpr) = &local.init
-            && let PatKind::Binding(_, local_id, _, _) = local.pat.kind
-            && path_to_local_id(retexpr, local_id)
-            && (cx.sess().edition() >= Edition::Edition2024 || !last_statement_borrows(cx, initexpr))
-            && !initexpr.span.in_external_macro(cx.sess().source_map())
-            && !retexpr.span.in_external_macro(cx.sess().source_map())
-            && !local.span.from_expansion()
-            && !span_contains_cfg(cx, stmt.span.between(retexpr.span))
-        {
-            span_lint_hir_and_then(
-                cx,
-                LET_AND_RETURN,
-                retexpr.hir_id,
-                retexpr.span,
-                "returning the result of a `let` binding from a block",
-                |err| {
-                    err.span_label(local.span, "unnecessary `let` binding");
-
-                    if let Some(src) = initexpr.span.get_source_text(cx) {
-                        let sugg = if binary_expr_needs_parentheses(initexpr) {
-                            if has_enclosing_paren(&src) {
-                                src.to_owned()
-                            } else {
-                                format!("({src})")
-                            }
-                        } else if !cx.typeck_results().expr_adjustments(retexpr).is_empty() {
-                            if has_enclosing_paren(&src) {
-                                format!("{src} as _")
-                            } else {
-                                format!("({src}) as _")
-                            }
-                        } else {
-                            src.to_owned()
-                        };
-                        err.multipart_suggestion(
-                            "return the expression directly",
-                            vec![(local.span, String::new()), (retexpr.span, sugg)],
-                            Applicability::MachineApplicable,
-                        );
-                    } else {
-                        err.span_help(initexpr.span, "this expression can be directly returned");
-                    }
-                },
-            );
-        }
-    }
-
-    fn check_fn(
-        &mut self,
-        cx: &LateContext<'tcx>,
-        kind: FnKind<'tcx>,
-        _: &'tcx FnDecl<'tcx>,
-        body: &'tcx Body<'tcx>,
-        sp: Span,
-        _: LocalDefId,
-    ) {
-        if sp.from_expansion() {
-            return;
-        }
-
-        match kind {
-            FnKind::Closure => {
-                // when returning without value in closure, replace this `return`
-                // with an empty block to prevent invalid suggestion (see #6501)
-                let replacement = if let ExprKind::Ret(None) = &body.value.kind {
-                    RetReplacement::Block
-                } else {
-                    RetReplacement::Empty
-                };
-                check_final_expr(cx, body.value, vec![], replacement, None);
-            },
-            FnKind::ItemFn(..) | FnKind::Method(..) => {
-                check_block_return(cx, &body.value.kind, sp, vec![]);
-            },
-        }
-    }
-}
-
-// if `expr` is a block, check if there are needless returns in it
-fn check_block_return<'tcx>(cx: &LateContext<'tcx>, expr_kind: &ExprKind<'tcx>, sp: Span, mut semi_spans: Vec<Span>) {
-    if let ExprKind::Block(block, _) = expr_kind {
-        if let Some(block_expr) = block.expr {
-            check_final_expr(cx, block_expr, semi_spans, RetReplacement::Empty, None);
-        } else if let Some(stmt) = block.stmts.iter().last() {
-            match stmt.kind {
-                StmtKind::Expr(expr) => {
-                    check_final_expr(cx, expr, semi_spans, RetReplacement::Empty, None);
-                },
-                StmtKind::Semi(semi_expr) => {
-                    // Remove ending semicolons and any whitespace ' ' in between.
-                    // Without `return`, the suggestion might not compile if the semicolon is retained
-                    if let Some(semi_span) = stmt.span.trim_start(semi_expr.span) {
-                        let semi_span_to_remove =
-                            span_find_starting_semi(cx.sess().source_map(), semi_span.with_hi(sp.hi()));
-                        semi_spans.push(semi_span_to_remove);
-                    }
-                    check_final_expr(cx, semi_expr, semi_spans, RetReplacement::Empty, None);
-                },
-                _ => (),
-            }
-        }
-    }
-}
-
-fn check_final_expr<'tcx>(
-    cx: &LateContext<'tcx>,
-    expr: &'tcx Expr<'tcx>,
-    semi_spans: Vec<Span>, /* containing all the places where we would need to remove semicolons if finding an
-                            * needless return */
-    replacement: RetReplacement<'tcx>,
-    match_ty_opt: Option<Ty<'_>>,
-) {
-    let peeled_drop_expr = expr.peel_drop_temps();
-    match &peeled_drop_expr.kind {
-        // simple return is always "bad"
-        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)
-            } else {
-                peeled_drop_expr.span
-            };
-
-            let replacement = if let Some(inner_expr) = inner {
-                // if desugar of `do yeet`, don't lint
-                if let ExprKind::Call(path_expr, [_]) = inner_expr.kind
-                    && let ExprKind::Path(QPath::LangItem(LangItem::TryTraitFromYeet, ..)) = path_expr.kind
-                {
-                    return;
-                }
-
-                let mut applicability = Applicability::MachineApplicable;
-                let (snippet, _) = snippet_with_context(cx, inner_expr.span, ret_span.ctxt(), "..", &mut applicability);
-                if binary_expr_needs_parentheses(inner_expr) {
-                    RetReplacement::NeedsPar(snippet, applicability)
-                } else {
-                    RetReplacement::Expr(snippet, applicability)
-                }
-            } else {
-                match match_ty_opt {
-                    Some(match_ty) => {
-                        match match_ty.kind() {
-                            // If the code got till here with
-                            // tuple not getting detected before it,
-                            // then we are sure it's going to be Unit
-                            // type
-                            ty::Tuple(_) => RetReplacement::Unit,
-                            // We don't want to anything in this case
-                            // cause we can't predict what the user would
-                            // want here
-                            _ => return,
-                        }
-                    },
-                    None => replacement,
-                }
-            };
-
-            if inner.is_some_and(|inner| leaks_droppable_temporary_with_limited_lifetime(cx, inner)) {
-                return;
-            }
-
-            if ret_span.from_expansion() || is_from_proc_macro(cx, expr) {
-                return;
-            }
-
-            // Returns may be used to turn an expression into a statement in rustc's AST.
-            // This allows the addition of attributes, like `#[allow]` (See: clippy#9361)
-            // `#[expect(clippy::needless_return)]` needs to be handled separately to
-            // actually fulfill the expectation (clippy::#12998)
-            match cx.tcx.hir_attrs(expr.hir_id) {
-                [] => {},
-                [attr] => {
-                    if matches!(Level::from_attr(attr), Some((Level::Expect, _)))
-                        && let metas = attr.meta_item_list()
-                        && let Some(lst) = metas
-                        && let [MetaItemInner::MetaItem(meta_item), ..] = lst.as_slice()
-                        && let [tool, lint_name] = meta_item.path.segments.as_slice()
-                        && tool.ident.name == sym::clippy
-                        && matches!(
-                            lint_name.ident.name,
-                            sym::needless_return | sym::style | sym::all | sym::warnings
-                        )
-                    {
-                        // This is an expectation of the `needless_return` lint
-                    } else {
-                        return;
-                    }
-                },
-                _ => return,
-            }
-
-            emit_return_lint(
-                cx,
-                peeled_drop_expr.span,
-                ret_span,
-                semi_spans,
-                &replacement,
-                expr.hir_id,
-            );
-        },
-        ExprKind::If(_, then, else_clause_opt) => {
-            check_block_return(cx, &then.kind, peeled_drop_expr.span, semi_spans.clone());
-            if let Some(else_clause) = else_clause_opt {
-                // The `RetReplacement` won't be used there as `else_clause` will be either a block or
-                // a `if` expression.
-                check_final_expr(cx, else_clause, semi_spans, RetReplacement::Empty, match_ty_opt);
-            }
-        },
-        // a match expr, check all arms
-        // an if/if let expr, check both exprs
-        // note, if without else is going to be a type checking error anyways
-        // (except for unit type functions) so we don't match it
-        ExprKind::Match(_, arms, MatchSource::Normal) => {
-            let match_ty = cx.typeck_results().expr_ty(peeled_drop_expr);
-            for arm in *arms {
-                check_final_expr(cx, arm.body, semi_spans.clone(), RetReplacement::Unit, Some(match_ty));
-            }
-        },
-        // if it's a whole block, check it
-        other_expr_kind => check_block_return(cx, other_expr_kind, peeled_drop_expr.span, semi_spans),
-    }
-}
-
-fn emit_return_lint(
-    cx: &LateContext<'_>,
-    lint_span: Span,
-    ret_span: Span,
-    semi_spans: Vec<Span>,
-    replacement: &RetReplacement<'_>,
-    at: HirId,
-) {
-    span_lint_hir_and_then(
-        cx,
-        NEEDLESS_RETURN,
-        at,
-        lint_span,
-        "unneeded `return` statement",
-        |diag| {
-            let suggestions = std::iter::once((ret_span, replacement.to_string()))
-                .chain(semi_spans.into_iter().map(|span| (span, String::new())))
-                .collect();
-
-            diag.multipart_suggestion_verbose(replacement.sugg_help(), suggestions, replacement.applicability());
-        },
-    );
-}
-
-fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
-    for_each_expr(cx, expr, |e| {
-        if let Some(def_id) = fn_def_id(cx, e)
-            && cx
-                .tcx
-                .fn_sig(def_id)
-                .instantiate_identity()
-                .skip_binder()
-                .output()
-                .walk()
-                .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(re) if !re.is_static()))
-        {
-            ControlFlow::Break(())
-        } else {
-            ControlFlow::Continue(())
-        }
-    })
-    .is_some()
-}
-
-// Go backwards while encountering whitespace and extend the given Span to that point.
-fn extend_span_to_previous_non_ws(cx: &LateContext<'_>, sp: Span) -> Span {
-    if let Ok(prev_source) = cx.sess().source_map().span_to_prev_source(sp) {
-        let ws = [b' ', b'\t', b'\n'];
-        if let Some(non_ws_pos) = prev_source.bytes().rposition(|c| !ws.contains(&c)) {
-            let len = prev_source.len() - non_ws_pos - 1;
-            return sp.with_lo(sp.lo() - BytePos::from_usize(len));
-        }
-    }
-
-    sp
-}
diff --git a/src/tools/clippy/clippy_lints/src/returns/let_and_return.rs b/src/tools/clippy/clippy_lints/src/returns/let_and_return.rs
new file mode 100644
index 00000000000..e2002fb36e5
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/returns/let_and_return.rs
@@ -0,0 +1,86 @@
+use clippy_utils::diagnostics::span_lint_hir_and_then;
+use clippy_utils::source::SpanRangeExt;
+use clippy_utils::sugg::has_enclosing_paren;
+use clippy_utils::visitors::for_each_expr;
+use clippy_utils::{binary_expr_needs_parentheses, fn_def_id, path_to_local_id, span_contains_cfg};
+use core::ops::ControlFlow;
+use rustc_errors::Applicability;
+use rustc_hir::{Block, Expr, PatKind, StmtKind};
+use rustc_lint::{LateContext, LintContext};
+use rustc_middle::ty::GenericArgKind;
+use rustc_span::edition::Edition;
+
+use super::LET_AND_RETURN;
+
+pub(super) fn check_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'_>) {
+    // we need both a let-binding stmt and an expr
+    if let Some(retexpr) = block.expr
+        && let Some(stmt) = block.stmts.last()
+        && let StmtKind::Let(local) = &stmt.kind
+        && local.ty.is_none()
+        && cx.tcx.hir_attrs(local.hir_id).is_empty()
+        && let Some(initexpr) = &local.init
+        && let PatKind::Binding(_, local_id, _, _) = local.pat.kind
+        && path_to_local_id(retexpr, local_id)
+        && (cx.sess().edition() >= Edition::Edition2024 || !last_statement_borrows(cx, initexpr))
+        && !initexpr.span.in_external_macro(cx.sess().source_map())
+        && !retexpr.span.in_external_macro(cx.sess().source_map())
+        && !local.span.from_expansion()
+        && !span_contains_cfg(cx, stmt.span.between(retexpr.span))
+    {
+        span_lint_hir_and_then(
+            cx,
+            LET_AND_RETURN,
+            retexpr.hir_id,
+            retexpr.span,
+            "returning the result of a `let` binding from a block",
+            |err| {
+                err.span_label(local.span, "unnecessary `let` binding");
+
+                if let Some(src) = initexpr.span.get_source_text(cx) {
+                    let sugg = if binary_expr_needs_parentheses(initexpr) {
+                        if has_enclosing_paren(&src) {
+                            src.to_owned()
+                        } else {
+                            format!("({src})")
+                        }
+                    } else if !cx.typeck_results().expr_adjustments(retexpr).is_empty() {
+                        if has_enclosing_paren(&src) {
+                            format!("{src} as _")
+                        } else {
+                            format!("({src}) as _")
+                        }
+                    } else {
+                        src.to_owned()
+                    };
+                    err.multipart_suggestion(
+                        "return the expression directly",
+                        vec![(local.span, String::new()), (retexpr.span, sugg)],
+                        Applicability::MachineApplicable,
+                    );
+                } else {
+                    err.span_help(initexpr.span, "this expression can be directly returned");
+                }
+            },
+        );
+    }
+}
+fn last_statement_borrows<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
+    for_each_expr(cx, expr, |e| {
+        if let Some(def_id) = fn_def_id(cx, e)
+            && cx
+                .tcx
+                .fn_sig(def_id)
+                .instantiate_identity()
+                .skip_binder()
+                .output()
+                .walk()
+                .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(re) if !re.is_static()))
+        {
+            ControlFlow::Break(())
+        } else {
+            ControlFlow::Continue(())
+        }
+    })
+    .is_some()
+}
diff --git a/src/tools/clippy/clippy_lints/src/returns/mod.rs b/src/tools/clippy/clippy_lints/src/returns/mod.rs
new file mode 100644
index 00000000000..47c6332b9b8
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/returns/mod.rs
@@ -0,0 +1,140 @@
+use rustc_hir::intravisit::FnKind;
+use rustc_hir::{Block, Body, FnDecl, Stmt};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::declare_lint_pass;
+use rustc_span::Span;
+use rustc_span::def_id::LocalDefId;
+
+mod let_and_return;
+mod needless_return;
+mod needless_return_with_question_mark;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for `let`-bindings, which are subsequently
+    /// returned.
+    ///
+    /// ### Why is this bad?
+    /// It is just extraneous code. Remove it to make your code
+    /// more rusty.
+    ///
+    /// ### Known problems
+    /// In the case of some temporaries, e.g. locks, eliding the variable binding could lead
+    /// to deadlocks. See [this issue](https://github.com/rust-lang/rust/issues/37612).
+    /// This could become relevant if the code is later changed to use the code that would have been
+    /// bound without first assigning it to a let-binding.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// fn foo() -> String {
+    ///     let x = String::new();
+    ///     x
+    /// }
+    /// ```
+    /// instead, use
+    /// ```no_run
+    /// fn foo() -> String {
+    ///     String::new()
+    /// }
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub LET_AND_RETURN,
+    style,
+    "creating a let-binding and then immediately returning it like `let x = expr; x` at the end of a block"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for return statements at the end of a block.
+    ///
+    /// ### Why is this bad?
+    /// Removing the `return` and semicolon will make the code
+    /// more rusty.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// fn foo(x: usize) -> usize {
+    ///     return x;
+    /// }
+    /// ```
+    /// simplify to
+    /// ```no_run
+    /// fn foo(x: usize) -> usize {
+    ///     x
+    /// }
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub NEEDLESS_RETURN,
+    // This lint requires some special handling in `check_final_expr` for `#[expect]`.
+    // This handling needs to be updated if the group gets changed. This should also
+    // be caught by tests.
+    style,
+    "using a return statement like `return expr;` where an expression would suffice"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for return statements on `Err` paired with the `?` operator.
+    ///
+    /// ### Why is this bad?
+    /// The `return` is unnecessary.
+    ///
+    /// Returns may be used to add attributes to the return expression. Return
+    /// statements with attributes are therefore be accepted by this lint.
+    ///
+    /// ### Example
+    /// ```rust,ignore
+    /// fn foo(x: usize) -> Result<(), Box<dyn Error>> {
+    ///     if x == 0 {
+    ///         return Err(...)?;
+    ///     }
+    ///     Ok(())
+    /// }
+    /// ```
+    /// simplify to
+    /// ```rust,ignore
+    /// fn foo(x: usize) -> Result<(), Box<dyn Error>> {
+    ///     if x == 0 {
+    ///         Err(...)?;
+    ///     }
+    ///     Ok(())
+    /// }
+    /// ```
+    /// if paired with `try_err`, use instead:
+    /// ```rust,ignore
+    /// fn foo(x: usize) -> Result<(), Box<dyn Error>> {
+    ///     if x == 0 {
+    ///         return Err(...);
+    ///     }
+    ///     Ok(())
+    /// }
+    /// ```
+    #[clippy::version = "1.73.0"]
+    pub NEEDLESS_RETURN_WITH_QUESTION_MARK,
+    style,
+    "using a return statement like `return Err(expr)?;` where removing it would suffice"
+}
+
+declare_lint_pass!(Return => [LET_AND_RETURN, NEEDLESS_RETURN, NEEDLESS_RETURN_WITH_QUESTION_MARK]);
+
+impl<'tcx> LateLintPass<'tcx> for Return {
+    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
+        needless_return_with_question_mark::check_stmt(cx, stmt);
+    }
+
+    fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'_>) {
+        let_and_return::check_block(cx, block);
+    }
+
+    fn check_fn(
+        &mut self,
+        cx: &LateContext<'tcx>,
+        kind: FnKind<'tcx>,
+        _: &'tcx FnDecl<'tcx>,
+        body: &'tcx Body<'tcx>,
+        sp: Span,
+        _: LocalDefId,
+    ) {
+        needless_return::check_fn(cx, kind, body, sp);
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/returns/needless_return.rs b/src/tools/clippy/clippy_lints/src/returns/needless_return.rs
new file mode 100644
index 00000000000..04739fc1b22
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/returns/needless_return.rs
@@ -0,0 +1,269 @@
+use clippy_utils::diagnostics::span_lint_hir_and_then;
+use clippy_utils::source::snippet_with_context;
+use clippy_utils::{
+    binary_expr_needs_parentheses, is_from_proc_macro, leaks_droppable_temporary_with_limited_lifetime,
+    span_contains_cfg, span_find_starting_semi, sym,
+};
+use rustc_ast::MetaItemInner;
+use rustc_errors::Applicability;
+use rustc_hir::intravisit::FnKind;
+use rustc_hir::{Body, Expr, ExprKind, HirId, LangItem, MatchSource, QPath, StmtKind};
+use rustc_lint::{LateContext, Level, LintContext};
+use rustc_middle::ty::{self, Ty};
+use rustc_span::{BytePos, Pos, Span};
+use std::borrow::Cow;
+use std::fmt::Display;
+
+use super::NEEDLESS_RETURN;
+
+#[derive(PartialEq, Eq)]
+enum RetReplacement<'tcx> {
+    Empty,
+    Block,
+    Unit,
+    NeedsPar(Cow<'tcx, str>, Applicability),
+    Expr(Cow<'tcx, str>, Applicability),
+}
+
+impl RetReplacement<'_> {
+    fn sugg_help(&self) -> &'static str {
+        match self {
+            Self::Empty | Self::Expr(..) => "remove `return`",
+            Self::Block => "replace `return` with an empty block",
+            Self::Unit => "replace `return` with a unit value",
+            Self::NeedsPar(..) => "remove `return` and wrap the sequence with parentheses",
+        }
+    }
+
+    fn applicability(&self) -> Applicability {
+        match self {
+            Self::Expr(_, ap) | Self::NeedsPar(_, ap) => *ap,
+            _ => Applicability::MachineApplicable,
+        }
+    }
+}
+
+impl Display for RetReplacement<'_> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Self::Empty => f.write_str(""),
+            Self::Block => f.write_str("{}"),
+            Self::Unit => f.write_str("()"),
+            Self::NeedsPar(inner, _) => write!(f, "({inner})"),
+            Self::Expr(inner, _) => write!(f, "{inner}"),
+        }
+    }
+}
+
+pub(super) fn check_fn<'tcx>(cx: &LateContext<'tcx>, kind: FnKind<'tcx>, body: &'tcx Body<'tcx>, sp: Span) {
+    if sp.from_expansion() {
+        return;
+    }
+
+    match kind {
+        FnKind::Closure => {
+            // when returning without value in closure, replace this `return`
+            // with an empty block to prevent invalid suggestion (see #6501)
+            let replacement = if let ExprKind::Ret(None) = &body.value.kind {
+                RetReplacement::Block
+            } else {
+                RetReplacement::Empty
+            };
+            check_final_expr(cx, body.value, vec![], replacement, None);
+        },
+        FnKind::ItemFn(..) | FnKind::Method(..) => {
+            check_block_return(cx, &body.value.kind, sp, vec![]);
+        },
+    }
+}
+
+// if `expr` is a block, check if there are needless returns in it
+fn check_block_return<'tcx>(cx: &LateContext<'tcx>, expr_kind: &ExprKind<'tcx>, sp: Span, mut semi_spans: Vec<Span>) {
+    if let ExprKind::Block(block, _) = expr_kind {
+        if let Some(block_expr) = block.expr {
+            check_final_expr(cx, block_expr, semi_spans, RetReplacement::Empty, None);
+        } else if let Some(stmt) = block.stmts.last() {
+            if span_contains_cfg(
+                cx,
+                Span::between(
+                    stmt.span,
+                    cx.sess().source_map().end_point(block.span), // the closing brace of the block
+                ),
+            ) {
+                return;
+            }
+            match stmt.kind {
+                StmtKind::Expr(expr) => {
+                    check_final_expr(cx, expr, semi_spans, RetReplacement::Empty, None);
+                },
+                StmtKind::Semi(semi_expr) => {
+                    // Remove ending semicolons and any whitespace ' ' in between.
+                    // Without `return`, the suggestion might not compile if the semicolon is retained
+                    if let Some(semi_span) = stmt.span.trim_start(semi_expr.span) {
+                        let semi_span_to_remove =
+                            span_find_starting_semi(cx.sess().source_map(), semi_span.with_hi(sp.hi()));
+                        semi_spans.push(semi_span_to_remove);
+                    }
+                    check_final_expr(cx, semi_expr, semi_spans, RetReplacement::Empty, None);
+                },
+                _ => (),
+            }
+        }
+    }
+}
+
+fn check_final_expr<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'tcx>,
+    semi_spans: Vec<Span>, /* containing all the places where we would need to remove semicolons if finding an
+                            * needless return */
+    replacement: RetReplacement<'tcx>,
+    match_ty_opt: Option<Ty<'_>>,
+) {
+    let peeled_drop_expr = expr.peel_drop_temps();
+    match &peeled_drop_expr.kind {
+        // simple return is always "bad"
+        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)
+            } else {
+                peeled_drop_expr.span
+            };
+
+            let replacement = if let Some(inner_expr) = inner {
+                // if desugar of `do yeet`, don't lint
+                if let ExprKind::Call(path_expr, [_]) = inner_expr.kind
+                    && let ExprKind::Path(QPath::LangItem(LangItem::TryTraitFromYeet, ..)) = path_expr.kind
+                {
+                    return;
+                }
+
+                let mut applicability = Applicability::MachineApplicable;
+                let (snippet, _) = snippet_with_context(cx, inner_expr.span, ret_span.ctxt(), "..", &mut applicability);
+                if binary_expr_needs_parentheses(inner_expr) {
+                    RetReplacement::NeedsPar(snippet, applicability)
+                } else {
+                    RetReplacement::Expr(snippet, applicability)
+                }
+            } else {
+                match match_ty_opt {
+                    Some(match_ty) => {
+                        match match_ty.kind() {
+                            // If the code got till here with
+                            // tuple not getting detected before it,
+                            // then we are sure it's going to be Unit
+                            // type
+                            ty::Tuple(_) => RetReplacement::Unit,
+                            // We don't want to anything in this case
+                            // cause we can't predict what the user would
+                            // want here
+                            _ => return,
+                        }
+                    },
+                    None => replacement,
+                }
+            };
+
+            if inner.is_some_and(|inner| leaks_droppable_temporary_with_limited_lifetime(cx, inner)) {
+                return;
+            }
+
+            if ret_span.from_expansion() || is_from_proc_macro(cx, expr) {
+                return;
+            }
+
+            // Returns may be used to turn an expression into a statement in rustc's AST.
+            // This allows the addition of attributes, like `#[allow]` (See: clippy#9361)
+            // `#[expect(clippy::needless_return)]` needs to be handled separately to
+            // actually fulfill the expectation (clippy::#12998)
+            match cx.tcx.hir_attrs(expr.hir_id) {
+                [] => {},
+                [attr] => {
+                    if matches!(Level::from_attr(attr), Some((Level::Expect, _)))
+                        && let metas = attr.meta_item_list()
+                        && let Some(lst) = metas
+                        && let [MetaItemInner::MetaItem(meta_item), ..] = lst.as_slice()
+                        && let [tool, lint_name] = meta_item.path.segments.as_slice()
+                        && tool.ident.name == sym::clippy
+                        && matches!(
+                            lint_name.ident.name,
+                            sym::needless_return | sym::style | sym::all | sym::warnings
+                        )
+                    {
+                        // This is an expectation of the `needless_return` lint
+                    } else {
+                        return;
+                    }
+                },
+                _ => return,
+            }
+
+            emit_return_lint(
+                cx,
+                peeled_drop_expr.span,
+                ret_span,
+                semi_spans,
+                &replacement,
+                expr.hir_id,
+            );
+        },
+        ExprKind::If(_, then, else_clause_opt) => {
+            check_block_return(cx, &then.kind, peeled_drop_expr.span, semi_spans.clone());
+            if let Some(else_clause) = else_clause_opt {
+                // The `RetReplacement` won't be used there as `else_clause` will be either a block or
+                // a `if` expression.
+                check_final_expr(cx, else_clause, semi_spans, RetReplacement::Empty, match_ty_opt);
+            }
+        },
+        // a match expr, check all arms
+        // an if/if let expr, check both exprs
+        // note, if without else is going to be a type checking error anyways
+        // (except for unit type functions) so we don't match it
+        ExprKind::Match(_, arms, MatchSource::Normal) => {
+            let match_ty = cx.typeck_results().expr_ty(peeled_drop_expr);
+            for arm in *arms {
+                check_final_expr(cx, arm.body, semi_spans.clone(), RetReplacement::Unit, Some(match_ty));
+            }
+        },
+        // if it's a whole block, check it
+        other_expr_kind => check_block_return(cx, other_expr_kind, peeled_drop_expr.span, semi_spans),
+    }
+}
+
+fn emit_return_lint(
+    cx: &LateContext<'_>,
+    lint_span: Span,
+    ret_span: Span,
+    semi_spans: Vec<Span>,
+    replacement: &RetReplacement<'_>,
+    at: HirId,
+) {
+    span_lint_hir_and_then(
+        cx,
+        NEEDLESS_RETURN,
+        at,
+        lint_span,
+        "unneeded `return` statement",
+        |diag| {
+            let suggestions = std::iter::once((ret_span, replacement.to_string()))
+                .chain(semi_spans.into_iter().map(|span| (span, String::new())))
+                .collect();
+
+            diag.multipart_suggestion_verbose(replacement.sugg_help(), suggestions, replacement.applicability());
+        },
+    );
+}
+
+// Go backwards while encountering whitespace and extend the given Span to that point.
+fn extend_span_to_previous_non_ws(cx: &LateContext<'_>, sp: Span) -> Span {
+    if let Ok(prev_source) = cx.sess().source_map().span_to_prev_source(sp) {
+        let ws = [b' ', b'\t', b'\n'];
+        if let Some(non_ws_pos) = prev_source.bytes().rposition(|c| !ws.contains(&c)) {
+            let len = prev_source.len() - non_ws_pos - 1;
+            return sp.with_lo(sp.lo() - BytePos::from_usize(len));
+        }
+    }
+
+    sp
+}
diff --git a/src/tools/clippy/clippy_lints/src/returns/needless_return_with_question_mark.rs b/src/tools/clippy/clippy_lints/src/returns/needless_return_with_question_mark.rs
new file mode 100644
index 00000000000..c05038cd1e5
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/returns/needless_return_with_question_mark.rs
@@ -0,0 +1,60 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::{is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, path_res};
+use rustc_errors::Applicability;
+use rustc_hir::LangItem::ResultErr;
+use rustc_hir::{ExprKind, HirId, ItemKind, MatchSource, Node, OwnerNode, Stmt, StmtKind};
+use rustc_lint::{LateContext, LintContext};
+use rustc_middle::ty::adjustment::Adjust;
+
+use super::NEEDLESS_RETURN_WITH_QUESTION_MARK;
+
+pub(super) fn check_stmt<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
+    if !stmt.span.in_external_macro(cx.sess().source_map())
+        && let StmtKind::Semi(expr) = stmt.kind
+        && let ExprKind::Ret(Some(ret)) = expr.kind
+        // return Err(...)? desugars to a match
+        // over a Err(...).branch()
+        // which breaks down to a branch call, with the callee being
+        // the constructor of the Err variant
+        && let ExprKind::Match(maybe_cons, _, MatchSource::TryDesugar(_)) = ret.kind
+        && let ExprKind::Call(_, [maybe_result_err]) = maybe_cons.kind
+        && let ExprKind::Call(maybe_constr, _) = maybe_result_err.kind
+        && is_res_lang_ctor(cx, path_res(cx, maybe_constr), ResultErr)
+
+        // Ensure this is not the final stmt, otherwise removing it would cause a compile error
+        && let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir_get_parent_item(expr.hir_id))
+        && let ItemKind::Fn { body, .. } = item.kind
+        && let block = cx.tcx.hir_body(body).value
+        && let ExprKind::Block(block, _) = block.kind
+        && !is_inside_let_else(cx.tcx, expr)
+        && let [.., final_stmt] = block.stmts
+        && final_stmt.hir_id != stmt.hir_id
+        && !is_from_proc_macro(cx, expr)
+        && !stmt_needs_never_type(cx, stmt.hir_id)
+    {
+        span_lint_and_sugg(
+            cx,
+            NEEDLESS_RETURN_WITH_QUESTION_MARK,
+            expr.span.until(ret.span),
+            "unneeded `return` statement with `?` operator",
+            "remove it",
+            String::new(),
+            Applicability::MachineApplicable,
+        );
+    }
+}
+
+/// Checks if a return statement is "needed" in the middle of a block, or if it can be removed.
+/// This is the case when the enclosing block expression is coerced to some other type,
+/// which only works because of the never-ness of `return` expressions
+fn stmt_needs_never_type(cx: &LateContext<'_>, stmt_hir_id: HirId) -> bool {
+    cx.tcx
+        .hir_parent_iter(stmt_hir_id)
+        .find_map(|(_, node)| if let Node::Expr(expr) = node { Some(expr) } else { None })
+        .is_some_and(|e| {
+            cx.typeck_results()
+                .expr_adjustments(e)
+                .iter()
+                .any(|adjust| adjust.target != cx.tcx.types.unit && matches!(adjust.kind, Adjust::NeverToAny))
+        })
+}
diff --git a/src/tools/clippy/clippy_lints/src/same_name_method.rs b/src/tools/clippy/clippy_lints/src/same_name_method.rs
index 67eb71f7d07..b87751f4986 100644
--- a/src/tools/clippy/clippy_lints/src/same_name_method.rs
+++ b/src/tools/clippy/clippy_lints/src/same_name_method.rs
@@ -68,9 +68,9 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
                 let existing_name = map.get_mut(res).unwrap();
 
                 match of_trait {
-                    Some(trait_ref) => {
+                    Some(of_trait) => {
                         let mut methods_in_trait: BTreeSet<Symbol> = if let Node::TraitRef(TraitRef { path, .. }) =
-                            cx.tcx.hir_node(trait_ref.hir_ref_id)
+                            cx.tcx.hir_node(of_trait.trait_ref.hir_ref_id)
                             && let Res::Def(DefKind::Trait, did) = path.res
                         {
                             // FIXME: if
diff --git a/src/tools/clippy/clippy_lints/src/semicolon_block.rs b/src/tools/clippy/clippy_lints/src/semicolon_block.rs
index db91c57b181..371d62a0684 100644
--- a/src/tools/clippy/clippy_lints/src/semicolon_block.rs
+++ b/src/tools/clippy/clippy_lints/src/semicolon_block.rs
@@ -1,5 +1,6 @@
 use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::SpanRangeExt;
 use rustc_errors::Applicability;
 use rustc_hir::{Block, Expr, ExprKind, Stmt, StmtKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -82,6 +83,19 @@ impl SemicolonBlock {
         let insert_span = tail.span.source_callsite().shrink_to_hi();
         let remove_span = semi_span.with_lo(block.span.hi());
 
+        // If the block is surrounded by parens (`({ 0 });`), the author probably knows what
+        // they're doing and why, so don't get in their way.
+        //
+        // This has the additional benefit of stopping the block being parsed as a function call:
+        // ```
+        // fn foo() {
+        //     ({ 0 }); // if we remove this `;`, this will parse as a `({ 0 })(5);` function call
+        //     (5);
+        // }
+        if remove_span.check_source_text(cx, |src| src.contains(')')) {
+            return;
+        }
+
         if self.semicolon_inside_block_ignore_singleline && get_line(cx, remove_span) == get_line(cx, insert_span) {
             return;
         }
@@ -155,6 +169,11 @@ impl LateLintPass<'_> for SemicolonBlock {
                 kind: ExprKind::Block(block, _),
                 ..
             }) if !block.span.from_expansion() => {
+                let attrs = cx.tcx.hir_attrs(stmt.hir_id);
+                if !attrs.is_empty() && !cx.tcx.features().stmt_expr_attributes() {
+                    return;
+                }
+
                 if let Some(tail) = block.expr {
                     self.semicolon_inside_block(cx, block, tail, stmt.span);
                 }
diff --git a/src/tools/clippy/clippy_lints/src/serde_api.rs b/src/tools/clippy/clippy_lints/src/serde_api.rs
index 2de22e4b6a3..01c7f394b9a 100644
--- a/src/tools/clippy/clippy_lints/src/serde_api.rs
+++ b/src/tools/clippy/clippy_lints/src/serde_api.rs
@@ -26,16 +26,16 @@ 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(trait_ref),
+            of_trait: Some(of_trait),
             items,
             ..
         }) = item.kind
         {
-            let did = trait_ref.path.res.def_id();
+            let did = of_trait.trait_ref.path.res.def_id();
             if paths::SERDE_DE_VISITOR.matches(cx, did) {
                 let mut seen_str = None;
                 let mut seen_string = None;
-                for item in *items {
+                for item in items {
                     match cx.tcx.item_name(item.owner_id) {
                         sym::visit_str => seen_str = Some(cx.tcx.def_span(item.owner_id)),
                         sym::visit_string => seen_string = Some(cx.tcx.def_span(item.owner_id)),
diff --git a/src/tools/clippy/clippy_lints/src/size_of_ref.rs b/src/tools/clippy/clippy_lints/src/size_of_ref.rs
index 60d923bcd77..606e852aae9 100644
--- a/src/tools/clippy/clippy_lints/src/size_of_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/size_of_ref.rs
@@ -1,5 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::{path_def_id, peel_middle_ty_refs};
+use clippy_utils::path_def_id;
+use clippy_utils::ty::peel_and_count_ty_refs;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
@@ -59,7 +60,7 @@ impl LateLintPass<'_> for SizeOfRef {
             && let Some(def_id) = path_def_id(cx, path)
             && cx.tcx.is_diagnostic_item(sym::mem_size_of_val, def_id)
             && let arg_ty = cx.typeck_results().expr_ty(arg)
-            && peel_middle_ty_refs(arg_ty).1 > 1
+            && peel_and_count_ty_refs(arg_ty).1 > 1
         {
             span_lint_and_help(
                 cx,
diff --git a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs
index e9534bc63a6..8c4a50041e6 100644
--- a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs
+++ b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs
@@ -126,6 +126,8 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports {
             && !is_from_proc_macro(cx, &first_segment.ident)
             && !matches!(def_kind, DefKind::Macro(_))
             && let Some(last_segment) = path.segments.last()
+            && let Res::Def(DefKind::Mod, crate_def_id) = first_segment.res
+            && crate_def_id.is_crate_root()
         {
             let (lint, used_mod, replace_with) = match first_segment.ident.name {
                 sym::std => match cx.tcx.crate_name(def_id.krate) {
diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs
index 490e6c974ae..57d5900b045 100644
--- a/src/tools/clippy/clippy_lints/src/strings.rs
+++ b/src/tools/clippy/clippy_lints/src/strings.rs
@@ -457,7 +457,8 @@ impl<'tcx> LateLintPass<'tcx> for TrimSplitWhitespace {
 }
 
 fn is_one_of_trim_diagnostic_items(cx: &LateContext<'_>, trim_def_id: DefId) -> bool {
-    cx.tcx.is_diagnostic_item(sym::str_trim, trim_def_id)
-        || cx.tcx.is_diagnostic_item(sym::str_trim_start, trim_def_id)
-        || cx.tcx.is_diagnostic_item(sym::str_trim_end, trim_def_id)
+    matches!(
+        cx.tcx.get_diagnostic_name(trim_def_id),
+        Some(sym::str_trim | sym::str_trim_start | sym::str_trim_end)
+    )
 }
diff --git a/src/tools/clippy/clippy_lints/src/swap.rs b/src/tools/clippy/clippy_lints/src/swap.rs
index 5ecbb56925e..76ab3cdae22 100644
--- a/src/tools/clippy/clippy_lints/src/swap.rs
+++ b/src/tools/clippy/clippy_lints/src/swap.rs
@@ -380,7 +380,7 @@ impl<'tcx> IndexBinding<'_, 'tcx> {
         }
     }
 
-    fn is_used_other_than_swapping(&mut self, idx_ident: Ident) -> bool {
+    fn is_used_other_than_swapping(&self, idx_ident: Ident) -> bool {
         if Self::is_used_slice_indexed(self.swap1_idx, idx_ident)
             || Self::is_used_slice_indexed(self.swap2_idx, idx_ident)
         {
@@ -389,7 +389,7 @@ impl<'tcx> IndexBinding<'_, 'tcx> {
         self.is_used_after_swap(idx_ident)
     }
 
-    fn is_used_after_swap(&mut self, idx_ident: Ident) -> bool {
+    fn is_used_after_swap(&self, idx_ident: Ident) -> bool {
         let mut v = IndexBindingVisitor {
             idx: idx_ident,
             suggest_span: self.suggest_span,
diff --git a/src/tools/clippy/clippy_lints/src/to_string_trait_impl.rs b/src/tools/clippy/clippy_lints/src/to_string_trait_impl.rs
index 9596b85664b..303f6028bd5 100644
--- a/src/tools/clippy/clippy_lints/src/to_string_trait_impl.rs
+++ b/src/tools/clippy/clippy_lints/src/to_string_trait_impl.rs
@@ -48,10 +48,10 @@ declare_lint_pass!(ToStringTraitImpl => [TO_STRING_TRAIT_IMPL]);
 impl<'tcx> LateLintPass<'tcx> for ToStringTraitImpl {
     fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'tcx>) {
         if let ItemKind::Impl(Impl {
-            of_trait: Some(trait_ref),
+            of_trait: Some(of_trait),
             ..
         }) = it.kind
-            && let Some(trait_did) = trait_ref.trait_def_id()
+            && let Some(trait_did) = of_trait.trait_ref.trait_def_id()
             && cx.tcx.is_diagnostic_item(sym::ToString, trait_did)
         {
             span_lint_and_help(
diff --git a/src/tools/clippy/clippy_lints/src/toplevel_ref_arg.rs b/src/tools/clippy/clippy_lints/src/toplevel_ref_arg.rs
new file mode 100644
index 00000000000..074b79263d3
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/toplevel_ref_arg.rs
@@ -0,0 +1,119 @@
+use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
+use clippy_utils::source::{snippet, snippet_with_context};
+use clippy_utils::sugg::Sugg;
+use clippy_utils::{is_lint_allowed, iter_input_pats};
+use rustc_errors::Applicability;
+use rustc_hir::intravisit::FnKind;
+use rustc_hir::{BindingMode, Body, ByRef, FnDecl, Mutability, PatKind, Stmt, StmtKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::declare_lint_pass;
+use rustc_span::Span;
+use rustc_span::def_id::LocalDefId;
+
+use crate::ref_patterns::REF_PATTERNS;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for function arguments and let bindings denoted as
+    /// `ref`.
+    ///
+    /// ### Why is this bad?
+    /// The `ref` declaration makes the function take an owned
+    /// value, but turns the argument into a reference (which means that the value
+    /// is destroyed when exiting the function). This adds not much value: either
+    /// take a reference type, or take an owned value and create references in the
+    /// body.
+    ///
+    /// For let bindings, `let x = &foo;` is preferred over `let ref x = foo`. The
+    /// type of `x` is more obvious with the former.
+    ///
+    /// ### Known problems
+    /// If the argument is dereferenced within the function,
+    /// removing the `ref` will lead to errors. This can be fixed by removing the
+    /// dereferences, e.g., changing `*x` to `x` within the function.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// fn foo(ref _x: u8) {}
+    /// ```
+    ///
+    /// Use instead:
+    /// ```no_run
+    /// fn foo(_x: &u8) {}
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub TOPLEVEL_REF_ARG,
+    style,
+    "an entire binding declared as `ref`, in a function argument or a `let` statement"
+}
+
+declare_lint_pass!(ToplevelRefArg => [TOPLEVEL_REF_ARG]);
+
+impl<'tcx> LateLintPass<'tcx> for ToplevelRefArg {
+    fn check_fn(
+        &mut self,
+        cx: &LateContext<'tcx>,
+        k: FnKind<'tcx>,
+        decl: &'tcx FnDecl<'_>,
+        body: &'tcx Body<'_>,
+        _: Span,
+        _: LocalDefId,
+    ) {
+        if !matches!(k, FnKind::Closure) {
+            for arg in iter_input_pats(decl, body) {
+                if let PatKind::Binding(BindingMode(ByRef::Yes(_), _), ..) = arg.pat.kind
+                    && is_lint_allowed(cx, REF_PATTERNS, arg.pat.hir_id)
+                    && !arg.span.in_external_macro(cx.tcx.sess.source_map())
+                {
+                    span_lint_hir(
+                        cx,
+                        TOPLEVEL_REF_ARG,
+                        arg.hir_id,
+                        arg.pat.span,
+                        "`ref` directly on a function parameter does not prevent taking ownership of the passed argument. \
+                            Consider using a reference type instead",
+                    );
+                }
+            }
+        }
+    }
+
+    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
+        if let StmtKind::Let(local) = stmt.kind
+            && let PatKind::Binding(BindingMode(ByRef::Yes(mutabl), _), .., name, None) = local.pat.kind
+            && let Some(init) = local.init
+            // Do not emit if clippy::ref_patterns is not allowed to avoid having two lints for the same issue.
+            && is_lint_allowed(cx, REF_PATTERNS, local.pat.hir_id)
+            && !stmt.span.in_external_macro(cx.tcx.sess.source_map())
+        {
+            let ctxt = local.span.ctxt();
+            let mut app = Applicability::MachineApplicable;
+            let sugg_init = Sugg::hir_with_context(cx, init, ctxt, "..", &mut app);
+            let (mutopt, initref) = match mutabl {
+                Mutability::Mut => ("mut ", sugg_init.mut_addr()),
+                Mutability::Not => ("", sugg_init.addr()),
+            };
+            let tyopt = if let Some(ty) = local.ty {
+                let ty_snip = snippet_with_context(cx, ty.span, ctxt, "_", &mut app).0;
+                format!(": &{mutopt}{ty_snip}")
+            } else {
+                String::new()
+            };
+            span_lint_hir_and_then(
+                cx,
+                TOPLEVEL_REF_ARG,
+                init.hir_id,
+                local.pat.span,
+                "`ref` on an entire `let` pattern is discouraged, take a reference with `&` instead",
+                |diag| {
+                    diag.span_suggestion(
+                        stmt.span,
+                        "try",
+                        format!("let {name}{tyopt} = {initref};", name = snippet(cx, name.span, ".."),),
+                        app,
+                    );
+                },
+            );
+        }
+    }
+}
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 535c044f49e..97e68b3df94 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/eager_transmute.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/eager_transmute.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::{eq_expr_value, path_to_local, sym};
+use clippy_utils::{eq_expr_value, path_to_local_with_projections, sym};
 use rustc_abi::WrappingRange;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, Node};
@@ -63,11 +63,7 @@ fn binops_with_local(cx: &LateContext<'_>, local_expr: &Expr<'_>, expr: &Expr<'_
 /// Checks if an expression is a path to a local variable (with optional projections), e.g.
 /// `x.field[0].field2` would return true.
 fn is_local_with_projections(expr: &Expr<'_>) -> bool {
-    match expr.kind {
-        ExprKind::Path(_) => path_to_local(expr).is_some(),
-        ExprKind::Field(expr, _) | ExprKind::Index(expr, ..) => is_local_with_projections(expr),
-        _ => false,
-    }
+    path_to_local_with_projections(expr).is_some()
 }
 
 pub(super) fn check<'tcx>(
diff --git a/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs b/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs
index 08f36a2ed5d..543f3c45e14 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/missing_transmute_annotations.rs
@@ -1,8 +1,12 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use std::borrow::Cow;
+
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::{HasSession, SpanRangeExt as _};
 use rustc_errors::Applicability;
-use rustc_hir::{GenericArg, HirId, LetStmt, Node, Path, TyKind};
+use rustc_hir::{Expr, GenericArg, HirId, LetStmt, Node, Path, TyKind};
 use rustc_lint::LateContext;
-use rustc_middle::ty::Ty;
+use rustc_middle::ty::{self, Ty};
+use rustc_span::Span;
 
 use crate::transmute::MISSING_TRANSMUTE_ANNOTATIONS;
 
@@ -38,6 +42,7 @@ fn is_function_block(cx: &LateContext<'_>, expr_hir_id: HirId) -> bool {
 pub(super) fn check<'tcx>(
     cx: &LateContext<'tcx>,
     path: &Path<'tcx>,
+    arg: &Expr<'tcx>,
     from_ty: Ty<'tcx>,
     to_ty: Ty<'tcx>,
     expr_hir_id: HirId,
@@ -68,14 +73,48 @@ pub(super) fn check<'tcx>(
     } else if is_function_block(cx, expr_hir_id) {
         return false;
     }
-    span_lint_and_sugg(
+    let span = last.ident.span.with_hi(path.span.hi());
+    span_lint_and_then(
         cx,
         MISSING_TRANSMUTE_ANNOTATIONS,
-        last.ident.span.with_hi(path.span.hi()),
+        span,
         "transmute used without annotations",
-        "consider adding missing annotations",
-        format!("{}::<{from_ty}, {to_ty}>", last.ident),
-        Applicability::MaybeIncorrect,
+        |diag| {
+            let from_ty_no_name = ty_cannot_be_named(from_ty);
+            let to_ty_no_name = ty_cannot_be_named(to_ty);
+            if from_ty_no_name || to_ty_no_name {
+                let to_name = match (from_ty_no_name, to_ty_no_name) {
+                    (true, false) => maybe_name_by_expr(cx, arg.span, "the origin type"),
+                    (false, true) => "the destination type".into(),
+                    _ => "the source and destination types".into(),
+                };
+                diag.help(format!(
+                    "consider giving {to_name} a name, and adding missing type annotations"
+                ));
+            } else {
+                diag.span_suggestion(
+                    span,
+                    "consider adding missing annotations",
+                    format!("{}::<{from_ty}, {to_ty}>", last.ident),
+                    Applicability::MaybeIncorrect,
+                );
+            }
+        },
     );
     true
 }
+
+fn ty_cannot_be_named(ty: Ty<'_>) -> bool {
+    matches!(
+        ty.kind(),
+        ty::Alias(ty::AliasTyKind::Opaque | ty::AliasTyKind::Inherent, _)
+    )
+}
+
+fn maybe_name_by_expr<'a>(sess: &impl HasSession, span: Span, default: &'a str) -> Cow<'a, str> {
+    span.with_source_text(sess, |name| {
+        (name.len() + 9 < default.len()).then_some(format!("`{name}`'s type").into())
+    })
+    .flatten()
+    .unwrap_or(default.into())
+}
diff --git a/src/tools/clippy/clippy_lints/src/transmute/mod.rs b/src/tools/clippy/clippy_lints/src/transmute/mod.rs
index d5112e2c3f9..5fda388259a 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/mod.rs
@@ -105,7 +105,7 @@ declare_clippy_lint! {
     /// ```
     #[clippy::version = "pre 1.29.0"]
     pub CROSSPOINTER_TRANSMUTE,
-    complexity,
+    suspicious,
     "transmutes that have to or from types that are a pointer to the other"
 }
 
@@ -520,7 +520,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
                 | transmuting_null::check(cx, e, arg, to_ty)
                 | transmute_null_to_fn::check(cx, e, arg, to_ty)
                 | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, self.msrv)
-                | missing_transmute_annotations::check(cx, path, from_ty, to_ty, e.hir_id)
+                | missing_transmute_annotations::check(cx, path, arg, from_ty, to_ty, e.hir_id)
                 | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context)
                 | transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg, self.msrv)
                 | transmute_int_to_bool::check(cx, e, from_ty, to_ty, arg)
diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs
index f27aaa2fa77..2257aa1b73c 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs
@@ -1,6 +1,6 @@
 use super::TRANSMUTE_INT_TO_NON_ZERO;
-use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::sugg;
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::sugg::Sugg;
 use rustc_errors::Applicability;
 use rustc_hir::Expr;
 use rustc_lint::LateContext;
@@ -16,35 +16,24 @@ pub(super) fn check<'tcx>(
     to_ty: Ty<'tcx>,
     arg: &'tcx Expr<'_>,
 ) -> bool {
-    let tcx = cx.tcx;
-
-    let (ty::Int(_) | ty::Uint(_), ty::Adt(adt, substs)) = (&from_ty.kind(), to_ty.kind()) else {
-        return false;
-    };
-
-    if !tcx.is_diagnostic_item(sym::NonZero, adt.did()) {
-        return false;
+    if let ty::Int(_) | ty::Uint(_) = from_ty.kind()
+        && let ty::Adt(adt, substs) = to_ty.kind()
+        && cx.tcx.is_diagnostic_item(sym::NonZero, adt.did())
+        && let int_ty = substs.type_at(0)
+        && from_ty == int_ty
+    {
+        let arg = Sugg::hir(cx, arg, "..");
+        span_lint_and_sugg(
+            cx,
+            TRANSMUTE_INT_TO_NON_ZERO,
+            e.span,
+            format!("transmute from a `{from_ty}` to a `{}<{int_ty}>`", sym::NonZero),
+            "consider using",
+            format!("{}::{}({arg})", sym::NonZero, sym::new_unchecked),
+            Applicability::Unspecified,
+        );
+        true
+    } else {
+        false
     }
-
-    let int_ty = substs.type_at(0);
-    if from_ty != int_ty {
-        return false;
-    }
-
-    span_lint_and_then(
-        cx,
-        TRANSMUTE_INT_TO_NON_ZERO,
-        e.span,
-        format!("transmute from a `{from_ty}` to a `{}<{int_ty}>`", sym::NonZero),
-        |diag| {
-            let arg = sugg::Sugg::hir(cx, arg, "..");
-            diag.span_suggestion(
-                e.span,
-                "consider using",
-                format!("{}::{}({arg})", sym::NonZero, sym::new_unchecked),
-                Applicability::Unspecified,
-            );
-        },
-    );
-    true
 }
diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs
index e58212fae15..e67ab6a73d2 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs
@@ -28,16 +28,27 @@ pub(super) fn check<'tcx>(
                 format!("transmute from a pointer type (`{from_ty}`) to a reference type (`{to_ty}`)"),
                 |diag| {
                     let arg = sugg::Sugg::hir(cx, arg, "..");
-                    let (deref, cast) = if *mutbl == Mutability::Mut {
-                        ("&mut *", "*mut")
-                    } else {
-                        ("&*", "*const")
+                    let (deref, cast) = match mutbl {
+                        Mutability::Mut => ("&mut *", "*mut"),
+                        Mutability::Not => ("&*", "*const"),
                     };
                     let mut app = Applicability::MachineApplicable;
 
                     let sugg = if let Some(ty) = get_explicit_type(path) {
                         let ty_snip = snippet_with_applicability(cx, ty.span, "..", &mut app);
-                        if msrv.meets(cx, msrvs::POINTER_CAST) {
+                        if !to_ref_ty.is_sized(cx.tcx, cx.typing_env()) {
+                            // We can't suggest `.cast()`, because that requires `to_ref_ty` to be Sized.
+                            if from_ptr_ty.has_erased_regions() {
+                                // We can't suggest `as *mut/const () as *mut/const to_ref_ty`, because the former is a
+                                // thin pointer, whereas the latter is a wide pointer, due of its pointee, `to_ref_ty`,
+                                // being !Sized.
+                                //
+                                // The only remaining option is be to skip `*mut/const ()`, but that might not be safe
+                                // to do because of the erased regions in `from_ptr_ty`, so reduce the applicability.
+                                app = Applicability::MaybeIncorrect;
+                            }
+                            sugg::make_unop(deref, arg.as_ty(format!("{cast} {ty_snip}"))).to_string()
+                        } else if msrv.meets(cx, msrvs::POINTER_CAST) {
                             format!("{deref}{}.cast::<{ty_snip}>()", arg.maybe_paren())
                         } else if from_ptr_ty.has_erased_regions() {
                             sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {ty_snip}"))).to_string()
@@ -45,15 +56,22 @@ pub(super) fn check<'tcx>(
                             sugg::make_unop(deref, arg.as_ty(format!("{cast} {ty_snip}"))).to_string()
                         }
                     } else if *from_ptr_ty == *to_ref_ty {
-                        if from_ptr_ty.has_erased_regions() {
-                            if msrv.meets(cx, msrvs::POINTER_CAST) {
-                                format!("{deref}{}.cast::<{to_ref_ty}>()", arg.maybe_paren())
-                            } else {
-                                sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {to_ref_ty}")))
-                                    .to_string()
-                            }
-                        } else {
+                        if !from_ptr_ty.has_erased_regions() {
                             sugg::make_unop(deref, arg).to_string()
+                        } else if !to_ref_ty.is_sized(cx.tcx, cx.typing_env()) {
+                            // 1. We can't suggest `.cast()`, because that requires `to_ref_ty` to be Sized.
+                            // 2. We can't suggest `as *mut/const () as *mut/const to_ref_ty`, because the former is a
+                            //    thin pointer, whereas the latter is a wide pointer, due of its pointee, `to_ref_ty`,
+                            //    being !Sized.
+                            //
+                            // The only remaining option is be to skip `*mut/const ()`, but that might not be safe to do
+                            // because of the erased regions in `from_ptr_ty`, so reduce the applicability.
+                            app = Applicability::MaybeIncorrect;
+                            sugg::make_unop(deref, arg.as_ty(format!("{cast} {to_ref_ty}"))).to_string()
+                        } else if msrv.meets(cx, msrvs::POINTER_CAST) {
+                            format!("{deref}{}.cast::<{to_ref_ty}>()", arg.maybe_paren())
+                        } else {
+                            sugg::make_unop(deref, arg.as_ty(format!("{cast} () as {cast} {to_ref_ty}"))).to_string()
                         }
                     } else {
                         sugg::make_unop(deref, arg.as_ty(format!("{cast} {to_ref_ty}"))).to_string()
diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_ref_to_ref.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_ref_to_ref.rs
index 3842c4eb60e..70c2a73ce6e 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/transmute_ref_to_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_ref_to_ref.rs
@@ -45,7 +45,9 @@ pub(super) fn check<'tcx>(
                 Applicability::MaybeIncorrect,
             );
             triggered = true;
-        } else if (cx.tcx.erase_regions(from_ty) != cx.tcx.erase_regions(to_ty)) && !const_context {
+        } else if (cx.tcx.erase_and_anonymize_regions(from_ty) != cx.tcx.erase_and_anonymize_regions(to_ty))
+            && !const_context
+        {
             span_lint_and_then(
                 cx,
                 TRANSMUTE_PTR_TO_PTR,
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 26323af3122..3e6aae475ec 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
@@ -12,8 +12,8 @@ pub(super) fn check<'tcx>(
     from_ty_orig: Ty<'tcx>,
     to_ty_orig: Ty<'tcx>,
 ) -> bool {
-    let mut from_ty = cx.tcx.erase_regions(from_ty_orig);
-    let mut to_ty = cx.tcx.erase_regions(to_ty_orig);
+    let mut from_ty = cx.tcx.erase_and_anonymize_regions(from_ty_orig);
+    let mut to_ty = cx.tcx.erase_and_anonymize_regions(to_ty_orig);
 
     while from_ty != to_ty {
         let reduced_tys = reduce_refs(cx, from_ty, to_ty);
diff --git a/src/tools/clippy/clippy_lints/src/transmute/useless_transmute.rs b/src/tools/clippy/clippy_lints/src/transmute/useless_transmute.rs
index ec5fb2793f9..b898920baef 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/useless_transmute.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/useless_transmute.rs
@@ -49,17 +49,7 @@ pub(super) fn check<'tcx>(
             true
         },
         (ty::Int(_) | ty::Uint(_), ty::RawPtr(_, _)) => {
-            span_lint_and_then(
-                cx,
-                USELESS_TRANSMUTE,
-                e.span,
-                "transmute from an integer to a pointer",
-                |diag| {
-                    if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) {
-                        diag.span_suggestion(e.span, "try", arg.as_ty(to_ty.to_string()), Applicability::Unspecified);
-                    }
-                },
-            );
+            // Handled by the upstream rustc `integer_to_ptr_transmutes` lint
             true
         },
         _ => false,
diff --git a/src/tools/clippy/clippy_lints/src/types/rc_buffer.rs b/src/tools/clippy/clippy_lints/src/types/rc_buffer.rs
index d691f1878b1..c4fd0fbf87a 100644
--- a/src/tools/clippy/clippy_lints/src/types/rc_buffer.rs
+++ b/src/tools/clippy/clippy_lints/src/types/rc_buffer.rs
@@ -11,7 +11,8 @@ use super::RC_BUFFER;
 
 pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
     let app = Applicability::Unspecified;
-    if cx.tcx.is_diagnostic_item(sym::Rc, def_id) {
+    let name = cx.tcx.get_diagnostic_name(def_id);
+    if name == Some(sym::Rc) {
         if let Some(alternate) = match_buffer_type(cx, qpath) {
             #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
             span_lint_and_then(
@@ -56,7 +57,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_
             );
             return true;
         }
-    } else if cx.tcx.is_diagnostic_item(sym::Arc, def_id) {
+    } else if name == Some(sym::Arc) {
         if let Some(alternate) = match_buffer_type(cx, qpath) {
             #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
             span_lint_and_then(
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 de3456a8ba5..0ba51daf027 100644
--- a/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs
+++ b/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs
@@ -13,14 +13,11 @@ use super::{REDUNDANT_ALLOCATION, utils};
 
 pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'tcx>, qpath: &QPath<'tcx>, def_id: DefId) -> bool {
     let mut applicability = Applicability::MaybeIncorrect;
-    let outer_sym = if Some(def_id) == cx.tcx.lang_items().owned_box() {
-        "Box"
-    } else if cx.tcx.is_diagnostic_item(sym::Rc, def_id) {
-        "Rc"
-    } else if cx.tcx.is_diagnostic_item(sym::Arc, def_id) {
-        "Arc"
-    } else {
-        return false;
+    let outer_sym = match cx.tcx.get_diagnostic_name(def_id) {
+        _ if Some(def_id) == cx.tcx.lang_items().owned_box() => "Box",
+        Some(sym::Rc) => "Rc",
+        Some(sym::Arc) => "Arc",
+        _ => return false,
     };
 
     if let Some(span) = utils::match_borrows_parameter(cx, qpath) {
diff --git a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs
index dcddff557d1..e843e169113 100644
--- a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs
+++ b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs
@@ -137,9 +137,9 @@ fn get_impl_trait_def_id(cx: &LateContext<'_>, method_def_id: LocalDefId) -> Opt
         // We exclude `impl` blocks generated from rustc's proc macros.
         && !cx.tcx.is_automatically_derived(owner_id.to_def_id())
         // It is a implementation of a trait.
-        && let Some(trait_) = impl_.of_trait
+        && let Some(of_trait) = impl_.of_trait
     {
-        trait_.trait_def_id()
+        of_trait.trait_ref.trait_def_id()
     } else {
         None
     }
@@ -242,8 +242,8 @@ fn check_to_string(cx: &LateContext<'_>, method_span: Span, method_def_id: Local
         // We exclude `impl` blocks generated from rustc's proc macros.
         && !cx.tcx.is_automatically_derived(owner_id.to_def_id())
         // It is a implementation of a trait.
-        && let Some(trait_) = impl_.of_trait
-        && let Some(trait_def_id) = trait_.trait_def_id()
+        && let Some(of_trait) = impl_.of_trait
+        && let Some(trait_def_id) = of_trait.trait_ref.trait_def_id()
         // The trait is `ToString`.
         && cx.tcx.is_diagnostic_item(sym::ToString, trait_def_id)
     {
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 cf603c6190b..ba0d4de5f3b 100644
--- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
+++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
@@ -8,7 +8,7 @@ use clippy_utils::source::walk_span_to_context;
 use clippy_utils::visitors::{Descend, for_each_expr};
 use hir::HirId;
 use rustc_hir as hir;
-use rustc_hir::{Block, BlockCheckMode, ItemKind, Node, UnsafeSource};
+use rustc_hir::{Block, BlockCheckMode, Impl, ItemKind, Node, UnsafeSource};
 use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_session::impl_lint_pass;
@@ -202,79 +202,41 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks {
         };
 
         let item_has_safety_comment = item_has_safety_comment(cx, item);
-        match (&item.kind, item_has_safety_comment) {
-            // lint unsafe impl without safety comment
-            (ItemKind::Impl(impl_), HasSafetyComment::No) if impl_.safety.is_unsafe() => {
-                if !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, item.hir_id())
-                    && !is_unsafe_from_proc_macro(cx, item.span)
-                {
-                    let source_map = cx.tcx.sess.source_map();
-                    let span = if source_map.is_multiline(item.span) {
-                        source_map.span_until_char(item.span, '\n')
-                    } else {
-                        item.span
-                    };
-
-                    #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
-                    span_lint_and_then(
-                        cx,
-                        UNDOCUMENTED_UNSAFE_BLOCKS,
-                        span,
-                        "unsafe impl missing a safety comment",
-                        |diag| {
-                            diag.help("consider adding a safety comment on the preceding line");
-                        },
-                    );
-                }
-            },
-            // lint safe impl with unnecessary safety comment
-            (ItemKind::Impl(impl_), HasSafetyComment::Yes(pos)) if impl_.safety.is_safe() => {
-                if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, item.hir_id()) {
-                    let (span, help_span) = mk_spans(pos);
-
-                    span_lint_and_then(
-                        cx,
-                        UNNECESSARY_SAFETY_COMMENT,
-                        span,
-                        "impl has unnecessary safety comment",
-                        |diag| {
-                            diag.span_help(help_span, "consider removing the safety comment");
-                        },
-                    );
-                }
-            },
-            (ItemKind::Impl(_), _) => {},
-            // const and static items only need a safety comment if their body is an unsafe block, lint otherwise
-            (&ItemKind::Const(.., body) | &ItemKind::Static(.., body), HasSafetyComment::Yes(pos)) => {
-                if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, body.hir_id) {
-                    let body = cx.tcx.hir_body(body);
-                    if !matches!(
-                        body.value.kind, hir::ExprKind::Block(block, _)
-                        if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
-                    ) {
-                        let (span, help_span) = mk_spans(pos);
-
-                        span_lint_and_then(
-                            cx,
-                            UNNECESSARY_SAFETY_COMMENT,
-                            span,
-                            format!(
-                                "{} has unnecessary safety comment",
-                                cx.tcx.def_descr(item.owner_id.to_def_id()),
-                            ),
-                            |diag| {
-                                diag.span_help(help_span, "consider removing the safety comment");
-                            },
-                        );
-                    }
-                }
-            },
-            // Aside from unsafe impls and consts/statics with an unsafe block, items in general
-            // do not have safety invariants that need to be documented, so lint those.
-            (_, HasSafetyComment::Yes(pos)) => {
-                if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, item.hir_id()) {
-                    let (span, help_span) = mk_spans(pos);
+        match item_has_safety_comment {
+            HasSafetyComment::Yes(pos) => check_has_safety_comment(cx, item, mk_spans(pos)),
+            HasSafetyComment::No => check_has_no_safety_comment(cx, item),
+            HasSafetyComment::Maybe => {},
+        }
+    }
+}
 
+fn check_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>, (span, help_span): (Span, Span)) {
+    match &item.kind {
+        ItemKind::Impl(Impl {
+            of_trait: Some(of_trait),
+            ..
+        }) if of_trait.safety.is_safe() => {
+            if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, item.hir_id()) {
+                span_lint_and_then(
+                    cx,
+                    UNNECESSARY_SAFETY_COMMENT,
+                    span,
+                    "impl has unnecessary safety comment",
+                    |diag| {
+                        diag.span_help(help_span, "consider removing the safety comment");
+                    },
+                );
+            }
+        },
+        ItemKind::Impl(_) => {},
+        // const and static items only need a safety comment if their body is an unsafe block, lint otherwise
+        &ItemKind::Const(.., body) | &ItemKind::Static(.., body) => {
+            if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, body.hir_id) {
+                let body = cx.tcx.hir_body(body);
+                if !matches!(
+                    body.value.kind, hir::ExprKind::Block(block, _)
+                    if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
+                ) {
                     span_lint_and_then(
                         cx,
                         UNNECESSARY_SAFETY_COMMENT,
@@ -288,12 +250,56 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks {
                         },
                     );
                 }
-            },
-            _ => (),
-        }
+            }
+        },
+        // Aside from unsafe impls and consts/statics with an unsafe block, items in general
+        // do not have safety invariants that need to be documented, so lint those.
+        _ => {
+            if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, item.hir_id()) {
+                span_lint_and_then(
+                    cx,
+                    UNNECESSARY_SAFETY_COMMENT,
+                    span,
+                    format!(
+                        "{} has unnecessary safety comment",
+                        cx.tcx.def_descr(item.owner_id.to_def_id()),
+                    ),
+                    |diag| {
+                        diag.span_help(help_span, "consider removing the safety comment");
+                    },
+                );
+            }
+        },
     }
 }
+fn check_has_no_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) {
+    if let ItemKind::Impl(Impl {
+        of_trait: Some(of_trait),
+        ..
+    }) = item.kind
+        && of_trait.safety.is_unsafe()
+        && !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, item.hir_id())
+        && !is_unsafe_from_proc_macro(cx, item.span)
+    {
+        let source_map = cx.tcx.sess.source_map();
+        let span = if source_map.is_multiline(item.span) {
+            source_map.span_until_char(item.span, '\n')
+        } else {
+            item.span
+        };
 
+        #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+        span_lint_and_then(
+            cx,
+            UNDOCUMENTED_UNSAFE_BLOCKS,
+            span,
+            "unsafe impl missing a safety comment",
+            |diag| {
+                diag.help("consider adding a safety comment on the preceding line");
+            },
+        );
+    }
+}
 fn expr_has_unnecessary_safety_comment<'tcx>(
     cx: &LateContext<'tcx>,
     expr: &'tcx hir::Expr<'tcx>,
@@ -505,7 +511,8 @@ fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> HasSaf
         },
         Node::Stmt(stmt) => {
             if let Node::Block(block) = cx.tcx.parent_hir_node(stmt.hir_id) {
-                walk_span_to_context(block.span, SyntaxContext::root()).map(Span::lo)
+                walk_span_to_context(block.span, SyntaxContext::root())
+                    .map(|sp| CommentStartBeforeItem::Offset(sp.lo()))
             } else {
                 // Problem getting the parent node. Pretend a comment was found.
                 return HasSafetyComment::Maybe;
@@ -518,10 +525,12 @@ fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> HasSaf
     };
 
     let source_map = cx.sess().source_map();
+    // If the comment is in the first line of the file, there is no preceding line
     if let Some(comment_start) = comment_start
         && let Ok(unsafe_line) = source_map.lookup_line(item.span.lo())
-        && let Ok(comment_start_line) = source_map.lookup_line(comment_start)
-        && Arc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf)
+        && let Ok(comment_start_line) = source_map.lookup_line(comment_start.into())
+        && let include_first_line_of_file = matches!(comment_start, CommentStartBeforeItem::Start)
+        && (include_first_line_of_file || Arc::ptr_eq(&unsafe_line.sf, &comment_start_line.sf))
         && let Some(src) = unsafe_line.sf.src.as_deref()
     {
         return if comment_start_line.line >= unsafe_line.line {
@@ -529,7 +538,8 @@ fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> HasSaf
         } else {
             match text_has_safety_comment(
                 src,
-                &unsafe_line.sf.lines()[comment_start_line.line + 1..=unsafe_line.line],
+                &unsafe_line.sf.lines()
+                    [(comment_start_line.line + usize::from(!include_first_line_of_file))..=unsafe_line.line],
                 unsafe_line.sf.start_pos,
             ) {
                 Some(b) => HasSafetyComment::Yes(b),
@@ -592,12 +602,27 @@ fn stmt_has_safety_comment(
     HasSafetyComment::Maybe
 }
 
+#[derive(Clone, Copy, Debug)]
+enum CommentStartBeforeItem {
+    Offset(BytePos),
+    Start,
+}
+
+impl From<CommentStartBeforeItem> for BytePos {
+    fn from(value: CommentStartBeforeItem) -> Self {
+        match value {
+            CommentStartBeforeItem::Offset(loc) => loc,
+            CommentStartBeforeItem::Start => BytePos(0),
+        }
+    }
+}
+
 fn comment_start_before_item_in_mod(
     cx: &LateContext<'_>,
     parent_mod: &hir::Mod<'_>,
     parent_mod_span: Span,
     item: &hir::Item<'_>,
-) -> Option<BytePos> {
+) -> Option<CommentStartBeforeItem> {
     parent_mod.item_ids.iter().enumerate().find_map(|(idx, item_id)| {
         if *item_id == item.item_id() {
             if idx == 0 {
@@ -605,15 +630,18 @@ fn comment_start_before_item_in_mod(
                 // ^------------------------------------------^ returns the start of this span
                 // ^---------------------^ finally checks comments in this range
                 if let Some(sp) = walk_span_to_context(parent_mod_span, SyntaxContext::root()) {
-                    return Some(sp.lo());
+                    return Some(CommentStartBeforeItem::Offset(sp.lo()));
                 }
             } else {
                 // some_item /* comment */ unsafe impl T {}
                 // ^-------^ returns the end of this span
                 //         ^---------------^ finally checks comments in this range
                 let prev_item = cx.tcx.hir_item(parent_mod.item_ids[idx - 1]);
+                if prev_item.span.is_dummy() {
+                    return Some(CommentStartBeforeItem::Start);
+                }
                 if let Some(sp) = walk_span_to_context(prev_item.span, SyntaxContext::root()) {
-                    return Some(sp.hi());
+                    return Some(CommentStartBeforeItem::Offset(sp.hi()));
                 }
             }
         }
@@ -668,7 +696,7 @@ fn get_body_search_span(cx: &LateContext<'_>) -> Option<Span> {
             }) => {
                 return maybe_mod_item
                     .and_then(|item| comment_start_before_item_in_mod(cx, mod_, *span, &item))
-                    .map(|comment_start| mod_.spans.inner_span.with_lo(comment_start))
+                    .map(|comment_start| mod_.spans.inner_span.with_lo(comment_start.into()))
                     .or(Some(*span));
             },
             node if let Some((span, _)) = span_and_hid_of_item_alike_node(&node)
diff --git a/src/tools/clippy/clippy_lints/src/uninit_vec.rs b/src/tools/clippy/clippy_lints/src/uninit_vec.rs
index cee4a53f03c..51116b5eba9 100644
--- a/src/tools/clippy/clippy_lints/src/uninit_vec.rs
+++ b/src/tools/clippy/clippy_lints/src/uninit_vec.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
 use clippy_utils::higher::{VecInitKind, get_vec_init_kind};
 use clippy_utils::ty::{is_type_diagnostic_item, is_uninit_value_valid_for_ty};
 use clippy_utils::{SpanlessEq, is_integer_literal, is_lint_allowed, path_to_local_id, peel_hir_expr_while, sym};
@@ -95,16 +95,13 @@ fn handle_uninit_vec_pair<'tcx>(
 
             // Check T of Vec<T>
             if !is_uninit_value_valid_for_ty(cx, args.type_at(0)) {
-                // FIXME: #7698, false positive of the internal lints
-                #[expect(clippy::collapsible_span_lint_calls)]
-                span_lint_and_then(
+                span_lint_and_help(
                     cx,
                     UNINIT_VEC,
                     vec![call_span, maybe_init_or_reserve.span],
                     "calling `set_len()` immediately after reserving a buffer creates uninitialized values",
-                    |diag| {
-                        diag.help("initialize the buffer or wrap the content in `MaybeUninit`");
-                    },
+                    None,
+                    "initialize the buffer or wrap the content in `MaybeUninit`",
                 );
             }
         } else {
diff --git a/src/tools/clippy/clippy_lints/src/unit_types/unit_cmp.rs b/src/tools/clippy/clippy_lints/src/unit_types/unit_cmp.rs
index 48b532968cb..38716519e23 100644
--- a/src/tools/clippy/clippy_lints/src/unit_types/unit_cmp.rs
+++ b/src/tools/clippy/clippy_lints/src/unit_types/unit_cmp.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::macros::{find_assert_eq_args, root_macro_call_first_node};
-use clippy_utils::sym;
+use clippy_utils::{is_unit_expr, sym};
 use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::LateContext;
 
@@ -16,10 +16,13 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
                 sym::assert_ne_macro | sym::debug_assert_ne_macro => "fail",
                 _ => return,
             };
-            let Some((left, _, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else {
+            let Some((lhs, rhs, _)) = find_assert_eq_args(cx, expr, macro_call.expn) else {
                 return;
             };
-            if !cx.typeck_results().expr_ty(left).is_unit() {
+            if is_unit_expr(lhs) || is_unit_expr(rhs) {
+                return;
+            }
+            if !cx.typeck_results().expr_ty(lhs).is_unit() {
                 return;
             }
             span_lint(
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 2b7d3dc0c90..6e3e41f08ee 100644
--- a/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs
@@ -55,7 +55,7 @@ impl UnnecessaryBoxReturns {
         }
     }
 
-    fn check_fn_item(&mut self, cx: &LateContext<'_>, decl: &FnDecl<'_>, def_id: LocalDefId, name: Symbol) {
+    fn check_fn_item(&self, cx: &LateContext<'_>, decl: &FnDecl<'_>, def_id: LocalDefId, name: Symbol) {
         // we don't want to tell someone to break an exported function if they ask us not to
         if self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(def_id) {
             return;
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs b/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs
index 7d996775a58..28f4884fa31 100644
--- a/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_owned_empty_strings.rs
@@ -41,7 +41,8 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyStrings {
             && let ty::Ref(_, inner_str, _) = cx.typeck_results().expr_ty_adjusted(expr).kind()
             && inner_str.is_str()
         {
-            if cx.tcx.is_diagnostic_item(sym::string_new, fun_def_id) {
+            let fun_name = cx.tcx.get_diagnostic_name(fun_def_id);
+            if fun_name == Some(sym::string_new) {
                 span_lint_and_sugg(
                     cx,
                     UNNECESSARY_OWNED_EMPTY_STRINGS,
@@ -51,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryOwnedEmptyStrings {
                     "\"\"".to_owned(),
                     Applicability::MachineApplicable,
                 );
-            } else if cx.tcx.is_diagnostic_item(sym::from_fn, fun_def_id)
+            } else if fun_name == Some(sym::from_fn)
                 && let [arg] = args
                 && let ExprKind::Lit(spanned) = &arg.kind
                 && let LitKind::Str(symbol, _) = spanned.node
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_semicolon.rs b/src/tools/clippy/clippy_lints/src/unnecessary_semicolon.rs
index f1d1a76d0c2..76e24b6bf80 100644
--- a/src/tools/clippy/clippy_lints/src/unnecessary_semicolon.rs
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_semicolon.rs
@@ -86,7 +86,9 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessarySemicolon {
                 expr.kind,
                 ExprKind::If(..) | ExprKind::Match(_, _, MatchSource::Normal | MatchSource::Postfix)
             )
-            && cx.typeck_results().expr_ty(expr) == cx.tcx.types.unit
+            && cx.typeck_results().expr_ty(expr).is_unit()
+            // if a stmt has attrs, then turning it into an expr will break the code, since attrs aren't allowed on exprs
+            && cx.tcx.hir_attrs(stmt.hir_id).is_empty()
         {
             if let Some(block_is_unit) = self.is_last_in_block(stmt) {
                 if cx.tcx.sess.edition() <= Edition2021 && leaks_droppable_temporary_with_limited_lifetime(cx, expr) {
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs
index 54a7efc090a..849c0b438a5 100644
--- a/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_wraps.rs
@@ -107,12 +107,10 @@ impl<'tcx> LateLintPass<'tcx> for UnnecessaryWraps {
         // Get the wrapper and inner types, if can't, abort.
         let (return_type_label, lang_item, inner_type) =
             if let ty::Adt(adt_def, subst) = return_ty(cx, hir_id.expect_owner()).kind() {
-                if cx.tcx.is_diagnostic_item(sym::Option, adt_def.did()) {
-                    ("Option", OptionSome, subst.type_at(0))
-                } else if cx.tcx.is_diagnostic_item(sym::Result, adt_def.did()) {
-                    ("Result", ResultOk, subst.type_at(0))
-                } else {
-                    return;
+                match cx.tcx.get_diagnostic_name(adt_def.did()) {
+                    Some(sym::Option) => ("Option", OptionSome, subst.type_at(0)),
+                    Some(sym::Result) => ("Result", ResultOk, subst.type_at(0)),
+                    _ => return,
                 }
             } else {
                 return;
diff --git a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
index e9ad578da2f..f3410c98973 100644
--- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
+++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
@@ -284,14 +284,14 @@ fn transform_with_focus_on_idx(alternatives: &mut ThinVec<Box<Pat>>, focus_idx:
             |k, ps1, idx| matches!(
                 k,
                 TupleStruct(qself2, path2, ps2)
-                    if eq_maybe_qself(qself1.as_ref(), qself2.as_ref())
+                    if eq_maybe_qself(qself1.as_deref(), qself2.as_deref())
                        && eq_path(path1, path2) && eq_pre_post(ps1, ps2, idx)
             ),
             |k| always_pat!(k, TupleStruct(_, _, ps) => ps),
         ),
         // Transform a record pattern `S { fp_0, ..., fp_n }`.
         Struct(qself1, path1, fps1, rest1) => {
-            extend_with_struct_pat(qself1.as_ref(), path1, fps1, *rest1, start, alternatives)
+            extend_with_struct_pat(qself1.as_deref(), path1, fps1, *rest1, start, alternatives)
         },
     };
 
@@ -304,7 +304,7 @@ fn transform_with_focus_on_idx(alternatives: &mut ThinVec<Box<Pat>>, focus_idx:
 /// So when we fixate on some `ident_k: pat_k`, we try to find `ident_k` in the other pattern
 /// and check that all `fp_i` where `i ∈ ((0...n) \ k)` between two patterns are equal.
 fn extend_with_struct_pat(
-    qself1: Option<&Box<ast::QSelf>>,
+    qself1: Option<&ast::QSelf>,
     path1: &ast::Path,
     fps1: &mut [ast::PatField],
     rest1: ast::PatFieldsRest,
@@ -319,14 +319,18 @@ fn extend_with_struct_pat(
             |k| {
                 matches!(k, Struct(qself2, path2, fps2, rest2)
                 if rest1 == *rest2 // If one struct pattern has `..` so must the other.
-                && eq_maybe_qself(qself1, qself2.as_ref())
+                && eq_maybe_qself(qself1, qself2.as_deref())
                 && eq_path(path1, path2)
                 && fps1.len() == fps2.len()
                 && fps1.iter().enumerate().all(|(idx_1, fp1)| {
                     if idx_1 == idx {
                         // In the case of `k`, we merely require identical field names
                         // so that we will transform into `ident_k: p1_k | p2_k`.
-                        let pos = fps2.iter().position(|fp2| eq_id(fp1.ident, fp2.ident));
+                        let pos = fps2.iter().position(|fp2| {
+                            // Avoid `Foo { bar } | Foo { bar }` => `Foo { bar | bar }`
+                            !(fp1.is_shorthand && fp2.is_shorthand)
+                                && eq_id(fp1.ident, fp2.ident)
+                        });
                         pos_in_2.set(pos);
                         pos.is_some()
                     } else {
diff --git a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs
index f3cd3f1bb28..af3ad4566c4 100644
--- a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs
@@ -89,7 +89,9 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount {
         {
             // 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) {
+            if let Some(trait_name) = cx.tcx.get_diagnostic_name(trait_id)
+                && matches!(trait_name, sym::IoRead | sym::IoWrite)
+            {
                 return;
             }
 
diff --git a/src/tools/clippy/clippy_lints/src/unused_peekable.rs b/src/tools/clippy/clippy_lints/src/unused_peekable.rs
index 1f5351e32aa..5224b62e9fc 100644
--- a/src/tools/clippy/clippy_lints/src/unused_peekable.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_peekable.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_hir_and_then;
-use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable};
+use clippy_utils::ty::{is_type_diagnostic_item, peel_and_count_ty_refs};
 use clippy_utils::{fn_def_id, is_trait_method, path_to_local_id, peel_ref_operators, sym};
 use rustc_ast::Mutability;
 use rustc_hir::intravisit::{Visitor, walk_expr};
@@ -61,7 +61,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedPeekable {
                 && let Some(init) = local.init
                 && !init.span.from_expansion()
                 && let Some(ty) = cx.typeck_results().expr_ty_opt(init)
-                && let (ty, _, Mutability::Mut) = peel_mid_ty_refs_is_mutable(ty)
+                && let (ty, _, None | Some(Mutability::Mut)) = peel_and_count_ty_refs(ty)
                 && is_type_diagnostic_item(cx, ty, sym::IterPeekable)
             {
                 let mut vis = PeekableVisitor::new(cx, binding);
@@ -211,7 +211,7 @@ impl<'tcx> Visitor<'tcx> for PeekableVisitor<'_, 'tcx> {
 
 fn arg_is_mut_peekable(cx: &LateContext<'_>, arg: &Expr<'_>) -> bool {
     if let Some(ty) = cx.typeck_results().expr_ty_opt(arg)
-        && let (ty, _, Mutability::Mut) = peel_mid_ty_refs_is_mutable(ty)
+        && let (ty, _, None | Some(Mutability::Mut)) = peel_and_count_ty_refs(ty)
         && is_type_diagnostic_item(cx, ty, sym::IterPeekable)
     {
         true
diff --git a/src/tools/clippy/clippy_lints/src/unused_unit.rs b/src/tools/clippy/clippy_lints/src/unused_unit.rs
index 3811f0fe6b5..d503bd3379b 100644
--- a/src/tools/clippy/clippy_lints/src/unused_unit.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_unit.rs
@@ -6,13 +6,13 @@ use rustc_errors::Applicability;
 use rustc_hir::def_id::LocalDefId;
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{
-    AssocItemConstraintKind, Body, Expr, ExprKind, FnDecl, FnRetTy, GenericArgsParentheses, Node, PolyTraitRef, Term,
-    Ty, TyKind,
+    AssocItemConstraintKind, Body, Expr, ExprKind, FnDecl, FnRetTy, GenericArgsParentheses, PolyTraitRef, Term, Ty,
+    TyKind,
 };
 use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
 use rustc_span::edition::Edition;
-use rustc_span::{BytePos, Span, sym};
+use rustc_span::{BytePos, Pos as _, Span, sym};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -49,19 +49,22 @@ impl<'tcx> LateLintPass<'tcx> for UnusedUnit {
         decl: &'tcx FnDecl<'tcx>,
         body: &'tcx Body<'tcx>,
         span: Span,
-        def_id: LocalDefId,
+        _def_id: LocalDefId,
     ) {
         if let FnRetTy::Return(hir_ty) = decl.output
             && is_unit_ty(hir_ty)
             && !hir_ty.span.from_expansion()
             && get_def(span) == get_def(hir_ty.span)
         {
-            // implicit types in closure signatures are forbidden when `for<...>` is present
-            if let FnKind::Closure = kind
-                && let Node::Expr(expr) = cx.tcx.hir_node_by_def_id(def_id)
-                && let ExprKind::Closure(closure) = expr.kind
-                && !closure.bound_generic_params.is_empty()
-            {
+            // The explicit `-> ()` in the closure signature might be necessary for multiple reasons:
+            // - Implicit types in closure signatures are forbidden when `for<...>` is present
+            // - If the closure body ends with a function call, and that function's return type is generic, the
+            //   `-> ()` could be required for it to be inferred
+            //
+            // There could be more reasons to have it, and, in general, we shouldn't discourage the users from
+            // writing more type annotations than strictly necessary, because it can help readability and
+            // maintainability
+            if let FnKind::Closure = kind {
                 return;
             }
 
@@ -97,16 +100,13 @@ impl<'tcx> LateLintPass<'tcx> for UnusedUnit {
     }
 
     fn check_poly_trait_ref(&mut self, cx: &LateContext<'tcx>, poly: &'tcx PolyTraitRef<'tcx>) {
-        let segments = &poly.trait_ref.path.segments;
-
-        if segments.len() == 1
-            && matches!(segments[0].ident.name, sym::Fn | sym::FnMut | sym::FnOnce)
-            && let Some(args) = segments[0].args
+        if let [segment] = &poly.trait_ref.path.segments
+            && matches!(segment.ident.name, sym::Fn | sym::FnMut | sym::FnOnce)
+            && let Some(args) = segment.args
             && args.parenthesized == GenericArgsParentheses::ParenSugar
-            && let constraints = &args.constraints
-            && constraints.len() == 1
-            && constraints[0].ident.name == sym::Output
-            && let AssocItemConstraintKind::Equality { term: Term::Ty(hir_ty) } = constraints[0].kind
+            && let [constraint] = &args.constraints
+            && constraint.ident.name == sym::Output
+            && let AssocItemConstraintKind::Equality { term: Term::Ty(hir_ty) } = constraint.kind
             && args.span_ext.hi() != poly.span.hi()
             && !hir_ty.span.from_expansion()
             && args.span_ext.hi() != hir_ty.span.hi()
@@ -160,17 +160,15 @@ fn get_def(span: Span) -> Option<Span> {
 
 fn lint_unneeded_unit_return(cx: &LateContext<'_>, ty_span: Span, span: Span) {
     let (ret_span, appl) =
-        span.with_hi(ty_span.hi())
-            .get_source_text(cx)
-            .map_or((ty_span, Applicability::MaybeIncorrect), |src| {
-                position_before_rarrow(&src).map_or((ty_span, Applicability::MaybeIncorrect), |rpos| {
-                    (
-                        #[expect(clippy::cast_possible_truncation)]
-                        ty_span.with_lo(BytePos(span.lo().0 + rpos as u32)),
-                        Applicability::MachineApplicable,
-                    )
-                })
-            });
+        if let Some(Some(rpos)) = span.with_hi(ty_span.hi()).with_source_text(cx, position_before_rarrow) {
+            (
+                ty_span.with_lo(span.lo() + BytePos::from_usize(rpos)),
+                Applicability::MachineApplicable,
+            )
+        } else {
+            (ty_span, Applicability::MaybeIncorrect)
+        };
+
     span_lint_and_sugg(
         cx,
         UNUSED_UNIT,
diff --git a/src/tools/clippy/clippy_lints/src/unwrap.rs b/src/tools/clippy/clippy_lints/src/unwrap.rs
index c641d4e55b9..34dfe5b6546 100644
--- a/src/tools/clippy/clippy_lints/src/unwrap.rs
+++ b/src/tools/clippy/clippy_lints/src/unwrap.rs
@@ -141,43 +141,45 @@ fn collect_unwrap_info<'tcx>(
         is_type_diagnostic_item(cx, ty, sym::Result) && matches!(method_name, sym::is_err | sym::is_ok)
     }
 
-    if let ExprKind::Binary(op, left, right) = &expr.kind {
-        match (invert, op.node) {
-            (false, BinOpKind::And | BinOpKind::BitAnd) | (true, BinOpKind::Or | BinOpKind::BitOr) => {
-                let mut unwrap_info = collect_unwrap_info(cx, if_expr, left, branch, invert, false);
-                unwrap_info.append(&mut collect_unwrap_info(cx, if_expr, right, branch, invert, false));
-                return unwrap_info;
-            },
-            _ => (),
-        }
-    } else if let ExprKind::Unary(UnOp::Not, expr) = &expr.kind {
-        return collect_unwrap_info(cx, if_expr, expr, branch, !invert, false);
-    } else if let ExprKind::MethodCall(method_name, receiver, [], _) = &expr.kind
-        && let Some(local_id) = path_to_local(receiver)
-        && let ty = cx.typeck_results().expr_ty(receiver)
-        && let name = method_name.ident.name
-        && (is_relevant_option_call(cx, ty, name) || is_relevant_result_call(cx, ty, name))
-    {
-        let unwrappable = matches!(name, sym::is_some | sym::is_ok);
-        let safe_to_unwrap = unwrappable != invert;
-        let kind = if is_type_diagnostic_item(cx, ty, sym::Option) {
-            UnwrappableKind::Option
-        } else {
-            UnwrappableKind::Result
-        };
+    match expr.kind {
+        ExprKind::Binary(op, left, right)
+            if matches!(
+                (invert, op.node),
+                (false, BinOpKind::And | BinOpKind::BitAnd) | (true, BinOpKind::Or | BinOpKind::BitOr)
+            ) =>
+        {
+            let mut unwrap_info = collect_unwrap_info(cx, if_expr, left, branch, invert, false);
+            unwrap_info.extend(collect_unwrap_info(cx, if_expr, right, branch, invert, false));
+            unwrap_info
+        },
+        ExprKind::Unary(UnOp::Not, expr) => collect_unwrap_info(cx, if_expr, expr, branch, !invert, false),
+        ExprKind::MethodCall(method_name, receiver, [], _)
+            if let Some(local_id) = path_to_local(receiver)
+                && let ty = cx.typeck_results().expr_ty(receiver)
+                && let name = method_name.ident.name
+                && (is_relevant_option_call(cx, ty, name) || is_relevant_result_call(cx, ty, name)) =>
+        {
+            let unwrappable = matches!(name, sym::is_some | sym::is_ok);
+            let safe_to_unwrap = unwrappable != invert;
+            let kind = if is_type_diagnostic_item(cx, ty, sym::Option) {
+                UnwrappableKind::Option
+            } else {
+                UnwrappableKind::Result
+            };
 
-        return vec![UnwrapInfo {
-            local_id,
-            if_expr,
-            check: expr,
-            check_name: name,
-            branch,
-            safe_to_unwrap,
-            kind,
-            is_entire_condition,
-        }];
+            vec![UnwrapInfo {
+                local_id,
+                if_expr,
+                check: expr,
+                check_name: name,
+                branch,
+                safe_to_unwrap,
+                kind,
+                is_entire_condition,
+            }]
+        },
+        _ => vec![],
     }
-    Vec::new()
 }
 
 /// A HIR visitor delegate that checks if a local variable of type `Option` or `Result` is mutated,
@@ -290,6 +292,7 @@ impl<'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'_, 'tcx> {
     fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
         // Shouldn't lint when `expr` is in macro.
         if expr.span.in_external_macro(self.cx.tcx.sess.source_map()) {
+            walk_expr(self, expr);
             return;
         }
         // Skip checking inside closures since they are visited through `Unwrap::check_fn()` already.
diff --git a/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs b/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs
index 7bec212a23c..f26647fa348 100644
--- a/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs
+++ b/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs
@@ -1,13 +1,12 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::visitors::for_each_expr;
-use clippy_utils::{method_chain_args, return_ty};
-use core::ops::ControlFlow;
-use rustc_hir as hir;
-use rustc_hir::ImplItemKind;
+use clippy_utils::{return_ty, sym};
+use rustc_hir::{
+    Body, BodyOwnerKind, Expr, ExprKind, FnSig, ImplItem, ImplItemKind, Item, ItemKind, OwnerId, PathSegment, QPath,
+};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::declare_lint_pass;
-use rustc_span::{Span, sym};
+use rustc_middle::ty::Ty;
+use rustc_session::impl_lint_pass;
+use rustc_span::{Ident, Span, Symbol};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -57,62 +56,165 @@ declare_clippy_lint! {
     "functions of type `Result<..>` or `Option`<...> that contain `expect()` or `unwrap()`"
 }
 
-declare_lint_pass!(UnwrapInResult=> [UNWRAP_IN_RESULT]);
+impl_lint_pass!(UnwrapInResult=> [UNWRAP_IN_RESULT]);
 
-impl<'tcx> LateLintPass<'tcx> for UnwrapInResult {
-    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
-        if let ImplItemKind::Fn(ref _signature, _) = impl_item.kind
-            // first check if it's a method or function
-            // checking if its return type is `result` or `option`
-            && (is_type_diagnostic_item(cx, return_ty(cx, impl_item.owner_id), sym::Result)
-                || is_type_diagnostic_item(cx, return_ty(cx, impl_item.owner_id), sym::Option))
-        {
-            lint_impl_body(cx, impl_item.span, impl_item);
+#[derive(Clone, Copy, Eq, PartialEq)]
+enum OptionOrResult {
+    Option,
+    Result,
+}
+
+impl OptionOrResult {
+    fn with_article(self) -> &'static str {
+        match self {
+            Self::Option => "an `Option`",
+            Self::Result => "a `Result`",
         }
     }
 }
 
-fn lint_impl_body<'tcx>(cx: &LateContext<'tcx>, impl_span: Span, impl_item: &'tcx hir::ImplItem<'_>) {
-    if let ImplItemKind::Fn(_, body_id) = impl_item.kind {
-        let body = cx.tcx.hir_body(body_id);
-        let typeck = cx.tcx.typeck(impl_item.owner_id.def_id);
-        let mut result = Vec::new();
-        let _: Option<!> = for_each_expr(cx, body.value, |e| {
-            // check for `expect`
-            if let Some(arglists) = method_chain_args(e, &[sym::expect]) {
-                let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs();
-                if is_type_diagnostic_item(cx, receiver_ty, sym::Option)
-                    || is_type_diagnostic_item(cx, receiver_ty, sym::Result)
-                {
-                    result.push(e.span);
-                }
-            }
-
-            // check for `unwrap`
-            if let Some(arglists) = method_chain_args(e, &[sym::unwrap]) {
-                let receiver_ty = typeck.expr_ty(arglists[0].0).peel_refs();
-                if is_type_diagnostic_item(cx, receiver_ty, sym::Option)
-                    || is_type_diagnostic_item(cx, receiver_ty, sym::Result)
-                {
-                    result.push(e.span);
-                }
-            }
-
-            ControlFlow::Continue(())
+struct OptionOrResultFn {
+    kind: OptionOrResult,
+    return_ty_span: Option<Span>,
+}
+
+#[derive(Default)]
+pub struct UnwrapInResult {
+    fn_stack: Vec<Option<OptionOrResultFn>>,
+    current_fn: Option<OptionOrResultFn>,
+}
+
+impl UnwrapInResult {
+    fn enter_item(&mut self, cx: &LateContext<'_>, fn_def_id: OwnerId, sig: &FnSig<'_>) {
+        self.fn_stack.push(self.current_fn.take());
+        self.current_fn = is_option_or_result(cx, return_ty(cx, fn_def_id)).map(|kind| OptionOrResultFn {
+            kind,
+            return_ty_span: Some(sig.decl.output.span()),
         });
+    }
 
-        // if we've found one, lint
-        if !result.is_empty() {
+    fn leave_item(&mut self) {
+        self.current_fn = self.fn_stack.pop().unwrap();
+    }
+}
+
+impl<'tcx> LateLintPass<'tcx> for UnwrapInResult {
+    fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) {
+        if let ImplItemKind::Fn(sig, _) = &impl_item.kind {
+            self.enter_item(cx, impl_item.owner_id, sig);
+        }
+    }
+
+    fn check_impl_item_post(&mut self, _: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'tcx>) {
+        if let ImplItemKind::Fn(..) = impl_item.kind {
+            self.leave_item();
+        }
+    }
+
+    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
+        if let ItemKind::Fn {
+            has_body: true, sig, ..
+        } = &item.kind
+        {
+            self.enter_item(cx, item.owner_id, sig);
+        }
+    }
+
+    fn check_item_post(&mut self, _: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
+        if let ItemKind::Fn { has_body: true, .. } = item.kind {
+            self.leave_item();
+        }
+    }
+
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
+        if expr.span.from_expansion() {
+            return;
+        }
+
+        if let Some(OptionOrResultFn {
+            kind,
+            ref mut return_ty_span,
+        }) = self.current_fn
+            && let Some((oor, fn_name)) = is_unwrap_or_expect_call(cx, expr)
+            && oor == kind
+        {
             span_lint_and_then(
                 cx,
                 UNWRAP_IN_RESULT,
-                impl_span,
-                "used unwrap or expect in a function that returns result or option",
-                move |diag| {
-                    diag.help("unwrap and expect should not be used in a function that returns result or option");
-                    diag.span_note(result, "potential non-recoverable error(s)");
+                expr.span,
+                format!("`{fn_name}` used in a function that returns {}", kind.with_article()),
+                |diag| {
+                    // Issue the note and help only once per function
+                    if let Some(span) = return_ty_span.take() {
+                        diag.span_note(span, "in this function signature");
+                        let complement = if kind == OptionOrResult::Result {
+                            " or calling the `.map_err()` method"
+                        } else {
+                            ""
+                        };
+                        diag.help(format!("consider using the `?` operator{complement}"));
+                    }
                 },
             );
         }
     }
+
+    fn check_body(&mut self, cx: &LateContext<'tcx>, body: &Body<'tcx>) {
+        let body_def_id = cx.tcx.hir_body_owner_def_id(body.id());
+        if !matches!(cx.tcx.hir_body_owner_kind(body_def_id), BodyOwnerKind::Fn) {
+            // When entering a body which is not a function, mask the potential surrounding
+            // function to not apply the lint.
+            self.fn_stack.push(self.current_fn.take());
+        }
+    }
+
+    fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &Body<'tcx>) {
+        let body_def_id = cx.tcx.hir_body_owner_def_id(body.id());
+        if !matches!(cx.tcx.hir_body_owner_kind(body_def_id), BodyOwnerKind::Fn) {
+            // Unmask the potential surrounding function.
+            self.current_fn = self.fn_stack.pop().unwrap();
+        }
+    }
+}
+
+fn is_option_or_result(cx: &LateContext<'_>, ty: Ty<'_>) -> Option<OptionOrResult> {
+    match ty.ty_adt_def().and_then(|def| cx.tcx.get_diagnostic_name(def.did())) {
+        Some(sym::Option) => Some(OptionOrResult::Option),
+        Some(sym::Result) => Some(OptionOrResult::Result),
+        _ => None,
+    }
+}
+
+fn is_unwrap_or_expect_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<(OptionOrResult, Symbol)> {
+    if let ExprKind::Call(func, _) = expr.kind
+        && let ExprKind::Path(QPath::TypeRelative(
+            hir_ty,
+            PathSegment {
+                ident:
+                    Ident {
+                        name: name @ (sym::unwrap | sym::expect),
+                        ..
+                    },
+                ..
+            },
+        )) = func.kind
+    {
+        is_option_or_result(cx, cx.typeck_results().node_type(hir_ty.hir_id)).map(|oor| (oor, *name))
+    } else if let ExprKind::MethodCall(
+        PathSegment {
+            ident: Ident {
+                name: name @ (sym::unwrap | sym::expect),
+                ..
+            },
+            ..
+        },
+        recv,
+        _,
+        _,
+    ) = expr.kind
+    {
+        is_option_or_result(cx, cx.typeck_results().expr_ty_adjusted(recv)).map(|oor| (oor, *name))
+    } else {
+        None
+    }
 }
diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs
index aeda864b7eb..9d5be922f43 100644
--- a/src/tools/clippy/clippy_lints/src/use_self.rs
+++ b/src/tools/clippy/clippy_lints/src/use_self.rs
@@ -2,20 +2,21 @@ use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::is_from_proc_macro;
 use clippy_utils::msrvs::{self, Msrv};
-use clippy_utils::ty::{same_type_and_consts, ty_from_hir_ty};
+use clippy_utils::ty::{same_type_modulo_regions, ty_from_hir_ty};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
 use rustc_hir::def::{CtorOf, DefKind, Res};
 use rustc_hir::def_id::LocalDefId;
 use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_ty};
 use rustc_hir::{
-    self as hir, AmbigArg, Expr, ExprKind, FnRetTy, FnSig, GenericArgsParentheses, GenericParam, GenericParamKind,
-    HirId, Impl, ImplItemKind, Item, ItemKind, Pat, PatExpr, PatExprKind, PatKind, Path, QPath, Ty, TyKind,
+    self as hir, AmbigArg, Expr, ExprKind, FnRetTy, FnSig, GenericArgsParentheses, GenericParamKind, HirId, Impl,
+    ImplItemKind, Item, ItemKind, Pat, PatExpr, PatExprKind, PatKind, Path, QPath, Ty, TyKind,
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::Ty as MiddleTy;
 use rustc_session::impl_lint_pass;
 use rustc_span::Span;
+use std::iter;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -101,17 +102,11 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
             let types_to_skip = generics
                 .params
                 .iter()
-                .filter_map(|param| match param {
-                    GenericParam {
-                        kind:
-                            GenericParamKind::Const {
-                                ty: Ty { hir_id, .. }, ..
-                            },
-                        ..
-                    } => Some(*hir_id),
+                .filter_map(|param| match param.kind {
+                    GenericParamKind::Const { ty, .. } => Some(ty.hir_id),
                     _ => None,
                 })
-                .chain(std::iter::once(self_ty.hir_id))
+                .chain([self_ty.hir_id])
                 .collect();
             StackItem::Check {
                 impl_id: item.owner_id.def_id,
@@ -151,8 +146,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
             // trait, not in the impl of the trait.
             let trait_method = cx
                 .tcx
-                .associated_item(impl_item.owner_id)
-                .trait_item_def_id
+                .trait_item_of(impl_item.owner_id)
                 .expect("impl method matches a trait method");
             let trait_method_sig = cx.tcx.fn_sig(trait_method).instantiate_identity();
             let trait_method_sig = cx.tcx.instantiate_bound_regions_with_erased(trait_method_sig);
@@ -210,11 +204,11 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
             && !types_to_skip.contains(&hir_ty.hir_id)
             && let ty = ty_from_hir_ty(cx, hir_ty.as_unambig_ty())
             && let impl_ty = cx.tcx.type_of(impl_id).instantiate_identity()
-            && same_type_and_consts(ty, impl_ty)
+            && same_type_modulo_regions(ty, impl_ty)
             // Ensure the type we encounter and the one from the impl have the same lifetime parameters. It may be that
-            // the lifetime parameters of `ty` are elided (`impl<'a> Foo<'a> { fn new() -> Self { Foo{..} } }`, in
+            // the lifetime parameters of `ty` are elided (`impl<'a> Foo<'a> { fn new() -> Self { Foo{..} } }`), in
             // which case we must still trigger the lint.
-            && (has_no_lifetime(ty) || same_lifetimes(ty, impl_ty))
+            && same_lifetimes(ty, impl_ty)
             && self.msrv.meets(cx, msrvs::TYPE_ALIAS_ENUM_VARIANTS)
         {
             span_lint(cx, hir_ty.span);
@@ -227,18 +221,16 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
             && cx.typeck_results().expr_ty(expr) == cx.tcx.type_of(impl_id).instantiate_identity()
             && self.msrv.meets(cx, msrvs::TYPE_ALIAS_ENUM_VARIANTS)
         {
-        } else {
-            return;
-        }
-        match expr.kind {
-            ExprKind::Struct(QPath::Resolved(_, path), ..) => check_path(cx, path),
-            ExprKind::Call(fun, _) => {
-                if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind {
-                    check_path(cx, path);
-                }
-            },
-            ExprKind::Path(QPath::Resolved(_, path)) => check_path(cx, path),
-            _ => (),
+            match expr.kind {
+                ExprKind::Struct(QPath::Resolved(_, path), ..) => check_path(cx, path),
+                ExprKind::Call(fun, _) => {
+                    if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind {
+                        check_path(cx, path);
+                    }
+                },
+                ExprKind::Path(QPath::Resolved(_, path)) => check_path(cx, path),
+                _ => (),
+            }
         }
     }
 
@@ -308,36 +300,20 @@ fn lint_path_to_variant(cx: &LateContext<'_>, path: &Path<'_>) {
     }
 }
 
-/// Returns `true` if types `a` and `b` have the same lifetime parameters, otherwise returns
-/// `false`.
+/// Checks whether types `a` and `b` have the same lifetime parameters.
 ///
 /// This function does not check that types `a` and `b` are the same types.
 fn same_lifetimes<'tcx>(a: MiddleTy<'tcx>, b: MiddleTy<'tcx>) -> bool {
     use rustc_middle::ty::{Adt, GenericArgKind};
-    match (&a.kind(), &b.kind()) {
-        (&Adt(_, args_a), &Adt(_, args_b)) => {
-            args_a
-                .iter()
-                .zip(args_b.iter())
-                .all(|(arg_a, arg_b)| match (arg_a.kind(), arg_b.kind()) {
-                    // TODO: Handle inferred lifetimes
-                    (GenericArgKind::Lifetime(inner_a), GenericArgKind::Lifetime(inner_b)) => inner_a == inner_b,
-                    (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => same_lifetimes(type_a, type_b),
-                    _ => true,
-                })
+    match (a.kind(), b.kind()) {
+        (Adt(_, args_a), Adt(_, args_b)) => {
+            iter::zip(*args_a, *args_b).all(|(arg_a, arg_b)| match (arg_a.kind(), arg_b.kind()) {
+                // TODO: Handle inferred lifetimes
+                (GenericArgKind::Lifetime(inner_a), GenericArgKind::Lifetime(inner_b)) => inner_a == inner_b,
+                (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => same_lifetimes(type_a, type_b),
+                _ => true,
+            })
         },
         _ => a == b,
     }
 }
-
-/// Returns `true` if `ty` has no lifetime parameter, otherwise returns `false`.
-fn has_no_lifetime(ty: MiddleTy<'_>) -> bool {
-    use rustc_middle::ty::{Adt, GenericArgKind};
-    match ty.kind() {
-        &Adt(_, args) => !args
-            .iter()
-            // TODO: Handle inferred lifetimes
-            .any(|arg| matches!(arg.kind(), GenericArgKind::Lifetime(..))),
-        _ => true,
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/useless_conversion.rs b/src/tools/clippy/clippy_lints/src/useless_conversion.rs
index e5b20c0e0a1..1b137017ecb 100644
--- a/src/tools/clippy/clippy_lints/src/useless_conversion.rs
+++ b/src/tools/clippy/clippy_lints/src/useless_conversion.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::{snippet, snippet_with_context};
 use clippy_utils::sugg::{DiagExt as _, Sugg};
-use clippy_utils::ty::{get_type_diagnostic_name, is_copy, is_type_diagnostic_item, same_type_and_consts};
+use clippy_utils::ty::{get_type_diagnostic_name, is_copy, is_type_diagnostic_item, same_type_modulo_regions};
 use clippy_utils::{
     get_parent_expr, is_inherent_method_call, is_trait_item, is_trait_method, is_ty_alias, path_to_local, sym,
 };
@@ -98,7 +98,7 @@ fn into_iter_bound<'tcx>(
             if tr.def_id() == into_iter_did {
                 into_iter_span = Some(*span);
             } else {
-                let tr = cx.tcx.erase_regions(tr);
+                let tr = cx.tcx.erase_and_anonymize_regions(tr);
                 if tr.has_escaping_bound_vars() {
                     return None;
                 }
@@ -184,7 +184,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
                     && (is_trait_item(cx, arg, sym::Into) || is_trait_item(cx, arg, sym::From))
                     && let ty::FnDef(_, args) = cx.typeck_results().expr_ty(arg).kind()
                     && let &[from_ty, to_ty] = args.into_type_list(cx.tcx).as_slice()
-                    && same_type_and_consts(from_ty, to_ty)
+                    && same_type_modulo_regions(from_ty, to_ty)
                 {
                     span_lint_and_then(
                         cx,
@@ -207,7 +207,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
                 if is_trait_method(cx, e, sym::Into) && name.ident.name == sym::into {
                     let a = cx.typeck_results().expr_ty(e);
                     let b = cx.typeck_results().expr_ty(recv);
-                    if same_type_and_consts(a, b) {
+                    if same_type_modulo_regions(a, b) {
                         let mut app = Applicability::MachineApplicable;
                         let sugg = snippet_with_context(cx, recv.span, e.span.ctxt(), "<expr>", &mut app).0;
                         span_lint_and_sugg(
@@ -324,7 +324,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
                     // If the types are identical then .into_iter() can be removed, unless the type
                     // implements Copy, in which case .into_iter() returns a copy of the receiver and
                     // cannot be safely omitted.
-                    if same_type_and_consts(a, b) && !is_copy(cx, b) {
+                    if same_type_modulo_regions(a, b) && !is_copy(cx, b) {
                         // Below we check if the parent method call meets the following conditions:
                         // 1. First parameter is `&mut self` (requires mutable reference)
                         // 2. Second parameter implements the `FnMut` trait (e.g., Iterator::any)
@@ -371,7 +371,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
                     && is_type_diagnostic_item(cx, a, sym::Result)
                     && let ty::Adt(_, args) = a.kind()
                     && let Some(a_type) = args.types().next()
-                    && same_type_and_consts(a_type, b)
+                    && same_type_modulo_regions(a_type, b)
                 {
                     span_lint_and_help(
                         cx,
@@ -386,16 +386,17 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
 
             ExprKind::Call(path, [arg]) => {
                 if let ExprKind::Path(ref qpath) = path.kind
-                    && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
                     && !is_ty_alias(qpath)
+                    && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
+                    && let Some(name) = cx.tcx.get_diagnostic_name(def_id)
                 {
                     let a = cx.typeck_results().expr_ty(e);
                     let b = cx.typeck_results().expr_ty(arg);
-                    if cx.tcx.is_diagnostic_item(sym::try_from_fn, def_id)
+                    if name == sym::try_from_fn
                         && is_type_diagnostic_item(cx, a, sym::Result)
                         && let ty::Adt(_, args) = a.kind()
                         && let Some(a_type) = args.types().next()
-                        && same_type_and_consts(a_type, b)
+                        && same_type_modulo_regions(a_type, b)
                     {
                         let hint = format!("consider removing `{}()`", snippet(cx, path.span, "TryFrom::try_from"));
                         span_lint_and_help(
@@ -406,9 +407,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessConversion {
                             None,
                             hint,
                         );
-                    }
-
-                    if cx.tcx.is_diagnostic_item(sym::from_fn, def_id) && same_type_and_consts(a, b) {
+                    } else if name == sym::from_fn && same_type_modulo_regions(a, b) {
                         let mut app = Applicability::MachineApplicable;
                         let sugg = Sugg::hir_with_context(cx, arg, e.span.ctxt(), "<expr>", &mut app).maybe_paren();
                         let sugg_msg = format!("consider removing `{}()`", snippet(cx, path.span, "From::from"));
diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs
index 2113cb92137..ece29362a39 100644
--- a/src/tools/clippy/clippy_lints/src/utils/author.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/author.rs
@@ -754,7 +754,8 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
                 self.ident(name);
                 sub.if_some(|p| self.pat(p));
             },
-            PatKind::Struct(ref qpath, fields, ignore) => {
+            PatKind::Struct(ref qpath, fields, etc) => {
+                let ignore = etc.is_some();
                 bind!(self, qpath, fields);
                 kind!("Struct(ref {qpath}, {fields}, {ignore})");
                 self.qpath(qpath, pat);
diff --git a/src/tools/clippy/clippy_lints/src/vec.rs b/src/tools/clippy/clippy_lints/src/vec.rs
index 7b6a25123e8..52b30ddce12 100644
--- a/src/tools/clippy/clippy_lints/src/vec.rs
+++ b/src/tools/clippy/clippy_lints/src/vec.rs
@@ -1,4 +1,6 @@
 use std::collections::BTreeMap;
+use std::collections::btree_map::Entry;
+use std::mem;
 use std::ops::ControlFlow;
 
 use clippy_config::Conf;
@@ -20,15 +22,36 @@ use rustc_span::{DesugaringKind, Span};
 pub struct UselessVec {
     too_large_for_stack: u64,
     msrv: Msrv,
-    span_to_lint_map: BTreeMap<Span, Option<(HirId, SuggestedType, String, Applicability)>>,
+    /// Maps from a `vec![]` source callsite invocation span to the "state" (i.e., whether we can
+    /// emit a warning there or not).
+    ///
+    /// The purpose of this is to buffer lints up until `check_crate_post` so that we can cancel a
+    /// lint while visiting, because a `vec![]` invocation span can appear multiple times when
+    /// it is passed as a macro argument, once in a context that doesn't require a `Vec<_>` and
+    /// another time that does. Consider:
+    /// ```
+    /// macro_rules! m {
+    ///     ($v:expr) => {
+    ///         let a = $v;
+    ///         $v.push(3);
+    ///     }
+    /// }
+    /// m!(vec![1, 2]);
+    /// ```
+    /// The macro invocation expands to two `vec![1, 2]` invocations. If we eagerly suggest changing
+    /// the first `vec![1, 2]` (which is shared with the other expn) to an array which indeed would
+    /// work, we get a false positive warning on the `$v.push(3)` which really requires `$v` to
+    /// be a vector.
+    span_to_state: BTreeMap<Span, VecState>,
     allow_in_test: bool,
 }
+
 impl UselessVec {
     pub fn new(conf: &'static Conf) -> Self {
         Self {
             too_large_for_stack: conf.too_large_for_stack,
             msrv: conf.msrv,
-            span_to_lint_map: BTreeMap::new(),
+            span_to_state: BTreeMap::new(),
             allow_in_test: conf.allow_useless_vec_in_tests,
         }
     }
@@ -62,17 +85,28 @@ declare_clippy_lint! {
 
 impl_lint_pass!(UselessVec => [USELESS_VEC]);
 
-impl<'tcx> LateLintPass<'tcx> for UselessVec {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        let Some(vec_args) = higher::VecArgs::hir(cx, expr.peel_borrows()) else {
-            return;
-        };
-        if self.allow_in_test && is_in_test(cx.tcx, expr.hir_id) {
-            return;
-        }
-        // the parent callsite of this `vec!` expression, or span to the borrowed one such as `&vec!`
-        let callsite = expr.span.parent_callsite().unwrap_or(expr.span);
+/// The "state" of a `vec![]` invocation, indicating whether it can or cannot be changed.
+enum VecState {
+    Change {
+        suggest_ty: SuggestedType,
+        vec_snippet: String,
+        expr_hir_id: HirId,
+    },
+    NoChange,
+}
+
+enum VecToArray {
+    /// Expression does not need to be a `Vec<_>` and its type can be changed to an array (or
+    /// slice).
+    Possible,
+    /// Expression must be a `Vec<_>`. Type cannot change.
+    Impossible,
+}
 
+impl UselessVec {
+    /// Checks if the surrounding environment requires this expression to actually be of type
+    /// `Vec<_>`, or if it can be changed to `&[]`/`[]` without causing type errors.
+    fn expr_usage_requires_vec(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) -> VecToArray {
         match cx.tcx.parent_hir_node(expr.hir_id) {
             // search for `let foo = vec![_]` expressions where all uses of `foo`
             // adjust to slices or call a method that exist on slices (e.g. len)
@@ -100,110 +134,126 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec {
                 .is_continue();
 
                 if only_slice_uses {
-                    self.check_vec_macro(cx, &vec_args, callsite, expr.hir_id, SuggestedType::Array);
+                    VecToArray::Possible
                 } else {
-                    self.span_to_lint_map.insert(callsite, None);
+                    VecToArray::Impossible
                 }
             },
             // if the local pattern has a specified type, do not lint.
             Node::LetStmt(LetStmt { ty: Some(_), .. }) if higher::VecArgs::hir(cx, expr).is_some() => {
-                self.span_to_lint_map.insert(callsite, None);
+                VecToArray::Impossible
             },
             // search for `for _ in vec![...]`
             Node::Expr(Expr { span, .. })
                 if span.is_desugaring(DesugaringKind::ForLoop) && self.msrv.meets(cx, msrvs::ARRAY_INTO_ITERATOR) =>
             {
-                let suggest_slice = suggest_type(expr);
-                self.check_vec_macro(cx, &vec_args, callsite, expr.hir_id, suggest_slice);
+                VecToArray::Possible
             },
             // search for `&vec![_]` or `vec![_]` expressions where the adjusted type is `&[_]`
             _ => {
-                let suggest_slice = suggest_type(expr);
-
                 if adjusts_to_slice(cx, expr) {
-                    self.check_vec_macro(cx, &vec_args, callsite, expr.hir_id, suggest_slice);
+                    VecToArray::Possible
                 } else {
-                    self.span_to_lint_map.insert(callsite, None);
+                    VecToArray::Impossible
                 }
             },
         }
     }
+}
+
+impl<'tcx> LateLintPass<'tcx> for UselessVec {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        if let Some(vec_args) = higher::VecArgs::hir(cx, expr.peel_borrows())
+            // The `vec![]` or `&vec![]` invocation span.
+            && let vec_span = expr.span.parent_callsite().unwrap_or(expr.span)
+            && !vec_span.from_expansion()
+        {
+            if self.allow_in_test && is_in_test(cx.tcx, expr.hir_id) {
+                return;
+            }
+
+            match self.expr_usage_requires_vec(cx, expr) {
+                VecToArray::Possible => {
+                    let suggest_ty = suggest_type(expr);
+
+                    // Size and `Copy` checks don't depend on the enclosing usage of the expression
+                    // and don't need to be inserted into the state map.
+                    let vec_snippet = match vec_args {
+                        higher::VecArgs::Repeat(expr, len) => {
+                            if is_copy(cx, cx.typeck_results().expr_ty(expr))
+                                && let Some(Constant::Int(length)) = ConstEvalCtxt::new(cx).eval(len)
+                                && let Ok(length) = u64::try_from(length)
+                                && size_of(cx, expr)
+                                    .checked_mul(length)
+                                    .is_some_and(|size| size <= self.too_large_for_stack)
+                            {
+                                suggest_ty.snippet(
+                                    cx,
+                                    Some(expr.span.source_callsite()),
+                                    Some(len.span.source_callsite()),
+                                )
+                            } else {
+                                return;
+                            }
+                        },
+                        higher::VecArgs::Vec(args) => {
+                            if let Ok(length) = u64::try_from(args.len())
+                                && size_of(cx, expr)
+                                    .checked_mul(length)
+                                    .is_some_and(|size| size <= self.too_large_for_stack)
+                            {
+                                suggest_ty.snippet(
+                                    cx,
+                                    args.first().zip(args.last()).map(|(first, last)| {
+                                        first.span.source_callsite().to(last.span.source_callsite())
+                                    }),
+                                    None,
+                                )
+                            } else {
+                                return;
+                            }
+                        },
+                    };
+
+                    if let Entry::Vacant(entry) = self.span_to_state.entry(vec_span) {
+                        entry.insert(VecState::Change {
+                            suggest_ty,
+                            vec_snippet,
+                            expr_hir_id: expr.hir_id,
+                        });
+                    }
+                },
+                VecToArray::Impossible => {
+                    self.span_to_state.insert(vec_span, VecState::NoChange);
+                },
+            }
+        }
+    }
 
     fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
-        for (span, lint_opt) in &self.span_to_lint_map {
-            if let Some((hir_id, suggest_slice, snippet, applicability)) = lint_opt {
-                let help_msg = format!("you can use {} directly", suggest_slice.desc());
-                span_lint_hir_and_then(cx, USELESS_VEC, *hir_id, *span, "useless use of `vec!`", |diag| {
-                    // If the `vec!` macro contains comment, better not make the suggestion machine
-                    // applicable as it would remove them.
-                    let applicability = if *applicability != Applicability::Unspecified
-                        && let source_map = cx.tcx.sess.source_map()
-                        && span_contains_comment(source_map, *span)
-                    {
+        for (span, state) in mem::take(&mut self.span_to_state) {
+            if let VecState::Change {
+                suggest_ty,
+                vec_snippet,
+                expr_hir_id,
+            } = state
+            {
+                span_lint_hir_and_then(cx, USELESS_VEC, expr_hir_id, span, "useless use of `vec!`", |diag| {
+                    let help_msg = format!("you can use {} directly", suggest_ty.desc());
+                    // If the `vec!` macro contains comment, better not make the suggestion machine applicable as it
+                    // would remove them.
+                    let applicability = if span_contains_comment(cx.tcx.sess.source_map(), span) {
                         Applicability::Unspecified
                     } else {
-                        *applicability
+                        Applicability::MachineApplicable
                     };
-                    diag.span_suggestion(*span, help_msg, snippet, applicability);
+                    diag.span_suggestion(span, help_msg, vec_snippet, applicability);
                 });
             }
         }
     }
 }
 
-impl UselessVec {
-    fn check_vec_macro<'tcx>(
-        &mut self,
-        cx: &LateContext<'tcx>,
-        vec_args: &higher::VecArgs<'tcx>,
-        span: Span,
-        hir_id: HirId,
-        suggest_slice: SuggestedType,
-    ) {
-        if span.from_expansion() {
-            return;
-        }
-
-        let snippet = match *vec_args {
-            higher::VecArgs::Repeat(elem, len) => {
-                if let Some(Constant::Int(len_constant)) = ConstEvalCtxt::new(cx).eval(len) {
-                    // vec![ty; N] works when ty is Clone, [ty; N] requires it to be Copy also
-                    if !is_copy(cx, cx.typeck_results().expr_ty(elem)) {
-                        return;
-                    }
-
-                    #[expect(clippy::cast_possible_truncation)]
-                    if len_constant as u64 * size_of(cx, elem) > self.too_large_for_stack {
-                        return;
-                    }
-
-                    suggest_slice.snippet(cx, Some(elem.span), Some(len.span))
-                } else {
-                    return;
-                }
-            },
-            higher::VecArgs::Vec(args) => {
-                let args_span = if let Some(last) = args.iter().last() {
-                    if args.len() as u64 * size_of(cx, last) > self.too_large_for_stack {
-                        return;
-                    }
-                    Some(args[0].span.source_callsite().to(last.span.source_callsite()))
-                } else {
-                    None
-                };
-                suggest_slice.snippet(cx, args_span, None)
-            },
-        };
-
-        self.span_to_lint_map.entry(span).or_insert(Some((
-            hir_id,
-            suggest_slice,
-            snippet,
-            Applicability::MachineApplicable,
-        )));
-    }
-}
-
 #[derive(Copy, Clone)]
 pub(crate) enum SuggestedType {
     /// Suggest using a slice `&[..]` / `&mut [..]`
@@ -221,11 +271,17 @@ impl SuggestedType {
     }
 
     fn snippet(self, cx: &LateContext<'_>, args_span: Option<Span>, len_span: Option<Span>) -> String {
+        // Invariant of the lint as implemented: all spans are from the root context (and as a result,
+        // always trivially crate-local).
+        assert!(args_span.is_none_or(|s| !s.from_expansion()));
+        assert!(len_span.is_none_or(|s| !s.from_expansion()));
+
         let maybe_args = args_span
-            .and_then(|sp| sp.get_source_text(cx))
+            .map(|sp| sp.get_source_text(cx).expect("spans are always crate-local"))
             .map_or(String::new(), |x| x.to_owned());
         let maybe_len = len_span
-            .and_then(|sp| sp.get_source_text(cx).map(|s| format!("; {s}")))
+            .map(|sp| sp.get_source_text(cx).expect("spans are always crate-local"))
+            .map(|st| format!("; {st}"))
             .unwrap_or_default();
 
         match self {
diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs
index d9a007635ca..c55c5ec2f51 100644
--- a/src/tools/clippy/clippy_lints/src/write.rs
+++ b/src/tools/clippy/clippy_lints/src/write.rs
@@ -347,10 +347,10 @@ impl<'tcx> LateLintPass<'tcx> for Write {
 
 fn is_debug_impl(cx: &LateContext<'_>, item: &Item<'_>) -> bool {
     if let ItemKind::Impl(Impl {
-        of_trait: Some(trait_ref),
+        of_trait: Some(of_trait),
         ..
     }) = &item.kind
-        && let Some(trait_id) = trait_ref.trait_def_id()
+        && let Some(trait_id) = of_trait.trait_ref.trait_def_id()
     {
         cx.tcx.is_diagnostic_item(sym::Debug, trait_id)
     } else {
@@ -537,7 +537,7 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
 
             sug_span = Some(sug_span.unwrap_or(arg.expr.span).to(arg.expr.span));
 
-            if let Some((_, index)) = positional_arg_piece_span(piece) {
+            if let Some((_, index)) = format_arg_piece_span(piece) {
                 replaced_position.push(index);
             }
 
@@ -569,16 +569,11 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
     }
 }
 
-/// Extract Span and its index from the given `piece`, if it's positional argument.
-fn positional_arg_piece_span(piece: &FormatArgsPiece) -> Option<(Span, usize)> {
+/// Extract Span and its index from the given `piece`
+fn format_arg_piece_span(piece: &FormatArgsPiece) -> Option<(Span, usize)> {
     match piece {
         FormatArgsPiece::Placeholder(FormatPlaceholder {
-            argument:
-                FormatArgPosition {
-                    index: Ok(index),
-                    kind: FormatArgPositionKind::Number,
-                    ..
-                },
+            argument: FormatArgPosition { index: Ok(index), .. },
             span: Some(span),
             ..
         }) => Some((*span, *index)),
diff --git a/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs b/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs
index 1550872bca2..f1572fd65bb 100644
--- a/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs
+++ b/src/tools/clippy/clippy_lints/src/zero_sized_map_values.rs
@@ -56,6 +56,9 @@ impl LateLintPass<'_> for ZeroSizedMapValues {
             // cannot check if it is `Sized` or not, such as an incomplete associated type in a
             // type alias. See an example in `issue14822()` of `tests/ui/zero_sized_hashmap_values.rs`.
             && !ty.has_non_region_param()
+            // Ensure that no region escapes to avoid an assertion error when computing the layout.
+            // See an example in `issue15429()` of `tests/ui/zero_sized_hashmap_values.rs`.
+            && !ty.has_escaping_bound_vars()
             && let Ok(layout) = cx.layout_of(ty)
             && layout.is_zst()
         {
diff --git a/src/tools/clippy/clippy_lints/src/zombie_processes.rs b/src/tools/clippy/clippy_lints/src/zombie_processes.rs
index 6ab94a52210..a934d2094e0 100644
--- a/src/tools/clippy/clippy_lints/src/zombie_processes.rs
+++ b/src/tools/clippy/clippy_lints/src/zombie_processes.rs
@@ -1,5 +1,6 @@
 use ControlFlow::{Break, Continue};
 use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::{fn_def_id, get_enclosing_block, path_to_local_id};
 use rustc_ast::Mutability;
 use rustc_ast::visit::visit_opt;
@@ -58,8 +59,8 @@ declare_lint_pass!(ZombieProcesses => [ZOMBIE_PROCESSES]);
 impl<'tcx> LateLintPass<'tcx> for ZombieProcesses {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
         if let ExprKind::Call(..) | ExprKind::MethodCall(..) = expr.kind
-            && let Some(child_adt) = cx.typeck_results().expr_ty(expr).ty_adt_def()
-            && cx.tcx.is_diagnostic_item(sym::Child, child_adt.did())
+            && let child_ty = cx.typeck_results().expr_ty(expr)
+            && is_type_diagnostic_item(cx, child_ty, sym::Child)
         {
             match cx.tcx.parent_hir_node(expr.hir_id) {
                 Node::LetStmt(local)
@@ -177,8 +178,8 @@ impl<'tcx> Visitor<'tcx> for WaitFinder<'_, 'tcx> {
                 Node::Expr(expr) if let ExprKind::AddrOf(_, Mutability::Not, _) = expr.kind => {},
                 Node::Expr(expr)
                     if let Some(fn_did) = fn_def_id(self.cx, expr)
-                        && (self.cx.tcx.is_diagnostic_item(sym::child_id, fn_did)
-                            || self.cx.tcx.is_diagnostic_item(sym::child_kill, fn_did)) => {},
+                        && let Some(fn_name) = self.cx.tcx.get_diagnostic_name(fn_did)
+                        && matches!(fn_name, sym::child_id | sym::child_kill) => {},
 
                 // Conservatively assume that all other kinds of nodes call `.wait()` somehow.
                 _ => return Break(MaybeWait(ex.span)),
@@ -351,9 +352,14 @@ fn check<'tcx>(cx: &LateContext<'tcx>, spawn_expr: &'tcx Expr<'tcx>, cause: Caus
 
 /// Checks if the given expression exits the process.
 fn is_exit_expression(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    fn_def_id(cx, expr).is_some_and(|fn_did| {
-        cx.tcx.is_diagnostic_item(sym::process_exit, fn_did) || cx.tcx.is_diagnostic_item(sym::process_abort, fn_did)
-    })
+    if let Some(fn_did) = fn_def_id(cx, expr)
+        && let Some(fn_name) = cx.tcx.get_diagnostic_name(fn_did)
+        && matches!(fn_name, sym::process_exit | sym::process_abort)
+    {
+        true
+    } else {
+        false
+    }
 }
 
 #[derive(Debug)]
diff --git a/src/tools/clippy/clippy_lints_internal/src/derive_deserialize_allowing_unknown.rs b/src/tools/clippy/clippy_lints_internal/src/derive_deserialize_allowing_unknown.rs
index 5e6a40ac2eb..0fd1e11b033 100644
--- a/src/tools/clippy/clippy_lints_internal/src/derive_deserialize_allowing_unknown.rs
+++ b/src/tools/clippy/clippy_lints_internal/src/derive_deserialize_allowing_unknown.rs
@@ -6,7 +6,8 @@ use rustc_hir::attrs::AttributeKind;
 use rustc_hir::def::Res;
 use rustc_hir::def_id::LocalDefId;
 use rustc_hir::{
-    AttrArgs, AttrItem, AttrPath, Attribute, HirId, Impl, Item, ItemKind, Path, QPath, TraitRef, Ty, TyKind, find_attr,
+    AttrArgs, AttrItem, AttrPath, Attribute, HirId, Impl, Item, ItemKind, Path, QPath, TraitImplHeader, TraitRef, Ty,
+    TyKind, find_attr,
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_lint_defs::declare_tool_lint;
@@ -56,10 +57,14 @@ impl<'tcx> LateLintPass<'tcx> for DeriveDeserializeAllowingUnknown {
         // Is this an `impl` (of a certain form)?
         let ItemKind::Impl(Impl {
             of_trait:
-                Some(TraitRef {
-                    path:
-                        Path {
-                            res: Res::Def(_, trait_def_id),
+                Some(TraitImplHeader {
+                    trait_ref:
+                        TraitRef {
+                            path:
+                                Path {
+                                    res: Res::Def(_, trait_def_id),
+                                    ..
+                                },
                             ..
                         },
                     ..
diff --git a/src/tools/clippy/clippy_test_deps/Cargo.lock b/src/tools/clippy/clippy_test_deps/Cargo.lock
index 2f987c0137c..b22cf9d107d 100644
--- a/src/tools/clippy/clippy_test_deps/Cargo.lock
+++ b/src/tools/clippy/clippy_test_deps/Cargo.lock
@@ -377,9 +377,9 @@ dependencies = [
 
 [[package]]
 name = "slab"
-version = "0.4.10"
+version = "0.4.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d"
+checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
 
 [[package]]
 name = "smallvec"
diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml
index bdf7431f29f..d58b47bf6de 100644
--- a/src/tools/clippy/clippy_utils/Cargo.toml
+++ b/src/tools/clippy/clippy_utils/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "clippy_utils"
-version = "0.1.91"
+version = "0.1.92"
 edition = "2024"
 description = "Helpful tools for writing lints, provided as they are used in Clippy"
 repository = "https://github.com/rust-lang/rust-clippy"
diff --git a/src/tools/clippy/clippy_utils/README.md b/src/tools/clippy/clippy_utils/README.md
index 6d8dd92d55d..2c66fdc73f5 100644
--- a/src/tools/clippy/clippy_utils/README.md
+++ b/src/tools/clippy/clippy_utils/README.md
@@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain:
 
 <!-- begin autogenerated nightly -->
 ```
-nightly-2025-08-07
+nightly-2025-09-18
 ```
 <!-- end autogenerated nightly -->
 
diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs
index 0312bf56e59..ad69e6eb184 100644
--- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs
+++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs
@@ -21,7 +21,7 @@ pub fn is_useless_with_eq_exprs(kind: BinOpKind) -> bool {
 }
 
 /// Checks if each element in the first slice is contained within the latter as per `eq_fn`.
-pub fn unordered_over<X>(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
+pub fn unordered_over<X, Y>(left: &[X], right: &[Y], mut eq_fn: impl FnMut(&X, &Y) -> bool) -> bool {
     left.len() == right.len() && left.iter().all(|l| right.iter().any(|r| eq_fn(l, r)))
 }
 
@@ -41,21 +41,23 @@ pub fn eq_pat(l: &Pat, r: &Pat) -> bool {
             b1 == b2 && eq_id(*i1, *i2) && both(s1.as_deref(), s2.as_deref(), eq_pat)
         },
         (Range(lf, lt, le), Range(rf, rt, re)) => {
-            eq_expr_opt(lf.as_ref(), rf.as_ref())
-                && eq_expr_opt(lt.as_ref(), rt.as_ref())
+            eq_expr_opt(lf.as_deref(), rf.as_deref())
+                && eq_expr_opt(lt.as_deref(), rt.as_deref())
                 && eq_range_end(&le.node, &re.node)
         },
         (Box(l), Box(r))
         | (Ref(l, Mutability::Not), Ref(r, Mutability::Not))
         | (Ref(l, Mutability::Mut), Ref(r, Mutability::Mut)) => eq_pat(l, r),
         (Tuple(l), Tuple(r)) | (Slice(l), Slice(r)) => over(l, r, |l, r| eq_pat(l, r)),
-        (Path(lq, lp), Path(rq, rp)) => both(lq.as_ref(), rq.as_ref(), eq_qself) && eq_path(lp, rp),
+        (Path(lq, lp), Path(rq, rp)) => both(lq.as_deref(), rq.as_deref(), eq_qself) && eq_path(lp, rp),
         (TupleStruct(lqself, lp, lfs), TupleStruct(rqself, rp, rfs)) => {
-            eq_maybe_qself(lqself.as_ref(), rqself.as_ref()) && eq_path(lp, rp) && over(lfs, rfs, |l, r| eq_pat(l, r))
+            eq_maybe_qself(lqself.as_deref(), rqself.as_deref())
+                && eq_path(lp, rp)
+                && over(lfs, rfs, |l, r| eq_pat(l, r))
         },
         (Struct(lqself, lp, lfs, lr), Struct(rqself, rp, rfs, rr)) => {
             lr == rr
-                && eq_maybe_qself(lqself.as_ref(), rqself.as_ref())
+                && eq_maybe_qself(lqself.as_deref(), rqself.as_deref())
                 && eq_path(lp, rp)
                 && unordered_over(lfs, rfs, eq_field_pat)
         },
@@ -82,11 +84,11 @@ pub fn eq_field_pat(l: &PatField, r: &PatField) -> bool {
         && over(&l.attrs, &r.attrs, eq_attr)
 }
 
-pub fn eq_qself(l: &Box<QSelf>, r: &Box<QSelf>) -> bool {
+pub fn eq_qself(l: &QSelf, r: &QSelf) -> bool {
     l.position == r.position && eq_ty(&l.ty, &r.ty)
 }
 
-pub fn eq_maybe_qself(l: Option<&Box<QSelf>>, r: Option<&Box<QSelf>>) -> bool {
+pub fn eq_maybe_qself(l: Option<&QSelf>, r: Option<&QSelf>) -> bool {
     match (l, r) {
         (Some(l), Some(r)) => eq_qself(l, r),
         (None, None) => true,
@@ -129,8 +131,8 @@ pub fn eq_generic_arg(l: &GenericArg, r: &GenericArg) -> bool {
     }
 }
 
-pub fn eq_expr_opt(l: Option<&Box<Expr>>, r: Option<&Box<Expr>>) -> bool {
-    both(l, r, |l, r| eq_expr(l, r))
+pub fn eq_expr_opt(l: Option<&Expr>, r: Option<&Expr>) -> bool {
+    both(l, r, eq_expr)
 }
 
 pub fn eq_struct_rest(l: &StructRest, r: &StructRest) -> bool {
@@ -177,7 +179,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
         (Cast(l, lt), Cast(r, rt)) | (Type(l, lt), Type(r, rt)) => eq_expr(l, r) && eq_ty(lt, rt),
         (Let(lp, le, _, _), Let(rp, re, _, _)) => eq_pat(lp, rp) && eq_expr(le, re),
         (If(lc, lt, le), If(rc, rt, re)) => {
-            eq_expr(lc, rc) && eq_block(lt, rt) && eq_expr_opt(le.as_ref(), re.as_ref())
+            eq_expr(lc, rc) && eq_block(lt, rt) && eq_expr_opt(le.as_deref(), re.as_deref())
         },
         (While(lc, lt, ll), While(rc, rt, rl)) => {
             eq_label(ll.as_ref(), rl.as_ref()) && eq_expr(lc, rc) && eq_block(lt, rt)
@@ -201,9 +203,11 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
         (Loop(lt, ll, _), Loop(rt, rl, _)) => eq_label(ll.as_ref(), rl.as_ref()) && eq_block(lt, rt),
         (Block(lb, ll), Block(rb, rl)) => eq_label(ll.as_ref(), rl.as_ref()) && eq_block(lb, rb),
         (TryBlock(l), TryBlock(r)) => eq_block(l, r),
-        (Yield(l), Yield(r)) => eq_expr_opt(l.expr(), r.expr()) && l.same_kind(r),
-        (Ret(l), Ret(r)) => eq_expr_opt(l.as_ref(), r.as_ref()),
-        (Break(ll, le), Break(rl, re)) => eq_label(ll.as_ref(), rl.as_ref()) && eq_expr_opt(le.as_ref(), re.as_ref()),
+        (Yield(l), Yield(r)) => eq_expr_opt(l.expr().map(Box::as_ref), r.expr().map(Box::as_ref)) && l.same_kind(r),
+        (Ret(l), Ret(r)) => eq_expr_opt(l.as_deref(), r.as_deref()),
+        (Break(ll, le), Break(rl, re)) => {
+            eq_label(ll.as_ref(), rl.as_ref()) && eq_expr_opt(le.as_deref(), re.as_deref())
+        },
         (Continue(ll), Continue(rl)) => eq_label(ll.as_ref(), rl.as_ref()),
         (Assign(l1, l2, _), Assign(r1, r2, _)) | (Index(l1, l2, _), Index(r1, r2, _)) => {
             eq_expr(l1, r1) && eq_expr(l2, r2)
@@ -240,13 +244,13 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
         },
         (Gen(lc, lb, lk, _), Gen(rc, rb, rk, _)) => lc == rc && eq_block(lb, rb) && lk == rk,
         (Range(lf, lt, ll), Range(rf, rt, rl)) => {
-            ll == rl && eq_expr_opt(lf.as_ref(), rf.as_ref()) && eq_expr_opt(lt.as_ref(), rt.as_ref())
+            ll == rl && eq_expr_opt(lf.as_deref(), rf.as_deref()) && eq_expr_opt(lt.as_deref(), rt.as_deref())
         },
         (AddrOf(lbk, lm, le), AddrOf(rbk, rm, re)) => lbk == rbk && lm == rm && eq_expr(le, re),
-        (Path(lq, lp), Path(rq, rp)) => both(lq.as_ref(), rq.as_ref(), eq_qself) && eq_path(lp, rp),
+        (Path(lq, lp), Path(rq, rp)) => both(lq.as_deref(), rq.as_deref(), eq_qself) && eq_path(lp, rp),
         (MacCall(l), MacCall(r)) => eq_mac_call(l, r),
         (Struct(lse), Struct(rse)) => {
-            eq_maybe_qself(lse.qself.as_ref(), rse.qself.as_ref())
+            eq_maybe_qself(lse.qself.as_deref(), rse.qself.as_deref())
                 && eq_path(&lse.path, &rse.path)
                 && eq_struct_rest(&lse.rest, &rse.rest)
                 && unordered_over(&lse.fields, &rse.fields, eq_field)
@@ -278,8 +282,8 @@ pub fn eq_field(l: &ExprField, r: &ExprField) -> bool {
 pub fn eq_arm(l: &Arm, r: &Arm) -> bool {
     l.is_placeholder == r.is_placeholder
         && eq_pat(&l.pat, &r.pat)
-        && eq_expr_opt(l.body.as_ref(), r.body.as_ref())
-        && eq_expr_opt(l.guard.as_ref(), r.guard.as_ref())
+        && eq_expr_opt(l.body.as_deref(), r.body.as_deref())
+        && eq_expr_opt(l.guard.as_deref(), r.guard.as_deref())
         && over(&l.attrs, &r.attrs, eq_attr)
 }
 
@@ -324,7 +328,7 @@ pub fn eq_item<K>(l: &Item<K>, r: &Item<K>, mut eq_kind: impl FnMut(&K, &K) -> b
     over(&l.attrs, &r.attrs, eq_attr) && eq_vis(&l.vis, &r.vis) && eq_kind(&l.kind, &r.kind)
 }
 
-#[expect(clippy::similar_names, clippy::too_many_lines)] // Just a big match statement
+#[expect(clippy::too_many_lines)] // Just a big match statement
 pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
     use ItemKind::*;
     match (l, r) {
@@ -347,7 +351,7 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
                 safety: rs,
                 define_opaque: _,
             }),
-        ) => eq_id(*li, *ri) && lm == rm && ls == rs && eq_ty(lt, rt) && eq_expr_opt(le.as_ref(), re.as_ref()),
+        ) => eq_id(*li, *ri) && lm == rm && ls == rs && eq_ty(lt, rt) && eq_expr_opt(le.as_deref(), re.as_deref()),
         (
             Const(box ConstItem {
                 defaultness: ld,
@@ -370,7 +374,7 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
                 && eq_id(*li, *ri)
                 && eq_generics(lg, rg)
                 && eq_ty(lt, rt)
-                && eq_expr_opt(le.as_ref(), re.as_ref())
+                && eq_expr_opt(le.as_deref(), re.as_deref())
         },
         (
             Fn(box ast::Fn {
@@ -403,7 +407,7 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
             ls == rs
                 && eq_id(*li, *ri)
                 && match (lmk, rmk) {
-                    (ModKind::Loaded(litems, linline, _, _), ModKind::Loaded(ritems, rinline, _, _)) => {
+                    (ModKind::Loaded(litems, linline, _), ModKind::Loaded(ritems, rinline, _)) => {
                         linline == rinline && over(litems, ritems, |l, r| eq_item(l, r, eq_item_kind))
                     },
                     (ModKind::Unloaded, ModKind::Unloaded) => true,
@@ -473,33 +477,27 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
             eq_id(*li, *ri) && eq_generics(lg, rg) && over(lb, rb, eq_generic_bound)
         },
         (
-            Impl(box ast::Impl {
-                safety: lu,
-                polarity: lp,
-                defaultness: ld,
-                constness: lc,
+            Impl(ast::Impl {
                 generics: lg,
                 of_trait: lot,
                 self_ty: lst,
                 items: li,
             }),
-            Impl(box ast::Impl {
-                safety: ru,
-                polarity: rp,
-                defaultness: rd,
-                constness: rc,
+            Impl(ast::Impl {
                 generics: rg,
                 of_trait: rot,
                 self_ty: rst,
                 items: ri,
             }),
         ) => {
-            matches!(lu, Safety::Default) == matches!(ru, Safety::Default)
-                && matches!(lp, ImplPolarity::Positive) == matches!(rp, ImplPolarity::Positive)
-                && eq_defaultness(*ld, *rd)
-                && matches!(lc, ast::Const::No) == matches!(rc, ast::Const::No)
-                && eq_generics(lg, rg)
-                && both(lot.as_ref(), rot.as_ref(), |l, r| eq_path(&l.path, &r.path))
+            eq_generics(lg, rg)
+                && both(lot.as_deref(), rot.as_deref(), |l, r| {
+                    matches!(l.safety, Safety::Default) == matches!(r.safety, Safety::Default)
+                        && matches!(l.polarity, ImplPolarity::Positive) == matches!(r.polarity, ImplPolarity::Positive)
+                        && eq_defaultness(l.defaultness, r.defaultness)
+                        && matches!(l.constness, ast::Const::No) == matches!(r.constness, ast::Const::No)
+                        && eq_path(&l.trait_ref.path, &r.trait_ref.path)
+                })
                 && eq_ty(lst, rst)
                 && over(li, ri, |l, r| eq_item(l, r, eq_assoc_item_kind))
         },
@@ -531,7 +529,7 @@ pub fn eq_foreign_item_kind(l: &ForeignItemKind, r: &ForeignItemKind) -> bool {
                 safety: rs,
                 define_opaque: _,
             }),
-        ) => eq_id(*li, *ri) && eq_ty(lt, rt) && lm == rm && eq_expr_opt(le.as_ref(), re.as_ref()) && ls == rs,
+        ) => eq_id(*li, *ri) && eq_ty(lt, rt) && lm == rm && eq_expr_opt(le.as_deref(), re.as_deref()) && ls == rs,
         (
             Fn(box ast::Fn {
                 defaultness: ld,
@@ -613,7 +611,7 @@ pub fn eq_assoc_item_kind(l: &AssocItemKind, r: &AssocItemKind) -> bool {
                 && eq_id(*li, *ri)
                 && eq_generics(lg, rg)
                 && eq_ty(lt, rt)
-                && eq_expr_opt(le.as_ref(), re.as_ref())
+                && eq_expr_opt(le.as_deref(), re.as_deref())
         },
         (
             Fn(box ast::Fn {
@@ -729,7 +727,8 @@ pub fn eq_fn_header(l: &FnHeader, r: &FnHeader) -> bool {
 pub fn eq_opt_fn_contract(l: &Option<Box<FnContract>>, r: &Option<Box<FnContract>>) -> bool {
     match (l, r) {
         (Some(l), Some(r)) => {
-            eq_expr_opt(l.requires.as_ref(), r.requires.as_ref()) && eq_expr_opt(l.ensures.as_ref(), r.ensures.as_ref())
+            eq_expr_opt(l.requires.as_deref(), r.requires.as_deref())
+                && eq_expr_opt(l.ensures.as_deref(), r.ensures.as_deref())
         },
         (None, None) => true,
         (Some(_), None) | (None, Some(_)) => false,
@@ -847,7 +846,7 @@ pub fn eq_ty(l: &Ty, r: &Ty) -> bool {
                 && eq_fn_decl(&l.decl, &r.decl)
         },
         (Tup(l), Tup(r)) => over(l, r, |l, r| eq_ty(l, r)),
-        (Path(lq, lp), Path(rq, rp)) => both(lq.as_ref(), rq.as_ref(), eq_qself) && eq_path(lp, rp),
+        (Path(lq, lp), Path(rq, rp)) => both(lq.as_deref(), rq.as_deref(), eq_qself) && eq_path(lp, rp),
         (TraitObject(lg, ls), TraitObject(rg, rs)) => ls == rs && over(lg, rg, eq_generic_bound),
         (ImplTrait(_, lg), ImplTrait(_, rg)) => over(lg, rg, eq_generic_bound),
         (Typeof(l), Typeof(r)) => eq_expr(&l.value, &r.value),
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 dc31ed08fb7..948a7203402 100644
--- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs
+++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs
@@ -13,20 +13,24 @@
 //! if the span is not from a `macro_rules` based macro.
 
 use rustc_abi::ExternAbi;
+use rustc_ast as ast;
 use rustc_ast::AttrStyle;
-use rustc_ast::ast::{AttrKind, Attribute, IntTy, LitIntType, LitKind, StrStyle, TraitObjectSyntax, UintTy};
+use rustc_ast::ast::{
+    AttrKind, Attribute, GenericArgs, IntTy, LitIntType, LitKind, StrStyle, TraitObjectSyntax, UintTy,
+};
 use rustc_ast::token::CommentKind;
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{
     Block, BlockCheckMode, Body, Closure, Destination, Expr, ExprKind, FieldDef, FnHeader, FnRetTy, HirId, Impl,
-    ImplItem, ImplItemKind, IsAuto, Item, ItemKind, Lit, LoopSource, MatchSource, MutTy, Node, Path, QPath, Safety,
-    TraitItem, TraitItemKind, Ty, TyKind, UnOp, UnsafeSource, Variant, VariantData, YieldSource,
+    ImplItem, ImplItemImplKind, ImplItemKind, IsAuto, Item, ItemKind, Lit, LoopSource, MatchSource, MutTy, Node, Path,
+    QPath, Safety, TraitImplHeader, TraitItem, TraitItemKind, Ty, TyKind, UnOp, UnsafeSource, Variant, VariantData,
+    YieldSource,
 };
 use rustc_lint::{EarlyContext, LateContext, LintContext};
 use rustc_middle::ty::TyCtxt;
 use rustc_session::Session;
 use rustc_span::symbol::{Ident, kw};
-use rustc_span::{Span, Symbol};
+use rustc_span::{Span, Symbol, sym};
 
 /// The search pattern to look for. Used by `span_matches_pat`
 #[derive(Clone)]
@@ -254,7 +258,10 @@ fn item_search_pat(item: &Item<'_>) -> (Pat, Pat) {
         ItemKind::Union(..) => (Pat::Str("union"), Pat::Str("}")),
         ItemKind::Trait(_, _, Safety::Unsafe, ..)
         | ItemKind::Impl(Impl {
-            safety: Safety::Unsafe, ..
+            of_trait: Some(TraitImplHeader {
+                safety: Safety::Unsafe, ..
+            }),
+            ..
         }) => (Pat::Str("unsafe"), Pat::Str("}")),
         ItemKind::Trait(_, IsAuto::Yes, ..) => (Pat::Str("auto"), Pat::Str("}")),
         ItemKind::Trait(..) => (Pat::Str("trait"), Pat::Str("}")),
@@ -277,16 +284,17 @@ fn trait_item_search_pat(item: &TraitItem<'_>) -> (Pat, Pat) {
 }
 
 fn impl_item_search_pat(item: &ImplItem<'_>) -> (Pat, Pat) {
-    let (start_pat, end_pat) = match &item.kind {
+    let (mut start_pat, end_pat) = match &item.kind {
         ImplItemKind::Const(..) => (Pat::Str("const"), Pat::Str(";")),
         ImplItemKind::Type(..) => (Pat::Str("type"), Pat::Str(";")),
         ImplItemKind::Fn(sig, ..) => (fn_header_search_pat(sig.header), Pat::Str("")),
     };
-    if item.vis_span.is_empty() {
-        (start_pat, end_pat)
-    } else {
-        (Pat::Str("pub"), end_pat)
+    if let ImplItemImplKind::Inherent { vis_span, .. } = item.impl_kind
+        && !vis_span.is_empty()
+    {
+        start_pat = Pat::Str("pub");
     }
+    (start_pat, end_pat)
 }
 
 fn field_def_search_pat(def: &FieldDef<'_>) -> (Pat, Pat) {
@@ -310,22 +318,24 @@ fn variant_search_pat(v: &Variant<'_>) -> (Pat, Pat) {
 }
 
 fn fn_kind_pat(tcx: TyCtxt<'_>, kind: &FnKind<'_>, body: &Body<'_>, hir_id: HirId) -> (Pat, Pat) {
-    let (start_pat, end_pat) = match kind {
+    let (mut start_pat, end_pat) = match kind {
         FnKind::ItemFn(.., header) => (fn_header_search_pat(*header), Pat::Str("")),
         FnKind::Method(.., sig) => (fn_header_search_pat(sig.header), Pat::Str("")),
         FnKind::Closure => return (Pat::Str(""), expr_search_pat(tcx, body.value).1),
     };
-    let start_pat = match tcx.hir_node(hir_id) {
-        Node::Item(Item { vis_span, .. }) | Node::ImplItem(ImplItem { vis_span, .. }) => {
-            if vis_span.is_empty() {
-                start_pat
-            } else {
-                Pat::Str("pub")
+    match tcx.hir_node(hir_id) {
+        Node::Item(Item { vis_span, .. })
+        | Node::ImplItem(ImplItem {
+            impl_kind: ImplItemImplKind::Inherent { vis_span, .. },
+            ..
+        }) => {
+            if !vis_span.is_empty() {
+                start_pat = Pat::Str("pub");
             }
         },
-        Node::TraitItem(_) => start_pat,
-        _ => Pat::Str(""),
-    };
+        Node::ImplItem(_) | Node::TraitItem(_) => {},
+        _ => start_pat = Pat::Str(""),
+    }
     (start_pat, end_pat)
 }
 
@@ -400,6 +410,7 @@ fn ty_search_pat(ty: &Ty<'_>) -> (Pat, Pat) {
         TyKind::OpaqueDef(..) => (Pat::Str("impl"), Pat::Str("")),
         TyKind::Path(qpath) => qpath_search_pat(&qpath),
         TyKind::Infer(()) => (Pat::Str("_"), Pat::Str("_")),
+        TyKind::UnsafeBinder(binder_ty) => (Pat::Str("unsafe"), ty_search_pat(binder_ty.inner_ty).1),
         TyKind::TraitObject(_, tagged_ptr) if let TraitObjectSyntax::Dyn = tagged_ptr.tag() => {
             (Pat::Str("dyn"), Pat::Str(""))
         },
@@ -408,6 +419,127 @@ fn ty_search_pat(ty: &Ty<'_>) -> (Pat, Pat) {
     }
 }
 
+fn ast_ty_search_pat(ty: &ast::Ty) -> (Pat, Pat) {
+    use ast::{Extern, FnRetTy, MutTy, Safety, TraitObjectSyntax, TyKind};
+
+    match &ty.kind {
+        TyKind::Slice(..) | TyKind::Array(..) => (Pat::Str("["), Pat::Str("]")),
+        TyKind::Ptr(MutTy { ty, .. }) => (Pat::Str("*"), ast_ty_search_pat(ty).1),
+        TyKind::Ref(_, MutTy { ty, .. }) | TyKind::PinnedRef(_, MutTy { ty, .. }) => {
+            (Pat::Str("&"), ast_ty_search_pat(ty).1)
+        },
+        TyKind::FnPtr(fn_ptr) => (
+            if let Safety::Unsafe(_) = fn_ptr.safety {
+                Pat::Str("unsafe")
+            } else if let Extern::Explicit(strlit, _) = fn_ptr.ext
+                && strlit.symbol == sym::rust
+            {
+                Pat::MultiStr(&["fn", "extern"])
+            } else {
+                Pat::Str("extern")
+            },
+            match &fn_ptr.decl.output {
+                FnRetTy::Default(_) => {
+                    if let [.., param] = &*fn_ptr.decl.inputs {
+                        ast_ty_search_pat(&param.ty).1
+                    } else {
+                        Pat::Str("(")
+                    }
+                },
+                FnRetTy::Ty(ty) => ast_ty_search_pat(ty).1,
+            },
+        ),
+        TyKind::Never => (Pat::Str("!"), Pat::Str("!")),
+        // Parenthesis are trimmed from the text before the search patterns are matched.
+        // See: `span_matches_pat`
+        TyKind::Tup(tup) => match &**tup {
+            [] => (Pat::Str(")"), Pat::Str("(")),
+            [ty] => ast_ty_search_pat(ty),
+            [head, .., tail] => (ast_ty_search_pat(head).0, ast_ty_search_pat(tail).1),
+        },
+        TyKind::ImplTrait(..) => (Pat::Str("impl"), Pat::Str("")),
+        TyKind::Path(qself_path, path) => {
+            let start = if qself_path.is_some() {
+                Pat::Str("<")
+            } else if let Some(first) = path.segments.first() {
+                ident_search_pat(first.ident).0
+            } else {
+                // this shouldn't be possible, but sure
+                Pat::Str("")
+            };
+            let end = if let Some(last) = path.segments.last() {
+                match last.args.as_deref() {
+                    // last `>` in `std::foo::Bar<T>`
+                    Some(GenericArgs::AngleBracketed(_)) => Pat::Str(">"),
+                    Some(GenericArgs::Parenthesized(par_args)) => match &par_args.output {
+                        FnRetTy::Default(_) => {
+                            if let Some(last) = par_args.inputs.last() {
+                                // `B` in `(A, B)` -- `)` gets stripped
+                                ast_ty_search_pat(last).1
+                            } else {
+                                // `(` in `()` -- `)` gets stripped
+                                Pat::Str("(")
+                            }
+                        },
+                        // `C` in `(A, B) -> C`
+                        FnRetTy::Ty(ty) => ast_ty_search_pat(ty).1,
+                    },
+                    // last `..` in `(..)` -- `)` gets stripped
+                    Some(GenericArgs::ParenthesizedElided(_)) => Pat::Str(".."),
+                    // `bar` in `std::foo::bar`
+                    None => ident_search_pat(last.ident).1,
+                }
+            } else {
+                // this shouldn't be possible, but sure
+                #[allow(
+                    clippy::collapsible_else_if,
+                    reason = "we want to keep these cases together, since they are both impossible"
+                )]
+                if qself_path.is_some() {
+                    // last `>` in `<Vec as IntoIterator>`
+                    Pat::Str(">")
+                } else {
+                    Pat::Str("")
+                }
+            };
+            (start, end)
+        },
+        TyKind::Infer => (Pat::Str("_"), Pat::Str("_")),
+        TyKind::Paren(ty) => ast_ty_search_pat(ty),
+        TyKind::UnsafeBinder(binder_ty) => (Pat::Str("unsafe"), ast_ty_search_pat(&binder_ty.inner_ty).1),
+        TyKind::TraitObject(_, trait_obj_syntax) => {
+            if let TraitObjectSyntax::Dyn = trait_obj_syntax {
+                (Pat::Str("dyn"), Pat::Str(""))
+            } else {
+                // NOTE: `TraitObject` is incomplete. It will always return true then.
+                (Pat::Str(""), Pat::Str(""))
+            }
+        },
+        TyKind::MacCall(mac_call) => {
+            let start = if let Some(first) = mac_call.path.segments.first() {
+                ident_search_pat(first.ident).0
+            } else {
+                Pat::Str("")
+            };
+            (start, Pat::Str(""))
+        },
+
+        // implicit, so has no contents to match against
+        TyKind::ImplicitSelf
+
+        // experimental
+        |TyKind::Pat(..)
+
+        // unused
+        | TyKind::CVarArgs
+        | TyKind::Typeof(_)
+
+        // placeholder
+        | TyKind::Dummy
+        | TyKind::Err(_) => (Pat::Str(""), Pat::Str("")),
+    }
+}
+
 fn ident_search_pat(ident: Ident) -> (Pat, Pat) {
     (Pat::Sym(ident.name), Pat::Sym(ident.name))
 }
@@ -442,6 +574,7 @@ impl_with_search_pat!((_cx: LateContext<'tcx>, self: Lit) => lit_search_pat(&sel
 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_with_search_pat!((_cx: EarlyContext<'tcx>, self: ast::Ty) => ast_ty_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/diagnostics.rs b/src/tools/clippy/clippy_utils/src/diagnostics.rs
index 625e1eead21..8a19039a7fe 100644
--- a/src/tools/clippy/clippy_utils/src/diagnostics.rs
+++ b/src/tools/clippy/clippy_utils/src/diagnostics.rs
@@ -22,13 +22,14 @@ fn docs_link(diag: &mut Diag<'_, ()>, lint: &'static Lint) {
     {
         diag.help(format!(
             "for further information visit https://rust-lang.github.io/rust-clippy/{}/index.html#{lint}",
-            &option_env!("RUST_RELEASE_NUM").map_or_else(
-                || "master".to_string(),
-                |n| {
-                    // extract just major + minor version and ignore patch versions
-                    format!("rust-{}", n.rsplit_once('.').unwrap().1)
-                }
-            )
+            match option_env!("CFG_RELEASE_CHANNEL") {
+                // Clippy version is 0.1.xx
+                //
+                // Always use .0 because we do not generate separate lint doc pages for rust patch releases
+                Some("stable") => concat!("rust-1.", env!("CARGO_PKG_VERSION_PATCH"), ".0"),
+                Some("beta") => "beta",
+                _ => "master",
+            }
         ));
     }
 }
diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs
index 4e0b00df950..bda28a663fb 100644
--- a/src/tools/clippy/clippy_utils/src/higher.rs
+++ b/src/tools/clippy/clippy_utils/src/higher.rs
@@ -287,23 +287,22 @@ impl<'a> VecArgs<'a> {
             && let ExprKind::Path(ref qpath) = fun.kind
             && is_expn_of(fun.span, sym::vec).is_some()
             && let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id()
+            && let Some(name) = cx.tcx.get_diagnostic_name(fun_def_id)
         {
-            return if cx.tcx.is_diagnostic_item(sym::vec_from_elem, fun_def_id) && args.len() == 2 {
-                // `vec![elem; size]` case
-                Some(VecArgs::Repeat(&args[0], &args[1]))
-            } else if cx.tcx.is_diagnostic_item(sym::slice_into_vec, fun_def_id) && args.len() == 1 {
-                // `vec![a, b, c]` case
-                if let ExprKind::Call(_, [arg]) = &args[0].kind
-                    && let ExprKind::Array(args) = arg.kind
+            return match (name, args) {
+                (sym::vec_from_elem, [elem, size]) => {
+                    // `vec![elem; size]` case
+                    Some(VecArgs::Repeat(elem, size))
+                },
+                (sym::slice_into_vec, [slice])
+                    if let ExprKind::Call(_, [arg]) = slice.kind
+                        && let ExprKind::Array(args) = arg.kind =>
                 {
+                    // `vec![a, b, c]` case
                     Some(VecArgs::Vec(args))
-                } else {
-                    None
-                }
-            } else if cx.tcx.is_diagnostic_item(sym::vec_new, fun_def_id) && args.is_empty() {
-                Some(VecArgs::Vec(&[]))
-            } else {
-                None
+                },
+                (sym::vec_new, []) => Some(VecArgs::Vec(&[])),
+                _ => None,
             };
         }
 
diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs
index f0d7fb89c44..b79e15cd717 100644
--- a/src/tools/clippy/clippy_utils/src/hir_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs
@@ -258,7 +258,7 @@ impl HirEqInterExpr<'_, '_, '_> {
         })
     }
 
-    fn should_ignore(&mut self, expr: &Expr<'_>) -> bool {
+    fn should_ignore(&self, expr: &Expr<'_>) -> bool {
         macro_backtrace(expr.span).last().is_some_and(|macro_call| {
             matches!(
                 self.inner.cx.tcx.get_diagnostic_name(macro_call.def_id),
@@ -757,7 +757,7 @@ pub fn both_some_and<X, Y>(l: Option<X>, r: Option<Y>, mut pred: impl FnMut(X, Y
 }
 
 /// Checks if two slices are equal as per `eq_fn`.
-pub fn over<X>(left: &[X], right: &[X], mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
+pub fn over<X, Y>(left: &[X], right: &[Y], mut eq_fn: impl FnMut(&X, &Y) -> bool) -> bool {
     left.len() == right.len() && left.iter().zip(right).all(|(x, y)| eq_fn(x, y))
 }
 
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index fc716d86fc6..feadc0ecf65 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -102,9 +102,9 @@ use rustc_hir::hir_id::{HirIdMap, HirIdSet};
 use rustc_hir::intravisit::{FnKind, Visitor, walk_expr};
 use rustc_hir::{
     self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, CoroutineDesugaring,
-    CoroutineKind, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs, HirId, Impl,
-    ImplItem, ImplItemKind, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode,
-    Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem,
+    CoroutineKind, CoroutineSource, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs,
+    HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId,
+    OwnerNode, Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem,
     TraitItemKind, TraitRef, TyKind, UnOp, def, find_attr,
 };
 use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize};
@@ -126,6 +126,7 @@ use rustc_span::{InnerSpan, Span};
 use source::{SpanRangeExt, walk_span_to_context};
 use visitors::{Visitable, for_each_unconsumed_temporary};
 
+use crate::ast_utils::unordered_over;
 use crate::consts::{ConstEvalCtxt, Constant, mir_to_const};
 use crate::higher::Range;
 use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type};
@@ -308,6 +309,7 @@ pub fn is_lang_item_or_ctor(cx: &LateContext<'_>, did: DefId, item: LangItem) ->
     cx.tcx.lang_items().get(item) == Some(did)
 }
 
+/// Checks if `expr` is an empty block or an empty tuple.
 pub fn is_unit_expr(expr: &Expr<'_>) -> bool {
     matches!(
         expr.kind,
@@ -327,13 +329,17 @@ pub fn is_wild(pat: &Pat<'_>) -> bool {
     matches!(pat.kind, PatKind::Wild)
 }
 
-// Checks if arm has the form `None => None`
-pub fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
-    matches!(
-        arm.pat.kind,
+/// Checks if the `pat` is `None`.
+pub fn is_none_pattern(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
+    matches!(pat.kind,
         PatKind::Expr(PatExpr { kind: PatExprKind::Path(qpath), .. })
-            if is_res_lang_ctor(cx, cx.qpath_res(qpath, arm.pat.hir_id), OptionNone)
-    )
+            if is_res_lang_ctor(cx, cx.qpath_res(qpath, pat.hir_id), OptionNone))
+}
+
+/// Checks if `arm` has the form `None => None`.
+pub fn is_none_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
+    is_none_pattern(cx, arm.pat)
+        && matches!(peel_blocks(arm.body).kind, ExprKind::Path(qpath) if is_res_lang_ctor(cx, cx.qpath_res(&qpath, arm.body.hir_id), OptionNone))
 }
 
 /// Checks if the given `QPath` belongs to a type alias.
@@ -460,6 +466,23 @@ pub fn path_to_local_id(expr: &Expr<'_>, id: HirId) -> bool {
     path_to_local(expr) == Some(id)
 }
 
+/// If the expression is a path to a local (with optional projections),
+/// returns the canonical `HirId` of the local.
+///
+/// For example, `x.field[0].field2` would return the `HirId` of `x`.
+pub fn path_to_local_with_projections(expr: &Expr<'_>) -> Option<HirId> {
+    match expr.kind {
+        ExprKind::Field(recv, _) | ExprKind::Index(recv, _, _) => path_to_local_with_projections(recv),
+        ExprKind::Path(QPath::Resolved(
+            _,
+            Path {
+                res: Res::Local(local), ..
+            },
+        )) => Some(*local),
+        _ => None,
+    }
+}
+
 pub trait MaybePath<'hir> {
     fn hir_id(&self) -> HirId;
     fn qpath_opt(&self) -> Option<&QPath<'hir>>;
@@ -528,8 +551,9 @@ pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>
 pub fn trait_ref_of_method<'tcx>(cx: &LateContext<'tcx>, owner: OwnerId) -> Option<&'tcx TraitRef<'tcx>> {
     if let Node::Item(item) = cx.tcx.hir_node(cx.tcx.hir_owner_parent(owner))
         && let ItemKind::Impl(impl_) = &item.kind
+        && let Some(of_trait) = impl_.of_trait
     {
-        return impl_.of_trait.as_ref();
+        return Some(&of_trait.trait_ref);
     }
     None
 }
@@ -622,9 +646,8 @@ fn is_default_equivalent_ctor(cx: &LateContext<'_>, def_id: DefId, path: &QPath<
         && let Some(impl_did) = cx.tcx.impl_of_assoc(def_id)
         && let Some(adt) = cx.tcx.type_of(impl_did).instantiate_identity().ty_adt_def()
     {
-        return std_types_symbols.iter().any(|&symbol| {
-            cx.tcx.is_diagnostic_item(symbol, adt.did()) || Some(adt.did()) == cx.tcx.lang_items().string()
-        });
+        return Some(adt.did()) == cx.tcx.lang_items().string()
+            || (cx.tcx.get_diagnostic_name(adt.did())).is_some_and(|adt_name| std_types_symbols.contains(&adt_name));
     }
     false
 }
@@ -1974,7 +1997,7 @@ pub fn is_expr_identity_of_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr<
         (PatKind::Tuple(pats, dotdot), ExprKind::Tup(tup))
             if dotdot.as_opt_usize().is_none() && pats.len() == tup.len() =>
         {
-            zip(pats, tup).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr, by_hir))
+            over(pats, tup, |pat, expr| is_expr_identity_of_pat(cx, pat, expr, by_hir))
         },
         (PatKind::Slice(before, None, after), ExprKind::Array(arr)) if before.len() + after.len() == arr.len() => {
             zip(before.iter().chain(after), arr).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr, by_hir))
@@ -1986,23 +2009,21 @@ pub fn is_expr_identity_of_pat(cx: &LateContext<'_>, pat: &Pat<'_>, expr: &Expr<
             if let ExprKind::Path(ident) = &ident.kind
                 && qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id)
                 // check fields
-                && zip(field_pats, fields).all(|(pat, expr)| is_expr_identity_of_pat(cx, pat, expr,by_hir))
+                && over(field_pats, fields, |pat, expr| is_expr_identity_of_pat(cx, pat, expr,by_hir))
             {
                 true
             } else {
                 false
             }
         },
-        (PatKind::Struct(pat_ident, field_pats, false), ExprKind::Struct(ident, fields, hir::StructTailExpr::None))
+        (PatKind::Struct(pat_ident, field_pats, None), ExprKind::Struct(ident, fields, hir::StructTailExpr::None))
             if field_pats.len() == fields.len() =>
         {
             // check ident
             qpath_res(&pat_ident, pat.hir_id) == qpath_res(ident, expr.hir_id)
                 // check fields
-                && field_pats.iter().all(|field_pat| {
-                    fields.iter().any(|field| {
-                        field_pat.ident == field.ident && is_expr_identity_of_pat(cx, field_pat.pat, field.expr, by_hir)
-                    })
+                && unordered_over(field_pats, fields, |field_pat, field| {
+                    field_pat.ident == field.ident && is_expr_identity_of_pat(cx, field_pat.pat, field.expr, by_hir)
                 })
         },
         _ => false,
@@ -2116,17 +2137,11 @@ pub fn std_or_core(cx: &LateContext<'_>) -> Option<&'static str> {
 }
 
 pub fn is_no_std_crate(cx: &LateContext<'_>) -> bool {
-    cx.tcx
-        .hir_attrs(hir::CRATE_HIR_ID)
-        .iter()
-        .any(|attr| attr.has_name(sym::no_std))
+    find_attr!(cx.tcx.hir_attrs(hir::CRATE_HIR_ID), AttributeKind::NoStd(..))
 }
 
 pub fn is_no_core_crate(cx: &LateContext<'_>) -> bool {
-    cx.tcx
-        .hir_attrs(hir::CRATE_HIR_ID)
-        .iter()
-        .any(|attr| attr.has_name(sym::no_core))
+    find_attr!(cx.tcx.hir_attrs(hir::CRATE_HIR_ID), AttributeKind::NoCore(..))
 }
 
 /// Check if parent of a hir node is a trait implementation block.
@@ -2363,15 +2378,12 @@ pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize)
     }
 }
 
-/// Peels off all references on the type. Returns the underlying type and the number of references
-/// removed.
-pub fn peel_middle_ty_refs(mut ty: Ty<'_>) -> (Ty<'_>, usize) {
-    let mut count = 0;
-    while let rustc_ty::Ref(_, dest_ty, _) = ty.kind() {
-        ty = *dest_ty;
-        count += 1;
+/// Returns the base type for HIR references and pointers.
+pub fn peel_hir_ty_refs_and_ptrs<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
+    match &ty.kind {
+        TyKind::Ptr(mut_ty) | TyKind::Ref(_, mut_ty) => peel_hir_ty_refs_and_ptrs(mut_ty.ty),
+        _ => ty,
     }
-    (ty, count)
 }
 
 /// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is
@@ -2387,6 +2399,24 @@ pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>
     expr
 }
 
+/// Returns a `Vec` of `Expr`s containing `AddrOf` operators (`&`) or deref operators (`*`) of a
+/// given expression.
+pub fn get_ref_operators<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>) -> Vec<&'hir Expr<'hir>> {
+    let mut operators = Vec::new();
+    peel_hir_expr_while(expr, |expr| match expr.kind {
+        ExprKind::AddrOf(_, _, e) => {
+            operators.push(expr);
+            Some(e)
+        },
+        ExprKind::Unary(UnOp::Deref, e) if cx.typeck_results().expr_ty(e).is_ref() => {
+            operators.push(expr);
+            Some(e)
+        },
+        _ => None,
+    });
+    operators
+}
+
 pub fn is_hir_ty_cfg_dependant(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
     if let TyKind::Path(QPath::Resolved(_, path)) = ty.kind
         && let Res::Def(_, def_id) = path.res
@@ -3215,8 +3245,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::AssocItemContainer::Trait => get_path_to_callee(tcx, from, def_id),
-        rustc_ty::AssocItemContainer::Impl => {
+        rustc_ty::AssocContainer::Trait => get_path_to_callee(tcx, from, def_id),
+        rustc_ty::AssocContainer::InherentImpl | rustc_ty::AssocContainer::TraitImpl(_) => {
             let ty = tcx.type_of(def_id).instantiate_identity();
             get_path_to_ty(tcx, from, ty, args)
         },
@@ -3615,3 +3645,17 @@ pub fn expr_adjustment_requires_coercion(cx: &LateContext<'_>, expr: &Expr<'_>)
         )
     })
 }
+
+/// Checks if the expression is an async block (i.e., `async { ... }`).
+pub fn is_expr_async_block(expr: &Expr<'_>) -> bool {
+    matches!(
+        expr.kind,
+        ExprKind::Closure(Closure {
+            kind: hir::ClosureKind::Coroutine(CoroutineKind::Desugared(
+                CoroutineDesugaring::Async,
+                CoroutineSource::Block
+            )),
+            ..
+        })
+    )
+}
diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs
index 60473a26493..7cd5a16f5b4 100644
--- a/src/tools/clippy/clippy_utils/src/macros.rs
+++ b/src/tools/clippy/clippy_utils/src/macros.rs
@@ -250,18 +250,13 @@ impl<'a> PanicExpn<'a> {
         };
         let name = path.segments.last().unwrap().ident.name;
 
-        // This has no argument
-        if name == sym::panic_cold_explicit {
-            return Some(Self::Empty);
-        }
-
         let [arg, rest @ ..] = args else {
             return None;
         };
         let result = match name {
             sym::panic if arg.span.eq_ctxt(expr.span) => Self::Empty,
             sym::panic | sym::panic_str => Self::Str(arg),
-            sym::panic_display | sym::panic_cold_display => {
+            sym::panic_display => {
                 let ExprKind::AddrOf(_, _, e) = &arg.kind else {
                     return None;
                 };
diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs
index 89a83e2c48f..6e07ed9ffcc 100644
--- a/src/tools/clippy/clippy_utils/src/msrvs.rs
+++ b/src/tools/clippy/clippy_utils/src/msrvs.rs
@@ -24,7 +24,7 @@ macro_rules! msrv_aliases {
 // names may refer to stabilized feature flags or library items
 msrv_aliases! {
     1,88,0 { LET_CHAINS }
-    1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT, CONST_CHAR_IS_DIGIT, UNSIGNED_IS_MULTIPLE_OF }
+    1,87,0 { OS_STR_DISPLAY, INT_MIDPOINT, CONST_CHAR_IS_DIGIT, UNSIGNED_IS_MULTIPLE_OF, INTEGER_SIGN_CAST }
     1,85,0 { UINT_FLOAT_MIDPOINT, CONST_SIZE_OF_VAL }
     1,84,0 { CONST_OPTION_AS_SLICE, MANUAL_DANGLING_PTR }
     1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY, CONST_MUT_REFS, CONST_UNWRAP }
@@ -73,7 +73,7 @@ msrv_aliases! {
     1,29,0 { ITER_FLATTEN }
     1,28,0 { FROM_BOOL, REPEAT_WITH, SLICE_FROM_REF }
     1,27,0 { ITERATOR_TRY_FOLD }
-    1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN }
+    1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN, POINTER_ADD_SUB_METHODS }
     1,24,0 { IS_ASCII_DIGIT, PTR_NULL }
     1,18,0 { HASH_MAP_RETAIN, HASH_SET_RETAIN }
     1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR }
@@ -189,25 +189,25 @@ impl MsrvStack {
 fn parse_attrs(sess: &Session, attrs: &[impl AttributeExt]) -> Option<RustcVersion> {
     let mut msrv_attrs = attrs.iter().filter(|attr| attr.path_matches(&[sym::clippy, sym::msrv]));
 
-    if let Some(msrv_attr) = msrv_attrs.next() {
-        if let Some(duplicate) = msrv_attrs.next_back() {
-            sess.dcx()
-                .struct_span_err(duplicate.span(), "`clippy::msrv` is defined multiple times")
-                .with_span_note(msrv_attr.span(), "first definition found here")
-                .emit();
-        }
-
-        if let Some(msrv) = msrv_attr.value_str() {
-            if let Some(version) = parse_version(msrv) {
-                return Some(version);
-            }
+    let msrv_attr = msrv_attrs.next()?;
 
-            sess.dcx()
-                .span_err(msrv_attr.span(), format!("`{msrv}` is not a valid Rust version"));
-        } else {
-            sess.dcx().span_err(msrv_attr.span(), "bad clippy attribute");
-        }
+    if let Some(duplicate) = msrv_attrs.next_back() {
+        sess.dcx()
+            .struct_span_err(duplicate.span(), "`clippy::msrv` is defined multiple times")
+            .with_span_note(msrv_attr.span(), "first definition found here")
+            .emit();
     }
 
-    None
+    let Some(msrv) = msrv_attr.value_str() else {
+        sess.dcx().span_err(msrv_attr.span(), "bad clippy attribute");
+        return None;
+    };
+
+    let Some(version) = parse_version(msrv) else {
+        sess.dcx()
+            .span_err(msrv_attr.span(), format!("`{msrv}` is not a valid Rust version"));
+        return None;
+    };
+
+    Some(version)
 }
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 68f0b5ea255..2bda6d50373 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
@@ -86,7 +86,7 @@ fn check_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, span: Span, msrv: Msrv)
             ty::FnPtr(..) => {
                 return Err((span, "function pointers in const fn are unstable".into()));
             },
-            ty::Dynamic(preds, _, _) => {
+            ty::Dynamic(preds, _) => {
                 for pred in *preds {
                     match pred.skip_binder() {
                         ty::ExistentialPredicate::AutoTrait(_) | ty::ExistentialPredicate::Projection(_) => {
@@ -126,7 +126,7 @@ fn check_rvalue<'tcx>(
 ) -> McfResult {
     match rvalue {
         Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())),
-        Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::RawPtr(_, place) => {
+        Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::RawPtr(_, place) => {
             check_place(cx, *place, span, body, msrv)
         },
         Rvalue::CopyForDeref(place) => check_place(cx, *place, span, body, msrv),
diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs
index e675291b6f3..638d3290312 100644
--- a/src/tools/clippy/clippy_utils/src/source.rs
+++ b/src/tools/clippy/clippy_utils/src/source.rs
@@ -215,6 +215,11 @@ impl fmt::Display for SourceText {
         self.as_str().fmt(f)
     }
 }
+impl fmt::Debug for SourceText {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.as_str().fmt(f)
+    }
+}
 
 fn get_source_range(sm: &SourceMap, sp: Range<BytePos>) -> Option<SourceFileRange> {
     let start = sm.lookup_byte_offset(sp.start);
diff --git a/src/tools/clippy/clippy_utils/src/sym.rs b/src/tools/clippy/clippy_utils/src/sym.rs
index ce7cc9348fb..7530d3bc715 100644
--- a/src/tools/clippy/clippy_utils/src/sym.rs
+++ b/src/tools/clippy/clippy_utils/src/sym.rs
@@ -125,6 +125,7 @@ generate! {
     cycle,
     cyclomatic_complexity,
     de,
+    deprecated_in_future,
     diagnostics,
     disallowed_types,
     drain,
@@ -194,7 +195,6 @@ generate! {
     itertools,
     join,
     kw,
-    last,
     lazy_static,
     lint_vec,
     ln,
@@ -241,9 +241,6 @@ generate! {
     or_insert,
     or_insert_with,
     outer_expn,
-    panic_cold_display,
-    panic_cold_explicit,
-    panic_display,
     panic_str,
     parse,
     partition,
diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs
index d79773f8321..e4bc3b76829 100644
--- a/src/tools/clippy/clippy_utils/src/ty/mod.rs
+++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs
@@ -11,13 +11,14 @@ use rustc_hir as hir;
 use rustc_hir::attrs::AttributeKind;
 use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
 use rustc_hir::def_id::DefId;
-use rustc_hir::{Expr, FnDecl, LangItem, TyKind, find_attr};
+use rustc_hir::{Expr, FnDecl, LangItem, find_attr};
 use rustc_hir_analysis::lower_ty;
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::LateContext;
 use rustc_middle::mir::ConstValue;
 use rustc_middle::mir::interpret::Scalar;
 use rustc_middle::traits::EvaluationResult;
+use rustc_middle::ty::adjustment::{Adjust, Adjustment};
 use rustc_middle::ty::layout::ValidityRequirement;
 use rustc_middle::ty::{
     self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef,
@@ -31,7 +32,7 @@ use rustc_trait_selection::traits::query::normalize::QueryNormalizeExt;
 use rustc_trait_selection::traits::{Obligation, ObligationCause};
 use std::assert_matches::debug_assert_matches;
 use std::collections::hash_map::Entry;
-use std::iter;
+use std::{iter, mem};
 
 use crate::path_res;
 use crate::paths::{PathNS, lookup_path_str};
@@ -42,13 +43,8 @@ pub use type_certainty::expr_type_is_certain;
 /// Lower a [`hir::Ty`] to a [`rustc_middle::ty::Ty`].
 pub fn ty_from_hir_ty<'tcx>(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'tcx>) -> Ty<'tcx> {
     cx.maybe_typeck_results()
-        .and_then(|results| {
-            if results.hir_owner == hir_ty.hir_id.owner {
-                results.node_type_opt(hir_ty.hir_id)
-            } else {
-                None
-            }
-        })
+        .filter(|results| results.hir_owner == hir_ty.hir_id.owner)
+        .and_then(|results| results.node_type_opt(hir_ty.hir_id))
         .unwrap_or_else(|| lower_ty(cx.tcx, hir_ty))
 }
 
@@ -284,7 +280,7 @@ pub fn implements_trait_with_env_from_iter<'tcx>(
         let _ = tcx.hir_body_owner_kind(callee_id);
     }
 
-    let ty = tcx.erase_regions(ty);
+    let ty = tcx.erase_and_anonymize_regions(ty);
     if ty.has_escaping_bound_vars() {
         return false;
     }
@@ -348,7 +344,7 @@ pub fn is_must_use_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
             }
             false
         },
-        ty::Dynamic(binder, _, _) => {
+        ty::Dynamic(binder, _) => {
             for predicate in *binder {
                 if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder()
                     && find_attr!(cx.tcx.get_all_attrs(trait_ref.def_id), AttributeKind::MustUse { .. })
@@ -386,10 +382,7 @@ pub fn is_recursively_primitive_type(ty: Ty<'_>) -> bool {
 /// Checks if the type is a reference equals to a diagnostic item
 pub fn is_type_ref_to_diagnostic_item(cx: &LateContext<'_>, ty: Ty<'_>, diag_item: Symbol) -> bool {
     match ty.kind() {
-        ty::Ref(_, ref_ty, _) => match ref_ty.kind() {
-            ty::Adt(adt, _) => cx.tcx.is_diagnostic_item(diag_item, adt.did()),
-            _ => false,
-        },
+        ty::Ref(_, ref_ty, _) => is_type_diagnostic_item(cx, *ref_ty, diag_item),
         _ => false,
     }
 }
@@ -477,63 +470,50 @@ pub fn needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
     needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default())
 }
 
-/// Peels off all references on the type. Returns the underlying type, the number of references
-/// removed, and whether the pointer is ultimately mutable or not.
-pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) {
-    fn f(ty: Ty<'_>, count: usize, mutability: Mutability) -> (Ty<'_>, usize, Mutability) {
-        match ty.kind() {
-            ty::Ref(_, ty, Mutability::Mut) => f(*ty, count + 1, mutability),
-            ty::Ref(_, ty, Mutability::Not) => f(*ty, count + 1, Mutability::Not),
-            _ => (ty, count, mutability),
-        }
-    }
-    f(ty, 0, Mutability::Mut)
-}
-
-/// Returns `true` if the given type is an `unsafe` function.
-pub fn type_is_unsafe_function<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+/// Returns `true` if `ty` denotes an `unsafe fn`.
+pub fn is_unsafe_fn<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
     ty.is_fn() && ty.fn_sig(cx.tcx).safety().is_unsafe()
 }
 
-/// Returns the base type for HIR references and pointers.
-pub fn walk_ptrs_hir_ty<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> &'tcx hir::Ty<'tcx> {
-    match ty.kind {
-        TyKind::Ptr(ref mut_ty) | TyKind::Ref(_, ref mut_ty) => walk_ptrs_hir_ty(mut_ty.ty),
-        _ => ty,
-    }
-}
-
-/// Returns the base type for references and raw pointers, and count reference
-/// depth.
-pub fn walk_ptrs_ty_depth(ty: Ty<'_>) -> (Ty<'_>, usize) {
-    fn inner(ty: Ty<'_>, depth: usize) -> (Ty<'_>, usize) {
-        match ty.kind() {
-            ty::Ref(_, ty, _) => inner(*ty, depth + 1),
-            _ => (ty, depth),
-        }
+/// Peels off all references on the type. Returns the underlying type, the number of references
+/// removed, and, if there were any such references, whether the pointer is ultimately mutable or
+/// not.
+pub fn peel_and_count_ty_refs(mut ty: Ty<'_>) -> (Ty<'_>, usize, Option<Mutability>) {
+    let mut count = 0;
+    let mut mutbl = None;
+    while let ty::Ref(_, dest_ty, m) = ty.kind() {
+        ty = *dest_ty;
+        count += 1;
+        mutbl.replace(mutbl.map_or(*m, |mutbl: Mutability| mutbl.min(*m)));
     }
-    inner(ty, 0)
+    (ty, count, mutbl)
 }
 
-/// Returns `true` if types `a` and `b` are same types having same `Const` generic args,
-/// otherwise returns `false`
-pub fn same_type_and_consts<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
+/// Checks whether `a` and `b` are same types having same `Const` generic args, but ignores
+/// lifetimes.
+///
+/// For example, the function would return `true` for
+/// - `u32` and `u32`
+/// - `[u8; N]` and `[u8; M]`, if `N=M`
+/// - `Option<T>` and `Option<U>`, if `same_type_modulo_regions(T, U)` holds
+/// - `&'a str` and `&'b str`
+///
+/// and `false` for:
+/// - `Result<u32, String>` and `Result<usize, String>`
+pub fn same_type_modulo_regions<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
     match (&a.kind(), &b.kind()) {
         (&ty::Adt(did_a, args_a), &ty::Adt(did_b, args_b)) => {
             if did_a != did_b {
                 return false;
             }
 
-            args_a
-                .iter()
-                .zip(args_b.iter())
-                .all(|(arg_a, arg_b)| match (arg_a.kind(), arg_b.kind()) {
-                    (GenericArgKind::Const(inner_a), GenericArgKind::Const(inner_b)) => inner_a == inner_b,
-                    (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => {
-                        same_type_and_consts(type_a, type_b)
-                    },
-                    _ => true,
-                })
+            iter::zip(*args_a, *args_b).all(|(arg_a, arg_b)| match (arg_a.kind(), arg_b.kind()) {
+                (GenericArgKind::Const(inner_a), GenericArgKind::Const(inner_b)) => inner_a == inner_b,
+                (GenericArgKind::Type(type_a), GenericArgKind::Type(type_b)) => {
+                    same_type_modulo_regions(type_a, type_b)
+                },
+                _ => true,
+            })
         },
         _ => a == b,
     }
@@ -675,7 +655,7 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'t
             cx.tcx.opt_parent(def_id),
         ),
         ty::FnPtr(sig_tys, hdr) => Some(ExprFnSig::Sig(sig_tys.with(hdr), None)),
-        ty::Dynamic(bounds, _, _) => {
+        ty::Dynamic(bounds, _) => {
             let lang_items = cx.tcx.lang_items();
             match bounds.principal() {
                 Some(bound)
@@ -1377,12 +1357,9 @@ pub fn has_non_owning_mutable_access<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<'
 
 /// Check if `ty` is slice-like, i.e., `&[T]`, `[T; N]`, or `Vec<T>`.
 pub fn is_slice_like<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
-    ty.is_slice()
-        || ty.is_array()
-        || matches!(ty.kind(), ty::Adt(adt_def, _) if cx.tcx.is_diagnostic_item(sym::Vec, adt_def.did()))
+    ty.is_slice() || ty.is_array() || is_type_diagnostic_item(cx, ty, sym::Vec)
 }
 
-/// Gets the index of a field by name.
 pub fn get_field_idx_by_name(ty: Ty<'_>, name: Symbol) -> Option<usize> {
     match *ty.kind() {
         ty::Adt(def, _) if def.is_union() || def.is_struct() => {
@@ -1392,3 +1369,11 @@ pub fn get_field_idx_by_name(ty: Ty<'_>, name: Symbol) -> Option<usize> {
         _ => None,
     }
 }
+
+/// Checks if the adjustments contain a mutable dereference of a `ManuallyDrop<_>`.
+pub fn adjust_derefs_manually_drop<'tcx>(adjustments: &'tcx [Adjustment<'tcx>], mut ty: Ty<'tcx>) -> bool {
+    adjustments.iter().any(|a| {
+        let ty = mem::replace(&mut ty, a.target);
+        matches!(a.kind, Adjust::Deref(Some(op)) if op.mutbl == Mutability::Mut) && is_manually_drop(ty)
+    })
+}
diff --git a/src/tools/clippy/clippy_utils/src/usage.rs b/src/tools/clippy/clippy_utils/src/usage.rs
index 76d43feee12..6eccbcdb122 100644
--- a/src/tools/clippy/clippy_utils/src/usage.rs
+++ b/src/tools/clippy/clippy_utils/src/usage.rs
@@ -144,11 +144,9 @@ impl<'tcx> Visitor<'tcx> for BindingUsageFinder<'_, 'tcx> {
 
 /// Checks if the given expression is a macro call to `todo!()` or `unimplemented!()`.
 pub fn is_todo_unimplemented_macro(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    root_macro_call_first_node(cx, expr).is_some_and(|macro_call| {
-        [sym::todo_macro, sym::unimplemented_macro]
-            .iter()
-            .any(|&sym| cx.tcx.is_diagnostic_item(sym, macro_call.def_id))
-    })
+    root_macro_call_first_node(cx, expr)
+        .and_then(|macro_call| cx.tcx.get_diagnostic_name(macro_call.def_id))
+        .is_some_and(|macro_name| matches!(macro_name, sym::todo_macro | sym::unimplemented_macro))
 }
 
 /// Checks if the given expression is a stub, i.e., a `todo!()` or `unimplemented!()` expression,
diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs
index ba5cbc73836..c9f5401ebe7 100644
--- a/src/tools/clippy/clippy_utils/src/visitors.rs
+++ b/src/tools/clippy/clippy_utils/src/visitors.rs
@@ -460,7 +460,8 @@ pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
         }
         fn visit_nested_item(&mut self, id: ItemId) -> Self::Result {
             if let ItemKind::Impl(i) = &self.cx.tcx.hir_item(id).kind
-                && i.safety.is_unsafe()
+                && let Some(of_trait) = i.of_trait
+                && of_trait.safety.is_unsafe()
             {
                 ControlFlow::Break(())
             } else {
diff --git a/src/tools/clippy/declare_clippy_lint/Cargo.toml b/src/tools/clippy/declare_clippy_lint/Cargo.toml
index ec0e59e7054..4de7b5fb592 100644
--- a/src/tools/clippy/declare_clippy_lint/Cargo.toml
+++ b/src/tools/clippy/declare_clippy_lint/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "declare_clippy_lint"
-version = "0.1.91"
+version = "0.1.92"
 edition = "2024"
 repository = "https://github.com/rust-lang/rust-clippy"
 license = "MIT OR Apache-2.0"
diff --git a/src/tools/clippy/rust-toolchain.toml b/src/tools/clippy/rust-toolchain.toml
index ac51ec2d61b..9c102de4482 100644
--- a/src/tools/clippy/rust-toolchain.toml
+++ b/src/tools/clippy/rust-toolchain.toml
@@ -1,6 +1,6 @@
 [toolchain]
 # begin autogenerated nightly
-channel = "nightly-2025-08-07"
+channel = "nightly-2025-09-18"
 # end autogenerated nightly
 components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
 profile = "minimal"
diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs
index c4076cbaa77..6bddcbfd94c 100644
--- a/src/tools/clippy/src/driver.rs
+++ b/src/tools/clippy/src/driver.rs
@@ -330,7 +330,8 @@ pub fn main() {
 
         // Do not run Clippy for Cargo's info queries so that invalid CLIPPY_ARGS are not cached
         // https://github.com/rust-lang/cargo/issues/14385
-        let info_query = has_arg(&orig_args, "-vV") || has_arg(&orig_args, "--print");
+        let info_query = has_arg(&orig_args, "-vV")
+            || arg_value(&orig_args, "--print", |val| val != "crate-root-lint-levels").is_some();
 
         let clippy_enabled = !cap_lints_allow && relevant_package && !info_query;
         if clippy_enabled {
diff --git a/src/tools/clippy/tests/config-consistency.rs b/src/tools/clippy/tests/config-consistency.rs
new file mode 100644
index 00000000000..9e7ca26c7d4
--- /dev/null
+++ b/src/tools/clippy/tests/config-consistency.rs
@@ -0,0 +1,30 @@
+#![feature(rustc_private)]
+
+// This test checks that all lints defined in `clippy_config::conf` in `#[lints]`
+// attributes exist as Clippy lints.
+//
+// This test is a no-op if run as part of the compiler test suite
+// and will always succeed.
+
+use std::collections::HashSet;
+
+#[test]
+fn config_consistency() {
+    if option_env!("RUSTC_TEST_SUITE").is_some() {
+        return;
+    }
+
+    let lint_names: HashSet<String> = clippy_lints::declared_lints::LINTS
+        .iter()
+        .map(|lint_info| lint_info.lint.name.strip_prefix("clippy::").unwrap().to_lowercase())
+        .collect();
+    for conf in clippy_config::get_configuration_metadata() {
+        for lint in conf.lints {
+            assert!(
+                lint_names.contains(*lint),
+                "Configuration option {} references lint `{lint}` which does not exist",
+                conf.name
+            );
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/missing-test-files.rs b/src/tools/clippy/tests/missing-test-files.rs
index 565dcd73f58..63f960c92fa 100644
--- a/src/tools/clippy/tests/missing-test-files.rs
+++ b/src/tools/clippy/tests/missing-test-files.rs
@@ -1,6 +1,6 @@
 #![warn(rust_2018_idioms, unused_lifetimes)]
 #![allow(clippy::assertions_on_constants)]
-#![feature(path_file_prefix)]
+#![cfg_attr(bootstrap, feature(path_file_prefix))]
 
 use std::cmp::Ordering;
 use std::ffi::OsStr;
diff --git a/src/tools/clippy/tests/no-profile-in-cargo-toml.rs b/src/tools/clippy/tests/no-profile-in-cargo-toml.rs
new file mode 100644
index 00000000000..1f8c4fae9b3
--- /dev/null
+++ b/src/tools/clippy/tests/no-profile-in-cargo-toml.rs
@@ -0,0 +1,37 @@
+// Check that we do not have `profile.*` sections in our `Cargo.toml` files,
+// as this causes warnings when run from the compiler repository which includes
+// Clippy in a workspace.
+//
+// Those sections can be put into `.cargo/config.toml` which will be read
+// when commands are issued from the top-level Clippy directory, outside of
+// a workspace.
+
+use std::fs::File;
+use std::io::{self, BufRead as _};
+use walkdir::WalkDir;
+
+#[test]
+fn no_profile_in_cargo_toml() {
+    // This check could parse `Cargo.toml` using a TOML deserializer, but in practice
+    // profile sections would be added at the beginning of a line as `[profile.*]`, so
+    // keep it fast and simple.
+    for entry in WalkDir::new(".")
+        .into_iter()
+        // Do not recurse into `target` as lintcheck might put some sources (and their
+        //  `Cargo.toml`) there.
+        .filter_entry(|e| e.file_name() != "target")
+        .filter_map(Result::ok)
+        .filter(|e| e.file_name().to_str() == Some("Cargo.toml"))
+    {
+        for line in io::BufReader::new(File::open(entry.path()).unwrap())
+            .lines()
+            .map(Result::unwrap)
+        {
+            if line.starts_with("[profile.") {
+                eprintln!("Profile section `{line}` found in file `{}`.", entry.path().display());
+                eprintln!("Use `.cargo/config.toml` for profiles specific to the standalone Clippy repository.");
+                panic!("Profile section found in `Cargo.toml`");
+            }
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/ui-cargo/undocumented_unsafe_blocks/fail/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/undocumented_unsafe_blocks/fail/Cargo.stderr
new file mode 100644
index 00000000000..59a7146ac90
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/undocumented_unsafe_blocks/fail/Cargo.stderr
@@ -0,0 +1,26 @@
+error: module has unnecessary safety comment
+ --> src/main.rs:2:1
+  |
+2 | mod x {}
+  | ^^^^^^^^
+  |
+help: consider removing the safety comment
+ --> src/main.rs:1:1
+  |
+1 | // SAFETY: ...
+  | ^^^^^^^^^^^^^^
+  = note: requested on the command line with `-D clippy::unnecessary-safety-comment`
+
+error: module has unnecessary safety comment
+ --> src/main.rs:5:1
+  |
+5 | mod y {}
+  | ^^^^^^^^
+  |
+help: consider removing the safety comment
+ --> src/main.rs:4:1
+  |
+4 | // SAFETY: ...
+  | ^^^^^^^^^^^^^^
+
+error: could not compile `undocumented_unsafe_blocks` (bin "undocumented_unsafe_blocks") due to 2 previous errors
diff --git a/src/tools/clippy/tests/ui-cargo/undocumented_unsafe_blocks/fail/Cargo.toml b/src/tools/clippy/tests/ui-cargo/undocumented_unsafe_blocks/fail/Cargo.toml
new file mode 100644
index 00000000000..36bb3472df0
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/undocumented_unsafe_blocks/fail/Cargo.toml
@@ -0,0 +1,12 @@
+# Reproducing #14553 requires the `# Safety` comment to be in the first line of 
+# the file. Since `unnecessary_safety_comment` is not enabled by default, we
+# will set it up here.
+
+[package]
+name = "undocumented_unsafe_blocks"
+edition = "2024"
+publish = false
+version = "0.1.0"
+
+[lints.clippy]
+unnecessary_safety_comment = "deny"
diff --git a/src/tools/clippy/tests/ui-cargo/undocumented_unsafe_blocks/fail/src/main.rs b/src/tools/clippy/tests/ui-cargo/undocumented_unsafe_blocks/fail/src/main.rs
new file mode 100644
index 00000000000..5cafcff99dd
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/undocumented_unsafe_blocks/fail/src/main.rs
@@ -0,0 +1,7 @@
+// SAFETY: ...
+mod x {}
+
+// SAFETY: ...
+mod y {}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr b/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr
index f03a745963e..e9d53c64dd9 100644
--- a/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr
+++ b/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr
@@ -4,7 +4,7 @@ error: use of a disallowed method `rustc_lint::context::LintContext::span_lint`
 LL |     cx.span_lint(lint, span, |lint| {
    |        ^^^^^^^^^
    |
-   = note: this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead
+   = note: this function does not add a link to our documentation; please use the `clippy_utils::diagnostics::span_lint*` functions instead
 note: the lint level is defined here
   --> tests/ui-internal/disallow_span_lint.rs:2:9
    |
@@ -17,7 +17,7 @@ error: use of a disallowed method `rustc_middle::ty::context::TyCtxt::node_span_
 LL |     tcx.node_span_lint(lint, hir_id, span, |lint| {
    |         ^^^^^^^^^^^^^^
    |
-   = note: this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead
+   = note: this function does not add a link to our documentation; please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead
 
 error: aborting due to 2 previous errors
 
diff --git a/src/tools/clippy/tests/ui-toml/excessive_precision/clippy.toml b/src/tools/clippy/tests/ui-toml/excessive_precision/clippy.toml
new file mode 100644
index 00000000000..c7fc230dcb3
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/excessive_precision/clippy.toml
@@ -0,0 +1 @@
+const-literal-digits-threshold = 20
diff --git a/src/tools/clippy/tests/ui-toml/excessive_precision/excessive_precision.fixed b/src/tools/clippy/tests/ui-toml/excessive_precision/excessive_precision.fixed
new file mode 100644
index 00000000000..577bbff2957
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/excessive_precision/excessive_precision.fixed
@@ -0,0 +1,38 @@
+#![warn(clippy::excessive_precision)]
+#![allow(
+    dead_code,
+    overflowing_literals,
+    unused_variables,
+    clippy::print_literal,
+    clippy::useless_vec
+)]
+
+fn main() {
+    // Overly specified constants
+    let _: f32 = 1.012_345_7;
+    //~^ excessive_precision
+    let _: f64 = 1.012_345_678_901_234_6;
+    //~^ excessive_precision
+    const _: f32 = 1.012345678901234567890;
+    const _: f64 = 1.012345678901234567890;
+
+    static STATIC1: f32 = 1.012345678901234567890;
+    static STATIC2: f64 = 1.012345678901234567890;
+
+    static mut STATIC_MUT1: f32 = 1.012345678901234567890;
+    static mut STATIC_MUT2: f64 = 1.012345678901234567890;
+}
+
+trait ExcessivelyPreciseTrait {
+    // Overly specified constants
+    const GOOD1: f32 = 1.012345678901234567890;
+    const GOOD2: f64 = 1.012345678901234567890;
+}
+
+struct ExcessivelyPreciseStruct;
+
+impl ExcessivelyPreciseStruct {
+    // Overly specified constants
+    const GOOD1: f32 = 1.012345678901234567890;
+    const GOOD2: f64 = 1.012345678901234567890;
+}
diff --git a/src/tools/clippy/tests/ui-toml/excessive_precision/excessive_precision.rs b/src/tools/clippy/tests/ui-toml/excessive_precision/excessive_precision.rs
new file mode 100644
index 00000000000..121448ed540
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/excessive_precision/excessive_precision.rs
@@ -0,0 +1,38 @@
+#![warn(clippy::excessive_precision)]
+#![allow(
+    dead_code,
+    overflowing_literals,
+    unused_variables,
+    clippy::print_literal,
+    clippy::useless_vec
+)]
+
+fn main() {
+    // Overly specified constants
+    let _: f32 = 1.012345678901234567890;
+    //~^ excessive_precision
+    let _: f64 = 1.012345678901234567890;
+    //~^ excessive_precision
+    const _: f32 = 1.012345678901234567890;
+    const _: f64 = 1.012345678901234567890;
+
+    static STATIC1: f32 = 1.012345678901234567890;
+    static STATIC2: f64 = 1.012345678901234567890;
+
+    static mut STATIC_MUT1: f32 = 1.012345678901234567890;
+    static mut STATIC_MUT2: f64 = 1.012345678901234567890;
+}
+
+trait ExcessivelyPreciseTrait {
+    // Overly specified constants
+    const GOOD1: f32 = 1.012345678901234567890;
+    const GOOD2: f64 = 1.012345678901234567890;
+}
+
+struct ExcessivelyPreciseStruct;
+
+impl ExcessivelyPreciseStruct {
+    // Overly specified constants
+    const GOOD1: f32 = 1.012345678901234567890;
+    const GOOD2: f64 = 1.012345678901234567890;
+}
diff --git a/src/tools/clippy/tests/ui-toml/excessive_precision/excessive_precision.stderr b/src/tools/clippy/tests/ui-toml/excessive_precision/excessive_precision.stderr
new file mode 100644
index 00000000000..65d33eddef1
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/excessive_precision/excessive_precision.stderr
@@ -0,0 +1,38 @@
+error: float has excessive precision
+  --> tests/ui-toml/excessive_precision/excessive_precision.rs:12:18
+   |
+LL |     let _: f32 = 1.012345678901234567890;
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: consider making it a `const` item
+  --> tests/ui-toml/excessive_precision/excessive_precision.rs:12:5
+   |
+LL |     let _: f32 = 1.012345678901234567890;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   = note: `-D clippy::excessive-precision` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::excessive_precision)]`
+help: consider changing the type or truncating it to
+   |
+LL -     let _: f32 = 1.012345678901234567890;
+LL +     let _: f32 = 1.012_345_7;
+   |
+
+error: float has excessive precision
+  --> tests/ui-toml/excessive_precision/excessive_precision.rs:14:18
+   |
+LL |     let _: f64 = 1.012345678901234567890;
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: consider making it a `const` item
+  --> tests/ui-toml/excessive_precision/excessive_precision.rs:14:5
+   |
+LL |     let _: f64 = 1.012345678901234567890;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: consider changing the type or truncating it to
+   |
+LL -     let _: f64 = 1.012345678901234567890;
+LL +     let _: f64 = 1.012_345_678_901_234_6;
+   |
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui-toml/functions_maxlines/test.stderr b/src/tools/clippy/tests/ui-toml/functions_maxlines/test.stderr
index 14a49cb76c1..e856963c87d 100644
--- a/src/tools/clippy/tests/ui-toml/functions_maxlines/test.stderr
+++ b/src/tools/clippy/tests/ui-toml/functions_maxlines/test.stderr
@@ -1,12 +1,8 @@
 error: this function has too many lines (2/1)
   --> tests/ui-toml/functions_maxlines/test.rs:19:1
    |
-LL | / fn too_many_lines() {
-LL | |
-LL | |     println!("This is bad.");
-LL | |     println!("This is bad.");
-LL | | }
-   | |_^
+LL | fn too_many_lines() {
+   | ^^^^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::too-many-lines` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::too_many_lines)]`
@@ -14,35 +10,20 @@ LL | | }
 error: this function has too many lines (4/1)
   --> tests/ui-toml/functions_maxlines/test.rs:26:1
    |
-LL | / async fn async_too_many_lines() {
-LL | |
-LL | |     println!("This is bad.");
-LL | |     println!("This is bad.");
-LL | | }
-   | |_^
+LL | async fn async_too_many_lines() {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: this function has too many lines (4/1)
   --> tests/ui-toml/functions_maxlines/test.rs:33:1
    |
-LL | / fn closure_too_many_lines() {
-LL | |
-LL | |     let _ = {
-LL | |         println!("This is bad.");
-LL | |         println!("This is bad.");
-LL | |     };
-LL | | }
-   | |_^
+LL | fn closure_too_many_lines() {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: this function has too many lines (2/1)
   --> tests/ui-toml/functions_maxlines/test.rs:56:1
    |
-LL | / fn comment_before_code() {
-LL | |
-LL | |     let _ = "test";
-LL | |     /* This comment extends to the front of
-LL | |     the code but this line should still count. */ let _ = 5;
-LL | | }
-   | |_^
+LL | fn comment_before_code() {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to 4 previous errors
 
diff --git a/src/tools/clippy/tests/ui/ref_option/all/clippy.toml b/src/tools/clippy/tests/ui-toml/ref_option/all/clippy.toml
index cda8d17eed4..cda8d17eed4 100644
--- a/src/tools/clippy/tests/ui/ref_option/all/clippy.toml
+++ b/src/tools/clippy/tests/ui-toml/ref_option/all/clippy.toml
diff --git a/src/tools/clippy/tests/ui/ref_option/private/clippy.toml b/src/tools/clippy/tests/ui-toml/ref_option/private/clippy.toml
index 5f304987aa9..5f304987aa9 100644
--- a/src/tools/clippy/tests/ui/ref_option/private/clippy.toml
+++ b/src/tools/clippy/tests/ui-toml/ref_option/private/clippy.toml
diff --git a/src/tools/clippy/tests/ui-toml/ref_option/ref_option.all.fixed b/src/tools/clippy/tests/ui-toml/ref_option/ref_option.all.fixed
new file mode 100644
index 00000000000..f8f097e9a75
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/ref_option/ref_option.all.fixed
@@ -0,0 +1,114 @@
+//@aux-build:../../ui/auxiliary/proc_macros.rs
+//@revisions: private all
+//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/private
+//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/all
+
+#![allow(unused, clippy::needless_lifetimes, clippy::borrowed_box)]
+#![warn(clippy::ref_option)]
+
+fn opt_u8(a: Option<&u8>) {}
+//~^ ref_option
+fn opt_gen<T>(a: Option<&T>) {}
+//~^ ref_option
+fn opt_string(a: std::option::Option<&String>) {}
+//~^ ref_option
+fn ret_u8<'a>(p: &'a str) -> Option<&'a u8> {
+    //~^ ref_option
+    panic!()
+}
+fn ret_u8_static() -> Option<&'static u8> {
+    //~^ ref_option
+    panic!()
+}
+fn mult_string(a: Option<&String>, b: Option<&Vec<u8>>) {}
+//~^ ref_option
+fn ret_box<'a>() -> Option<&'a Box<u8>> {
+    //~^ ref_option
+    panic!()
+}
+
+pub fn pub_opt_string(a: Option<&String>) {}
+//~[all]^ ref_option
+pub fn pub_mult_string(a: Option<&String>, b: Option<&Vec<u8>>) {}
+//~[all]^ ref_option
+
+pub struct PubStruct;
+
+impl PubStruct {
+    pub fn pub_opt_params(&self, a: Option<&()>) {}
+    //~[all]^ ref_option
+    pub fn pub_opt_ret(&self) -> Option<&String> {
+        //~[all]^ ref_option
+        panic!()
+    }
+
+    fn private_opt_params(&self, a: Option<&()>) {}
+    //~^ ref_option
+    fn private_opt_ret(&self) -> Option<&String> {
+        //~^ ref_option
+        panic!()
+    }
+}
+
+// valid, don't change
+fn mut_u8(a: &mut Option<u8>) {}
+pub fn pub_mut_u8(a: &mut Option<String>) {}
+
+// might be good to catch in the future
+fn mut_u8_ref(a: &mut &Option<u8>) {}
+pub fn pub_mut_u8_ref(a: &mut &Option<String>) {}
+fn lambdas() {
+    // Not handled for now, not sure if we should
+    let x = |a: &Option<String>| {};
+    let x = |a: &Option<String>| -> &Option<String> { panic!() };
+}
+
+pub mod external {
+    proc_macros::external!(
+        fn opt_u8(a: &Option<u8>) {}
+        fn ret_u8<'a>(p: &'a str) -> &'a Option<u8> {
+            panic!()
+        }
+        pub fn pub_opt_u8(a: &Option<u8>) {}
+
+        pub struct PubStruct;
+        impl PubStruct {
+            pub fn pub_opt_params(&self, a: &Option<()>) {}
+            pub fn pub_opt_ret(&self) -> &Option<String> {
+                panic!()
+            }
+
+            fn private_opt_params(&self, a: &Option<()>) {}
+            fn private_opt_ret(&self) -> &Option<String> {
+                panic!()
+            }
+        }
+    );
+}
+
+pub mod proc_macros {
+    proc_macros::with_span!(
+        span
+
+        fn opt_u8(a: &Option<u8>) {}
+        fn ret_u8<'a>(p: &'a str) -> &'a Option<u8> {
+            panic!()
+        }
+        pub fn pub_opt_u8(a: &Option<u8>) {}
+
+        pub struct PubStruct;
+        impl PubStruct {
+            pub fn pub_opt_params(&self, a: &Option<()>) {}
+            pub fn pub_opt_ret(&self) -> &Option<String> {
+                panic!()
+            }
+
+            fn private_opt_params(&self, a: &Option<()>) {}
+            fn private_opt_ret(&self) -> &Option<String> {
+                panic!()
+            }
+        }
+    );
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/ref_option/ref_option.all.stderr b/src/tools/clippy/tests/ui-toml/ref_option/ref_option.all.stderr
index bd43c28336e..45ce105e030 100644
--- a/src/tools/clippy/tests/ui/ref_option/ref_option.all.stderr
+++ b/src/tools/clippy/tests/ui-toml/ref_option/ref_option.all.stderr
@@ -1,5 +1,5 @@
 error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
-  --> tests/ui/ref_option/ref_option.rs:8:1
+  --> tests/ui-toml/ref_option/ref_option.rs:9:1
    |
 LL | fn opt_u8(a: &Option<u8>) {}
    | ^^^^^^^^^^^^^-----------^^^^
@@ -10,7 +10,7 @@ LL | fn opt_u8(a: &Option<u8>) {}
    = help: to override `-D warnings` add `#[allow(clippy::ref_option)]`
 
 error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
-  --> tests/ui/ref_option/ref_option.rs:10:1
+  --> tests/ui-toml/ref_option/ref_option.rs:11:1
    |
 LL | fn opt_gen<T>(a: &Option<T>) {}
    | ^^^^^^^^^^^^^^^^^----------^^^^
@@ -18,7 +18,7 @@ LL | fn opt_gen<T>(a: &Option<T>) {}
    |                  help: change this to: `Option<&T>`
 
 error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
-  --> tests/ui/ref_option/ref_option.rs:12:1
+  --> tests/ui-toml/ref_option/ref_option.rs:13:1
    |
 LL | fn opt_string(a: &std::option::Option<String>) {}
    | ^^^^^^^^^^^^^^^^^----------------------------^^^^
@@ -26,10 +26,10 @@ LL | fn opt_string(a: &std::option::Option<String>) {}
    |                  help: change this to: `std::option::Option<&String>`
 
 error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
-  --> tests/ui/ref_option/ref_option.rs:14:1
+  --> tests/ui-toml/ref_option/ref_option.rs:15:1
    |
-LL |   fn ret_string<'a>(p: &'a str) -> &'a Option<u8> {
-   |   ^                                -------------- help: change this to: `Option<&'a u8>`
+LL |   fn ret_u8<'a>(p: &'a str) -> &'a Option<u8> {
+   |   ^                            -------------- help: change this to: `Option<&'a u8>`
    |  _|
    | |
 LL | |
@@ -38,10 +38,10 @@ LL | | }
    | |_^
 
 error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
-  --> tests/ui/ref_option/ref_option.rs:18:1
+  --> tests/ui-toml/ref_option/ref_option.rs:19:1
    |
-LL |   fn ret_string_static() -> &'static Option<u8> {
-   |   ^                         ------------------- help: change this to: `Option<&'static u8>`
+LL |   fn ret_u8_static() -> &'static Option<u8> {
+   |   ^                     ------------------- help: change this to: `Option<&'static u8>`
    |  _|
    | |
 LL | |
@@ -50,7 +50,7 @@ LL | | }
    | |_^
 
 error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
-  --> tests/ui/ref_option/ref_option.rs:22:1
+  --> tests/ui-toml/ref_option/ref_option.rs:23:1
    |
 LL | fn mult_string(a: &Option<String>, b: &Option<Vec<u8>>) {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -62,7 +62,7 @@ LL + fn mult_string(a: Option<&String>, b: Option<&Vec<u8>>) {}
    |
 
 error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
-  --> tests/ui/ref_option/ref_option.rs:24:1
+  --> tests/ui-toml/ref_option/ref_option.rs:25:1
    |
 LL |   fn ret_box<'a>() -> &'a Option<Box<u8>> {
    |   ^                   ------------------- help: change this to: `Option<&'a Box<u8>>`
@@ -74,7 +74,7 @@ LL | | }
    | |_^
 
 error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
-  --> tests/ui/ref_option/ref_option.rs:29:1
+  --> tests/ui-toml/ref_option/ref_option.rs:30:1
    |
 LL | pub fn pub_opt_string(a: &Option<String>) {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^---------------^^^^
@@ -82,7 +82,7 @@ LL | pub fn pub_opt_string(a: &Option<String>) {}
    |                          help: change this to: `Option<&String>`
 
 error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
-  --> tests/ui/ref_option/ref_option.rs:31:1
+  --> tests/ui-toml/ref_option/ref_option.rs:32:1
    |
 LL | pub fn pub_mult_string(a: &Option<String>, b: &Option<Vec<u8>>) {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -94,39 +94,7 @@ LL + pub fn pub_mult_string(a: Option<&String>, b: Option<&Vec<u8>>) {}
    |
 
 error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
-  --> tests/ui/ref_option/ref_option.rs:35:5
-   |
-LL |     fn pub_trait_opt(&self, a: &Option<Vec<u8>>);
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------^^
-   |                                |
-   |                                help: change this to: `Option<&Vec<u8>>`
-
-error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
-  --> tests/ui/ref_option/ref_option.rs:37:5
-   |
-LL |     fn pub_trait_ret(&self) -> &Option<Vec<u8>>;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------^
-   |                                |
-   |                                help: change this to: `Option<&Vec<u8>>`
-
-error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
-  --> tests/ui/ref_option/ref_option.rs:42:5
-   |
-LL |     fn trait_opt(&self, a: &Option<String>);
-   |     ^^^^^^^^^^^^^^^^^^^^^^^---------------^^
-   |                            |
-   |                            help: change this to: `Option<&String>`
-
-error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
-  --> tests/ui/ref_option/ref_option.rs:44:5
-   |
-LL |     fn trait_ret(&self) -> &Option<String>;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^---------------^
-   |                            |
-   |                            help: change this to: `Option<&String>`
-
-error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
-  --> tests/ui/ref_option/ref_option.rs:51:5
+  --> tests/ui-toml/ref_option/ref_option.rs:38:5
    |
 LL |     pub fn pub_opt_params(&self, a: &Option<()>) {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^^^
@@ -134,7 +102,7 @@ LL |     pub fn pub_opt_params(&self, a: &Option<()>) {}
    |                                     help: change this to: `Option<&()>`
 
 error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
-  --> tests/ui/ref_option/ref_option.rs:53:5
+  --> tests/ui-toml/ref_option/ref_option.rs:40:5
    |
 LL |       pub fn pub_opt_ret(&self) -> &Option<String> {
    |       ^                            --------------- help: change this to: `Option<&String>`
@@ -146,7 +114,7 @@ LL | |     }
    | |_____^
 
 error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
-  --> tests/ui/ref_option/ref_option.rs:58:5
+  --> tests/ui-toml/ref_option/ref_option.rs:45:5
    |
 LL |     fn private_opt_params(&self, a: &Option<()>) {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^^^
@@ -154,7 +122,7 @@ LL |     fn private_opt_params(&self, a: &Option<()>) {}
    |                                     help: change this to: `Option<&()>`
 
 error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
-  --> tests/ui/ref_option/ref_option.rs:60:5
+  --> tests/ui-toml/ref_option/ref_option.rs:47:5
    |
 LL |       fn private_opt_ret(&self) -> &Option<String> {
    |       ^                            --------------- help: change this to: `Option<&String>`
@@ -165,5 +133,5 @@ LL | |         panic!()
 LL | |     }
    | |_____^
 
-error: aborting due to 17 previous errors
+error: aborting due to 13 previous errors
 
diff --git a/src/tools/clippy/tests/ui-toml/ref_option/ref_option.private.fixed b/src/tools/clippy/tests/ui-toml/ref_option/ref_option.private.fixed
new file mode 100644
index 00000000000..4dd14a82206
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/ref_option/ref_option.private.fixed
@@ -0,0 +1,114 @@
+//@aux-build:../../ui/auxiliary/proc_macros.rs
+//@revisions: private all
+//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/private
+//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/all
+
+#![allow(unused, clippy::needless_lifetimes, clippy::borrowed_box)]
+#![warn(clippy::ref_option)]
+
+fn opt_u8(a: Option<&u8>) {}
+//~^ ref_option
+fn opt_gen<T>(a: Option<&T>) {}
+//~^ ref_option
+fn opt_string(a: std::option::Option<&String>) {}
+//~^ ref_option
+fn ret_u8<'a>(p: &'a str) -> Option<&'a u8> {
+    //~^ ref_option
+    panic!()
+}
+fn ret_u8_static() -> Option<&'static u8> {
+    //~^ ref_option
+    panic!()
+}
+fn mult_string(a: Option<&String>, b: Option<&Vec<u8>>) {}
+//~^ ref_option
+fn ret_box<'a>() -> Option<&'a Box<u8>> {
+    //~^ ref_option
+    panic!()
+}
+
+pub fn pub_opt_string(a: &Option<String>) {}
+//~[all]^ ref_option
+pub fn pub_mult_string(a: &Option<String>, b: &Option<Vec<u8>>) {}
+//~[all]^ ref_option
+
+pub struct PubStruct;
+
+impl PubStruct {
+    pub fn pub_opt_params(&self, a: &Option<()>) {}
+    //~[all]^ ref_option
+    pub fn pub_opt_ret(&self) -> &Option<String> {
+        //~[all]^ ref_option
+        panic!()
+    }
+
+    fn private_opt_params(&self, a: Option<&()>) {}
+    //~^ ref_option
+    fn private_opt_ret(&self) -> Option<&String> {
+        //~^ ref_option
+        panic!()
+    }
+}
+
+// valid, don't change
+fn mut_u8(a: &mut Option<u8>) {}
+pub fn pub_mut_u8(a: &mut Option<String>) {}
+
+// might be good to catch in the future
+fn mut_u8_ref(a: &mut &Option<u8>) {}
+pub fn pub_mut_u8_ref(a: &mut &Option<String>) {}
+fn lambdas() {
+    // Not handled for now, not sure if we should
+    let x = |a: &Option<String>| {};
+    let x = |a: &Option<String>| -> &Option<String> { panic!() };
+}
+
+pub mod external {
+    proc_macros::external!(
+        fn opt_u8(a: &Option<u8>) {}
+        fn ret_u8<'a>(p: &'a str) -> &'a Option<u8> {
+            panic!()
+        }
+        pub fn pub_opt_u8(a: &Option<u8>) {}
+
+        pub struct PubStruct;
+        impl PubStruct {
+            pub fn pub_opt_params(&self, a: &Option<()>) {}
+            pub fn pub_opt_ret(&self) -> &Option<String> {
+                panic!()
+            }
+
+            fn private_opt_params(&self, a: &Option<()>) {}
+            fn private_opt_ret(&self) -> &Option<String> {
+                panic!()
+            }
+        }
+    );
+}
+
+pub mod proc_macros {
+    proc_macros::with_span!(
+        span
+
+        fn opt_u8(a: &Option<u8>) {}
+        fn ret_u8<'a>(p: &'a str) -> &'a Option<u8> {
+            panic!()
+        }
+        pub fn pub_opt_u8(a: &Option<u8>) {}
+
+        pub struct PubStruct;
+        impl PubStruct {
+            pub fn pub_opt_params(&self, a: &Option<()>) {}
+            pub fn pub_opt_ret(&self) -> &Option<String> {
+                panic!()
+            }
+
+            fn private_opt_params(&self, a: &Option<()>) {}
+            fn private_opt_ret(&self) -> &Option<String> {
+                panic!()
+            }
+        }
+    );
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/ref_option/ref_option.private.stderr b/src/tools/clippy/tests/ui-toml/ref_option/ref_option.private.stderr
index 88c65e429d8..a63efd60a03 100644
--- a/src/tools/clippy/tests/ui/ref_option/ref_option.private.stderr
+++ b/src/tools/clippy/tests/ui-toml/ref_option/ref_option.private.stderr
@@ -1,5 +1,5 @@
 error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
-  --> tests/ui/ref_option/ref_option.rs:8:1
+  --> tests/ui-toml/ref_option/ref_option.rs:9:1
    |
 LL | fn opt_u8(a: &Option<u8>) {}
    | ^^^^^^^^^^^^^-----------^^^^
@@ -10,7 +10,7 @@ LL | fn opt_u8(a: &Option<u8>) {}
    = help: to override `-D warnings` add `#[allow(clippy::ref_option)]`
 
 error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
-  --> tests/ui/ref_option/ref_option.rs:10:1
+  --> tests/ui-toml/ref_option/ref_option.rs:11:1
    |
 LL | fn opt_gen<T>(a: &Option<T>) {}
    | ^^^^^^^^^^^^^^^^^----------^^^^
@@ -18,7 +18,7 @@ LL | fn opt_gen<T>(a: &Option<T>) {}
    |                  help: change this to: `Option<&T>`
 
 error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
-  --> tests/ui/ref_option/ref_option.rs:12:1
+  --> tests/ui-toml/ref_option/ref_option.rs:13:1
    |
 LL | fn opt_string(a: &std::option::Option<String>) {}
    | ^^^^^^^^^^^^^^^^^----------------------------^^^^
@@ -26,10 +26,10 @@ LL | fn opt_string(a: &std::option::Option<String>) {}
    |                  help: change this to: `std::option::Option<&String>`
 
 error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
-  --> tests/ui/ref_option/ref_option.rs:14:1
+  --> tests/ui-toml/ref_option/ref_option.rs:15:1
    |
-LL |   fn ret_string<'a>(p: &'a str) -> &'a Option<u8> {
-   |   ^                                -------------- help: change this to: `Option<&'a u8>`
+LL |   fn ret_u8<'a>(p: &'a str) -> &'a Option<u8> {
+   |   ^                            -------------- help: change this to: `Option<&'a u8>`
    |  _|
    | |
 LL | |
@@ -38,10 +38,10 @@ LL | | }
    | |_^
 
 error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
-  --> tests/ui/ref_option/ref_option.rs:18:1
+  --> tests/ui-toml/ref_option/ref_option.rs:19:1
    |
-LL |   fn ret_string_static() -> &'static Option<u8> {
-   |   ^                         ------------------- help: change this to: `Option<&'static u8>`
+LL |   fn ret_u8_static() -> &'static Option<u8> {
+   |   ^                     ------------------- help: change this to: `Option<&'static u8>`
    |  _|
    | |
 LL | |
@@ -50,7 +50,7 @@ LL | | }
    | |_^
 
 error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
-  --> tests/ui/ref_option/ref_option.rs:22:1
+  --> tests/ui-toml/ref_option/ref_option.rs:23:1
    |
 LL | fn mult_string(a: &Option<String>, b: &Option<Vec<u8>>) {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -62,7 +62,7 @@ LL + fn mult_string(a: Option<&String>, b: Option<&Vec<u8>>) {}
    |
 
 error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
-  --> tests/ui/ref_option/ref_option.rs:24:1
+  --> tests/ui-toml/ref_option/ref_option.rs:25:1
    |
 LL |   fn ret_box<'a>() -> &'a Option<Box<u8>> {
    |   ^                   ------------------- help: change this to: `Option<&'a Box<u8>>`
@@ -74,23 +74,7 @@ LL | | }
    | |_^
 
 error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
-  --> tests/ui/ref_option/ref_option.rs:42:5
-   |
-LL |     fn trait_opt(&self, a: &Option<String>);
-   |     ^^^^^^^^^^^^^^^^^^^^^^^---------------^^
-   |                            |
-   |                            help: change this to: `Option<&String>`
-
-error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
-  --> tests/ui/ref_option/ref_option.rs:44:5
-   |
-LL |     fn trait_ret(&self) -> &Option<String>;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^---------------^
-   |                            |
-   |                            help: change this to: `Option<&String>`
-
-error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
-  --> tests/ui/ref_option/ref_option.rs:58:5
+  --> tests/ui-toml/ref_option/ref_option.rs:45:5
    |
 LL |     fn private_opt_params(&self, a: &Option<()>) {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-----------^^^^
@@ -98,7 +82,7 @@ LL |     fn private_opt_params(&self, a: &Option<()>) {}
    |                                     help: change this to: `Option<&()>`
 
 error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
-  --> tests/ui/ref_option/ref_option.rs:60:5
+  --> tests/ui-toml/ref_option/ref_option.rs:47:5
    |
 LL |       fn private_opt_ret(&self) -> &Option<String> {
    |       ^                            --------------- help: change this to: `Option<&String>`
@@ -109,5 +93,5 @@ LL | |         panic!()
 LL | |     }
    | |_____^
 
-error: aborting due to 11 previous errors
+error: aborting due to 9 previous errors
 
diff --git a/src/tools/clippy/tests/ui-toml/ref_option/ref_option.rs b/src/tools/clippy/tests/ui-toml/ref_option/ref_option.rs
new file mode 100644
index 00000000000..8397c2213d1
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/ref_option/ref_option.rs
@@ -0,0 +1,114 @@
+//@aux-build:../../ui/auxiliary/proc_macros.rs
+//@revisions: private all
+//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/private
+//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/all
+
+#![allow(unused, clippy::needless_lifetimes, clippy::borrowed_box)]
+#![warn(clippy::ref_option)]
+
+fn opt_u8(a: &Option<u8>) {}
+//~^ ref_option
+fn opt_gen<T>(a: &Option<T>) {}
+//~^ ref_option
+fn opt_string(a: &std::option::Option<String>) {}
+//~^ ref_option
+fn ret_u8<'a>(p: &'a str) -> &'a Option<u8> {
+    //~^ ref_option
+    panic!()
+}
+fn ret_u8_static() -> &'static Option<u8> {
+    //~^ ref_option
+    panic!()
+}
+fn mult_string(a: &Option<String>, b: &Option<Vec<u8>>) {}
+//~^ ref_option
+fn ret_box<'a>() -> &'a Option<Box<u8>> {
+    //~^ ref_option
+    panic!()
+}
+
+pub fn pub_opt_string(a: &Option<String>) {}
+//~[all]^ ref_option
+pub fn pub_mult_string(a: &Option<String>, b: &Option<Vec<u8>>) {}
+//~[all]^ ref_option
+
+pub struct PubStruct;
+
+impl PubStruct {
+    pub fn pub_opt_params(&self, a: &Option<()>) {}
+    //~[all]^ ref_option
+    pub fn pub_opt_ret(&self) -> &Option<String> {
+        //~[all]^ ref_option
+        panic!()
+    }
+
+    fn private_opt_params(&self, a: &Option<()>) {}
+    //~^ ref_option
+    fn private_opt_ret(&self) -> &Option<String> {
+        //~^ ref_option
+        panic!()
+    }
+}
+
+// valid, don't change
+fn mut_u8(a: &mut Option<u8>) {}
+pub fn pub_mut_u8(a: &mut Option<String>) {}
+
+// might be good to catch in the future
+fn mut_u8_ref(a: &mut &Option<u8>) {}
+pub fn pub_mut_u8_ref(a: &mut &Option<String>) {}
+fn lambdas() {
+    // Not handled for now, not sure if we should
+    let x = |a: &Option<String>| {};
+    let x = |a: &Option<String>| -> &Option<String> { panic!() };
+}
+
+pub mod external {
+    proc_macros::external!(
+        fn opt_u8(a: &Option<u8>) {}
+        fn ret_u8<'a>(p: &'a str) -> &'a Option<u8> {
+            panic!()
+        }
+        pub fn pub_opt_u8(a: &Option<u8>) {}
+
+        pub struct PubStruct;
+        impl PubStruct {
+            pub fn pub_opt_params(&self, a: &Option<()>) {}
+            pub fn pub_opt_ret(&self) -> &Option<String> {
+                panic!()
+            }
+
+            fn private_opt_params(&self, a: &Option<()>) {}
+            fn private_opt_ret(&self) -> &Option<String> {
+                panic!()
+            }
+        }
+    );
+}
+
+pub mod proc_macros {
+    proc_macros::with_span!(
+        span
+
+        fn opt_u8(a: &Option<u8>) {}
+        fn ret_u8<'a>(p: &'a str) -> &'a Option<u8> {
+            panic!()
+        }
+        pub fn pub_opt_u8(a: &Option<u8>) {}
+
+        pub struct PubStruct;
+        impl PubStruct {
+            pub fn pub_opt_params(&self, a: &Option<()>) {}
+            pub fn pub_opt_ret(&self) -> &Option<String> {
+                panic!()
+            }
+
+            fn private_opt_params(&self, a: &Option<()>) {}
+            fn private_opt_ret(&self) -> &Option<String> {
+                panic!()
+            }
+        }
+    );
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/ref_option/ref_option_traits.all.stderr b/src/tools/clippy/tests/ui-toml/ref_option/ref_option_traits.all.stderr
index 886bf2b0349..602e148be60 100644
--- a/src/tools/clippy/tests/ui/ref_option/ref_option_traits.all.stderr
+++ b/src/tools/clippy/tests/ui-toml/ref_option/ref_option_traits.all.stderr
@@ -1,5 +1,5 @@
 error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
-  --> tests/ui/ref_option/ref_option_traits.rs:9:5
+  --> tests/ui-toml/ref_option/ref_option_traits.rs:10:5
    |
 LL |     fn pub_trait_opt(&self, a: &Option<Vec<u8>>);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------^^
@@ -10,7 +10,7 @@ LL |     fn pub_trait_opt(&self, a: &Option<Vec<u8>>);
    = help: to override `-D warnings` add `#[allow(clippy::ref_option)]`
 
 error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
-  --> tests/ui/ref_option/ref_option_traits.rs:11:5
+  --> tests/ui-toml/ref_option/ref_option_traits.rs:12:5
    |
 LL |     fn pub_trait_ret(&self) -> &Option<Vec<u8>>;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^----------------^
@@ -18,7 +18,7 @@ LL |     fn pub_trait_ret(&self) -> &Option<Vec<u8>>;
    |                                help: change this to: `Option<&Vec<u8>>`
 
 error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
-  --> tests/ui/ref_option/ref_option_traits.rs:16:5
+  --> tests/ui-toml/ref_option/ref_option_traits.rs:17:5
    |
 LL |     fn trait_opt(&self, a: &Option<String>);
    |     ^^^^^^^^^^^^^^^^^^^^^^^---------------^^
@@ -26,7 +26,7 @@ LL |     fn trait_opt(&self, a: &Option<String>);
    |                            help: change this to: `Option<&String>`
 
 error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
-  --> tests/ui/ref_option/ref_option_traits.rs:18:5
+  --> tests/ui-toml/ref_option/ref_option_traits.rs:19:5
    |
 LL |     fn trait_ret(&self) -> &Option<String>;
    |     ^^^^^^^^^^^^^^^^^^^^^^^---------------^
diff --git a/src/tools/clippy/tests/ui/ref_option/ref_option_traits.private.stderr b/src/tools/clippy/tests/ui-toml/ref_option/ref_option_traits.private.stderr
index cfab7fa5734..20bea400edf 100644
--- a/src/tools/clippy/tests/ui/ref_option/ref_option_traits.private.stderr
+++ b/src/tools/clippy/tests/ui-toml/ref_option/ref_option_traits.private.stderr
@@ -1,5 +1,5 @@
 error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
-  --> tests/ui/ref_option/ref_option_traits.rs:16:5
+  --> tests/ui-toml/ref_option/ref_option_traits.rs:17:5
    |
 LL |     fn trait_opt(&self, a: &Option<String>);
    |     ^^^^^^^^^^^^^^^^^^^^^^^---------------^^
@@ -10,7 +10,7 @@ LL |     fn trait_opt(&self, a: &Option<String>);
    = help: to override `-D warnings` add `#[allow(clippy::ref_option)]`
 
 error: it is more idiomatic to use `Option<&T>` instead of `&Option<T>`
-  --> tests/ui/ref_option/ref_option_traits.rs:18:5
+  --> tests/ui-toml/ref_option/ref_option_traits.rs:19:5
    |
 LL |     fn trait_ret(&self) -> &Option<String>;
    |     ^^^^^^^^^^^^^^^^^^^^^^^---------------^
diff --git a/src/tools/clippy/tests/ui-toml/ref_option/ref_option_traits.rs b/src/tools/clippy/tests/ui-toml/ref_option/ref_option_traits.rs
new file mode 100644
index 00000000000..e1477d7f846
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/ref_option/ref_option_traits.rs
@@ -0,0 +1,71 @@
+//@no-rustfix: fixes are only done to traits, not the impls
+//@aux-build:../../ui/auxiliary/proc_macros.rs
+//@revisions: private all
+//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/private
+//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/ref_option/all
+
+#![warn(clippy::ref_option)]
+
+pub trait PubTrait {
+    fn pub_trait_opt(&self, a: &Option<Vec<u8>>);
+    //~[all]^ ref_option
+    fn pub_trait_ret(&self) -> &Option<Vec<u8>>;
+    //~[all]^ ref_option
+}
+
+trait PrivateTrait {
+    fn trait_opt(&self, a: &Option<String>);
+    //~^ ref_option
+    fn trait_ret(&self) -> &Option<String>;
+    //~^ ref_option
+}
+
+pub struct PubStruct;
+
+impl PubTrait for PubStruct {
+    fn pub_trait_opt(&self, a: &Option<Vec<u8>>) {}
+    fn pub_trait_ret(&self) -> &Option<Vec<u8>> {
+        panic!()
+    }
+}
+
+struct PrivateStruct;
+
+impl PrivateTrait for PrivateStruct {
+    fn trait_opt(&self, a: &Option<String>) {}
+    fn trait_ret(&self) -> &Option<String> {
+        panic!()
+    }
+}
+
+pub mod external {
+    proc_macros::external!(
+        pub trait PubTrait {
+            fn pub_trait_opt(&self, a: &Option<Vec<u8>>);
+            fn pub_trait_ret(&self) -> &Option<Vec<u8>>;
+        }
+
+        trait PrivateTrait {
+            fn trait_opt(&self, a: &Option<String>);
+            fn trait_ret(&self) -> &Option<String>;
+        }
+    );
+}
+
+pub mod proc_macros {
+    proc_macros::with_span!(
+        span
+
+        pub trait PubTrait {
+            fn pub_trait_opt(&self, a: &Option<Vec<u8>>);
+            fn pub_trait_ret(&self) -> &Option<Vec<u8>>;
+        }
+
+        trait PrivateTrait {
+            fn trait_opt(&self, a: &Option<String>);
+            fn trait_ret(&self) -> &Option<String>;
+        }
+    );
+}
+
+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 6ee77ebd8ec..20aeb4bb849 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
@@ -35,6 +35,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect
            check-inconsistent-struct-field-initializers
            check-private-items
            cognitive-complexity-threshold
+           const-literal-digits-threshold
            disallowed-macros
            disallowed-methods
            disallowed-names
@@ -129,6 +130,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect
            check-inconsistent-struct-field-initializers
            check-private-items
            cognitive-complexity-threshold
+           const-literal-digits-threshold
            disallowed-macros
            disallowed-methods
            disallowed-names
@@ -223,6 +225,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni
            check-inconsistent-struct-field-initializers
            check-private-items
            cognitive-complexity-threshold
+           const-literal-digits-threshold
            disallowed-macros
            disallowed-methods
            disallowed-names
diff --git a/src/tools/clippy/tests/ui/as_ptr_cast_mut.fixed b/src/tools/clippy/tests/ui/as_ptr_cast_mut.fixed
new file mode 100644
index 00000000000..fe9c5dca5ba
--- /dev/null
+++ b/src/tools/clippy/tests/ui/as_ptr_cast_mut.fixed
@@ -0,0 +1,38 @@
+#![allow(unused)]
+#![warn(clippy::as_ptr_cast_mut)]
+#![allow(clippy::wrong_self_convention, clippy::unnecessary_cast)]
+
+struct MutPtrWrapper(Vec<u8>);
+impl MutPtrWrapper {
+    fn as_ptr(&mut self) -> *const u8 {
+        self.0.as_mut_ptr() as *const u8
+    }
+}
+
+struct Covariant<T>(*const T);
+impl<T> Covariant<T> {
+    fn as_ptr(self) -> *const T {
+        self.0
+    }
+}
+
+fn main() {
+    let mut string = String::new();
+    let _ = string.as_mut_ptr();
+    //~^ as_ptr_cast_mut
+
+    let _ = string.as_ptr() as *const i8;
+    let _ = string.as_mut_ptr();
+    let _ = string.as_mut_ptr() as *mut u8;
+    let _ = string.as_mut_ptr() as *const u8;
+
+    let nn = std::ptr::NonNull::new(4 as *mut u8).unwrap();
+    let _ = nn.as_ptr() as *mut u8;
+
+    let mut wrap = MutPtrWrapper(Vec::new());
+    let _ = wrap.as_ptr() as *mut u8;
+
+    let mut local = 4;
+    let ref_with_write_perm = Covariant(std::ptr::addr_of_mut!(local) as *const _);
+    let _ = ref_with_write_perm.as_ptr() as *mut u8;
+}
diff --git a/src/tools/clippy/tests/ui/as_ptr_cast_mut.rs b/src/tools/clippy/tests/ui/as_ptr_cast_mut.rs
index baf7279adc4..3f22c2058d0 100644
--- a/src/tools/clippy/tests/ui/as_ptr_cast_mut.rs
+++ b/src/tools/clippy/tests/ui/as_ptr_cast_mut.rs
@@ -1,7 +1,6 @@
 #![allow(unused)]
 #![warn(clippy::as_ptr_cast_mut)]
 #![allow(clippy::wrong_self_convention, clippy::unnecessary_cast)]
-//@no-rustfix: incorrect suggestion
 
 struct MutPtrWrapper(Vec<u8>);
 impl MutPtrWrapper {
@@ -22,9 +21,6 @@ fn main() {
     let _ = string.as_ptr() as *mut u8;
     //~^ as_ptr_cast_mut
 
-    let _: *mut i8 = string.as_ptr() as *mut _;
-    //~^ as_ptr_cast_mut
-
     let _ = string.as_ptr() as *const i8;
     let _ = string.as_mut_ptr();
     let _ = string.as_mut_ptr() as *mut u8;
diff --git a/src/tools/clippy/tests/ui/as_ptr_cast_mut.stderr b/src/tools/clippy/tests/ui/as_ptr_cast_mut.stderr
index b3fc223ccdb..fa9fb23e2d0 100644
--- a/src/tools/clippy/tests/ui/as_ptr_cast_mut.stderr
+++ b/src/tools/clippy/tests/ui/as_ptr_cast_mut.stderr
@@ -1,5 +1,5 @@
 error: casting the result of `as_ptr` to *mut u8
-  --> tests/ui/as_ptr_cast_mut.rs:22:13
+  --> tests/ui/as_ptr_cast_mut.rs:21:13
    |
 LL |     let _ = string.as_ptr() as *mut u8;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `string.as_mut_ptr()`
@@ -7,11 +7,5 @@ LL |     let _ = string.as_ptr() as *mut u8;
    = note: `-D clippy::as-ptr-cast-mut` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::as_ptr_cast_mut)]`
 
-error: casting the result of `as_ptr` to *mut i8
-  --> tests/ui/as_ptr_cast_mut.rs:25:22
-   |
-LL |     let _: *mut i8 = string.as_ptr() as *mut _;
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `string.as_mut_ptr()`
-
-error: aborting due to 2 previous errors
+error: aborting due to 1 previous error
 
diff --git a/src/tools/clippy/tests/ui/as_ptr_cast_mut_unfixable.rs b/src/tools/clippy/tests/ui/as_ptr_cast_mut_unfixable.rs
new file mode 100644
index 00000000000..a8f6b06bd4f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/as_ptr_cast_mut_unfixable.rs
@@ -0,0 +1,16 @@
+//@no-rustfix
+#![allow(unused)]
+#![warn(clippy::as_ptr_cast_mut)]
+
+fn main() {
+    let mut string = String::new();
+
+    // the `*mut _` is actually necessary since it does two things at once:
+    // - changes the mutability (caught by the lint)
+    // - changes the type
+    //
+    // and so replacing this with `as_mut_ptr` removes the second thing,
+    // resulting in a type mismatch
+    let _: *mut i8 = string.as_ptr() as *mut _;
+    //~^ as_ptr_cast_mut
+}
diff --git a/src/tools/clippy/tests/ui/as_ptr_cast_mut_unfixable.stderr b/src/tools/clippy/tests/ui/as_ptr_cast_mut_unfixable.stderr
new file mode 100644
index 00000000000..c5bcad6f4df
--- /dev/null
+++ b/src/tools/clippy/tests/ui/as_ptr_cast_mut_unfixable.stderr
@@ -0,0 +1,11 @@
+error: casting the result of `as_ptr` to *mut i8
+  --> tests/ui/as_ptr_cast_mut_unfixable.rs:14:22
+   |
+LL |     let _: *mut i8 = string.as_ptr() as *mut _;
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `string.as_mut_ptr()`
+   |
+   = note: `-D clippy::as-ptr-cast-mut` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::as_ptr_cast_mut)]`
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/clippy/tests/ui/as_underscore_unfixable.rs b/src/tools/clippy/tests/ui/as_underscore_unfixable.rs
new file mode 100644
index 00000000000..854feca7174
--- /dev/null
+++ b/src/tools/clippy/tests/ui/as_underscore_unfixable.rs
@@ -0,0 +1,14 @@
+//@no-rustfix
+
+#![warn(clippy::as_underscore)]
+
+fn main() {
+    // From issue #15282
+    let f = async || ();
+    let _: Box<dyn FnOnce() -> _> = Box::new(f) as _;
+    //~^ as_underscore
+
+    let barr = || (|| ());
+    let _: Box<dyn Fn() -> _> = Box::new(barr) as _;
+    //~^ as_underscore
+}
diff --git a/src/tools/clippy/tests/ui/as_underscore_unfixable.stderr b/src/tools/clippy/tests/ui/as_underscore_unfixable.stderr
new file mode 100644
index 00000000000..7385bea5c65
--- /dev/null
+++ b/src/tools/clippy/tests/ui/as_underscore_unfixable.stderr
@@ -0,0 +1,20 @@
+error: using `as _` conversion
+  --> tests/ui/as_underscore_unfixable.rs:8:37
+   |
+LL |     let _: Box<dyn FnOnce() -> _> = Box::new(f) as _;
+   |                                     ^^^^^^^^^^^^^^^^
+   |
+   = help: consider giving the type explicitly
+   = note: `-D clippy::as-underscore` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::as_underscore)]`
+
+error: using `as _` conversion
+  --> tests/ui/as_underscore_unfixable.rs:12:33
+   |
+LL |     let _: Box<dyn Fn() -> _> = Box::new(barr) as _;
+   |                                 ^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider giving the type explicitly
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/assertions_on_result_states.fixed b/src/tools/clippy/tests/ui/assertions_on_result_states.fixed
index 09c1a8d0ed1..b2b7318c113 100644
--- a/src/tools/clippy/tests/ui/assertions_on_result_states.fixed
+++ b/src/tools/clippy/tests/ui/assertions_on_result_states.fixed
@@ -82,9 +82,18 @@ fn main() {
     assert!(r.is_err());
 }
 
-#[allow(dead_code)]
 fn issue9450() {
     let res: Result<i32, i32> = Ok(1);
     res.unwrap_err();
     //~^ assertions_on_result_states
 }
+
+fn issue9916(res: Result<u32, u32>) {
+    let a = 0;
+    match a {
+        0 => {},
+        1 => { res.unwrap(); },
+        //~^ assertions_on_result_states
+        _ => todo!(),
+    }
+}
diff --git a/src/tools/clippy/tests/ui/assertions_on_result_states.rs b/src/tools/clippy/tests/ui/assertions_on_result_states.rs
index c63c2502b53..33f1485326b 100644
--- a/src/tools/clippy/tests/ui/assertions_on_result_states.rs
+++ b/src/tools/clippy/tests/ui/assertions_on_result_states.rs
@@ -82,9 +82,18 @@ fn main() {
     assert!(r.is_err());
 }
 
-#[allow(dead_code)]
 fn issue9450() {
     let res: Result<i32, i32> = Ok(1);
     assert!(res.is_err())
     //~^ assertions_on_result_states
 }
+
+fn issue9916(res: Result<u32, u32>) {
+    let a = 0;
+    match a {
+        0 => {},
+        1 => assert!(res.is_ok()),
+        //~^ assertions_on_result_states
+        _ => todo!(),
+    }
+}
diff --git a/src/tools/clippy/tests/ui/assertions_on_result_states.stderr b/src/tools/clippy/tests/ui/assertions_on_result_states.stderr
index 3bf811588c6..826f049555c 100644
--- a/src/tools/clippy/tests/ui/assertions_on_result_states.stderr
+++ b/src/tools/clippy/tests/ui/assertions_on_result_states.stderr
@@ -38,10 +38,16 @@ LL |     assert!(r.is_err());
    |     ^^^^^^^^^^^^^^^^^^^ help: replace with: `r.unwrap_err()`
 
 error: called `assert!` with `Result::is_err`
-  --> tests/ui/assertions_on_result_states.rs:88:5
+  --> tests/ui/assertions_on_result_states.rs:87:5
    |
 LL |     assert!(res.is_err())
    |     ^^^^^^^^^^^^^^^^^^^^^ help: replace with: `res.unwrap_err();`
 
-error: aborting due to 7 previous errors
+error: called `assert!` with `Result::is_ok`
+  --> tests/ui/assertions_on_result_states.rs:95:14
+   |
+LL |         1 => assert!(res.is_ok()),
+   |              ^^^^^^^^^^^^^^^^^^^^ help: replace with: `{ res.unwrap(); }`
+
+error: aborting due to 8 previous errors
 
diff --git a/src/tools/clippy/tests/ui/assign_ops.fixed b/src/tools/clippy/tests/ui/assign_ops.fixed
index 3754b9dfe74..2046d089d6a 100644
--- a/src/tools/clippy/tests/ui/assign_ops.fixed
+++ b/src/tools/clippy/tests/ui/assign_ops.fixed
@@ -1,8 +1,9 @@
 #![allow(clippy::useless_vec)]
 #![warn(clippy::assign_op_pattern)]
-#![feature(const_trait_impl, const_ops)]
+#![feature(const_ops)]
+#![feature(const_trait_impl)]
 
-use core::num::Wrapping;
+use std::num::Wrapping;
 use std::ops::{Mul, MulAssign};
 
 fn main() {
@@ -82,6 +83,18 @@ mod issue14871 {
     pub trait Number: Copy + Add<Self, Output = Self> + AddAssign {
         const ZERO: Self;
         const ONE: Self;
+
+        fn non_constant(value: usize) -> Self {
+            let mut res = Self::ZERO;
+            let mut count = 0;
+            while count < value {
+                res += Self::ONE;
+                //~^ assign_op_pattern
+                count += 1;
+                //~^ assign_op_pattern
+            }
+            res
+        }
     }
 
     #[rustfmt::skip] // rustfmt doesn't understand the order of pub const on traits (yet)
@@ -91,7 +104,7 @@ mod issue14871 {
 
     impl<T> const NumberConstants for T
     where
-        T: Number + [const] core::ops::Add,
+        T: Number + [const] std::ops::Add,
     {
         fn constant(value: usize) -> Self {
             let mut res = Self::ZERO;
@@ -99,8 +112,28 @@ mod issue14871 {
             while count < value {
                 res = res + Self::ONE;
                 count += 1;
+                //~^ assign_op_pattern
             }
             res
         }
     }
+
+    pub struct S;
+
+    impl const std::ops::Add for S {
+        type Output = S;
+        fn add(self, _rhs: S) -> S {
+            S
+        }
+    }
+
+    impl const std::ops::AddAssign for S {
+        fn add_assign(&mut self, rhs: S) {}
+    }
+
+    const fn do_add() {
+        let mut s = S;
+        s += S;
+        //~^ assign_op_pattern
+    }
 }
diff --git a/src/tools/clippy/tests/ui/assign_ops.rs b/src/tools/clippy/tests/ui/assign_ops.rs
index 0b878d4f490..f83a40f5547 100644
--- a/src/tools/clippy/tests/ui/assign_ops.rs
+++ b/src/tools/clippy/tests/ui/assign_ops.rs
@@ -1,8 +1,9 @@
 #![allow(clippy::useless_vec)]
 #![warn(clippy::assign_op_pattern)]
-#![feature(const_trait_impl, const_ops)]
+#![feature(const_ops)]
+#![feature(const_trait_impl)]
 
-use core::num::Wrapping;
+use std::num::Wrapping;
 use std::ops::{Mul, MulAssign};
 
 fn main() {
@@ -82,6 +83,18 @@ mod issue14871 {
     pub trait Number: Copy + Add<Self, Output = Self> + AddAssign {
         const ZERO: Self;
         const ONE: Self;
+
+        fn non_constant(value: usize) -> Self {
+            let mut res = Self::ZERO;
+            let mut count = 0;
+            while count < value {
+                res = res + Self::ONE;
+                //~^ assign_op_pattern
+                count = count + 1;
+                //~^ assign_op_pattern
+            }
+            res
+        }
     }
 
     #[rustfmt::skip] // rustfmt doesn't understand the order of pub const on traits (yet)
@@ -91,16 +104,36 @@ mod issue14871 {
 
     impl<T> const NumberConstants for T
     where
-        T: Number + [const] core::ops::Add,
+        T: Number + [const] std::ops::Add,
     {
         fn constant(value: usize) -> Self {
             let mut res = Self::ZERO;
             let mut count = 0;
             while count < value {
                 res = res + Self::ONE;
-                count += 1;
+                count = count + 1;
+                //~^ assign_op_pattern
             }
             res
         }
     }
+
+    pub struct S;
+
+    impl const std::ops::Add for S {
+        type Output = S;
+        fn add(self, _rhs: S) -> S {
+            S
+        }
+    }
+
+    impl const std::ops::AddAssign for S {
+        fn add_assign(&mut self, rhs: S) {}
+    }
+
+    const fn do_add() {
+        let mut s = S;
+        s = s + S;
+        //~^ assign_op_pattern
+    }
 }
diff --git a/src/tools/clippy/tests/ui/assign_ops.stderr b/src/tools/clippy/tests/ui/assign_ops.stderr
index c5e698b3ee1..a4fca04893c 100644
--- a/src/tools/clippy/tests/ui/assign_ops.stderr
+++ b/src/tools/clippy/tests/ui/assign_ops.stderr
@@ -1,5 +1,5 @@
 error: manual implementation of an assign operation
-  --> tests/ui/assign_ops.rs:10:5
+  --> tests/ui/assign_ops.rs:11:5
    |
 LL |     a = a + 1;
    |     ^^^^^^^^^ help: replace it with: `a += 1`
@@ -8,70 +8,94 @@ LL |     a = a + 1;
    = help: to override `-D warnings` add `#[allow(clippy::assign_op_pattern)]`
 
 error: manual implementation of an assign operation
-  --> tests/ui/assign_ops.rs:12:5
+  --> tests/ui/assign_ops.rs:13:5
    |
 LL |     a = 1 + a;
    |     ^^^^^^^^^ help: replace it with: `a += 1`
 
 error: manual implementation of an assign operation
-  --> tests/ui/assign_ops.rs:14:5
+  --> tests/ui/assign_ops.rs:15:5
    |
 LL |     a = a - 1;
    |     ^^^^^^^^^ help: replace it with: `a -= 1`
 
 error: manual implementation of an assign operation
-  --> tests/ui/assign_ops.rs:16:5
+  --> tests/ui/assign_ops.rs:17:5
    |
 LL |     a = a * 99;
    |     ^^^^^^^^^^ help: replace it with: `a *= 99`
 
 error: manual implementation of an assign operation
-  --> tests/ui/assign_ops.rs:18:5
+  --> tests/ui/assign_ops.rs:19:5
    |
 LL |     a = 42 * a;
    |     ^^^^^^^^^^ help: replace it with: `a *= 42`
 
 error: manual implementation of an assign operation
-  --> tests/ui/assign_ops.rs:20:5
+  --> tests/ui/assign_ops.rs:21:5
    |
 LL |     a = a / 2;
    |     ^^^^^^^^^ help: replace it with: `a /= 2`
 
 error: manual implementation of an assign operation
-  --> tests/ui/assign_ops.rs:22:5
+  --> tests/ui/assign_ops.rs:23:5
    |
 LL |     a = a % 5;
    |     ^^^^^^^^^ help: replace it with: `a %= 5`
 
 error: manual implementation of an assign operation
-  --> tests/ui/assign_ops.rs:24:5
+  --> tests/ui/assign_ops.rs:25:5
    |
 LL |     a = a & 1;
    |     ^^^^^^^^^ help: replace it with: `a &= 1`
 
 error: manual implementation of an assign operation
-  --> tests/ui/assign_ops.rs:31:5
+  --> tests/ui/assign_ops.rs:32:5
    |
 LL |     s = s + "bla";
    |     ^^^^^^^^^^^^^ help: replace it with: `s += "bla"`
 
 error: manual implementation of an assign operation
-  --> tests/ui/assign_ops.rs:36:5
+  --> tests/ui/assign_ops.rs:37:5
    |
 LL |     a = a + Wrapping(1u32);
    |     ^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `a += Wrapping(1u32)`
 
 error: manual implementation of an assign operation
-  --> tests/ui/assign_ops.rs:39:5
+  --> tests/ui/assign_ops.rs:40:5
    |
 LL |     v[0] = v[0] + v[1];
    |     ^^^^^^^^^^^^^^^^^^ help: replace it with: `v[0] += v[1]`
 
 error: manual implementation of an assign operation
-  --> tests/ui/assign_ops.rs:52:5
+  --> tests/ui/assign_ops.rs:53:5
    |
 LL |     buf = buf + cows.clone();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `buf += cows.clone()`
 
-error: aborting due to 12 previous errors
+error: manual implementation of an assign operation
+  --> tests/ui/assign_ops.rs:91:17
+   |
+LL |                 res = res + Self::ONE;
+   |                 ^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `res += Self::ONE`
+
+error: manual implementation of an assign operation
+  --> tests/ui/assign_ops.rs:93:17
+   |
+LL |                 count = count + 1;
+   |                 ^^^^^^^^^^^^^^^^^ help: replace it with: `count += 1`
+
+error: manual implementation of an assign operation
+  --> tests/ui/assign_ops.rs:114:17
+   |
+LL |                 count = count + 1;
+   |                 ^^^^^^^^^^^^^^^^^ help: replace it with: `count += 1`
+
+error: manual implementation of an assign operation
+  --> tests/ui/assign_ops.rs:136:9
+   |
+LL |         s = s + S;
+   |         ^^^^^^^^^ help: replace it with: `s += S`
+
+error: aborting due to 16 previous errors
 
diff --git a/src/tools/clippy/tests/ui/async_yields_async.fixed b/src/tools/clippy/tests/ui/async_yields_async.fixed
index 93c573d3086..bd1b31f74ee 100644
--- a/src/tools/clippy/tests/ui/async_yields_async.fixed
+++ b/src/tools/clippy/tests/ui/async_yields_async.fixed
@@ -80,3 +80,42 @@ fn check_expect_suppression() {
         }
     };
 }
+
+#[allow(clippy::let_underscore_future)]
+fn issue15552() {
+    async fn bar(i: i32) {}
+
+    macro_rules! call_bar {
+        () => {
+            async { bar(5).await }
+        };
+        ($e:expr) => {
+            bar($e)
+        };
+    }
+    let x = async { call_bar!(5).await };
+    //~^ async_yields_async
+    let y = async { call_bar!().await };
+    //~^ async_yields_async
+    //~| async_yields_async
+
+    use std::future::{Future, Ready};
+    use std::ops::Add;
+    use std::pin::Pin;
+    use std::task::{Context, Poll};
+    struct CustomFutureType;
+    impl Add for CustomFutureType {
+        type Output = Self;
+        fn add(self, other: Self) -> Self {
+            self
+        }
+    }
+    impl Future for CustomFutureType {
+        type Output = ();
+        fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
+            Poll::Ready(())
+        }
+    }
+    let _ = async { (CustomFutureType + CustomFutureType).await };
+    //~^ async_yields_async
+}
diff --git a/src/tools/clippy/tests/ui/async_yields_async.rs b/src/tools/clippy/tests/ui/async_yields_async.rs
index 166d522e1c0..605d2734157 100644
--- a/src/tools/clippy/tests/ui/async_yields_async.rs
+++ b/src/tools/clippy/tests/ui/async_yields_async.rs
@@ -80,3 +80,42 @@ fn check_expect_suppression() {
         }
     };
 }
+
+#[allow(clippy::let_underscore_future)]
+fn issue15552() {
+    async fn bar(i: i32) {}
+
+    macro_rules! call_bar {
+        () => {
+            async { bar(5) }
+        };
+        ($e:expr) => {
+            bar($e)
+        };
+    }
+    let x = async { call_bar!(5) };
+    //~^ async_yields_async
+    let y = async { call_bar!() };
+    //~^ async_yields_async
+    //~| async_yields_async
+
+    use std::future::{Future, Ready};
+    use std::ops::Add;
+    use std::pin::Pin;
+    use std::task::{Context, Poll};
+    struct CustomFutureType;
+    impl Add for CustomFutureType {
+        type Output = Self;
+        fn add(self, other: Self) -> Self {
+            self
+        }
+    }
+    impl Future for CustomFutureType {
+        type Output = ();
+        fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> {
+            Poll::Ready(())
+        }
+    }
+    let _ = async { CustomFutureType + CustomFutureType };
+    //~^ async_yields_async
+}
diff --git a/src/tools/clippy/tests/ui/async_yields_async.stderr b/src/tools/clippy/tests/ui/async_yields_async.stderr
index 17a35076fa3..3041cf65789 100644
--- a/src/tools/clippy/tests/ui/async_yields_async.stderr
+++ b/src/tools/clippy/tests/ui/async_yields_async.stderr
@@ -89,5 +89,50 @@ LL | |         CustomFutureType
 LL | |     };
    | |_____- outer async construct
 
-error: aborting due to 6 previous errors
+error: an async construct yields a type which is itself awaitable
+  --> tests/ui/async_yields_async.rs:96:21
+   |
+LL |     let x = async { call_bar!(5) };
+   |                   --^^^^^^^^^^^^--
+   |                   | |
+   |                   | awaitable value not awaited
+   |                   | help: consider awaiting this value: `call_bar!(5).await`
+   |                   outer async construct
+
+error: an async construct yields a type which is itself awaitable
+  --> tests/ui/async_yields_async.rs:98:21
+   |
+LL |     let y = async { call_bar!() };
+   |                   --^^^^^^^^^^^--
+   |                   | |
+   |                   | awaitable value not awaited
+   |                   | help: consider awaiting this value: `call_bar!().await`
+   |                   outer async construct
+
+error: an async construct yields a type which is itself awaitable
+  --> tests/ui/async_yields_async.rs:90:21
+   |
+LL |             async { bar(5) }
+   |                   --^^^^^^--
+   |                   | |
+   |                   | awaitable value not awaited
+   |                   | help: consider awaiting this value: `bar(5).await`
+   |                   outer async construct
+...
+LL |     let y = async { call_bar!() };
+   |                     ----------- in this macro invocation
+   |
+   = note: this error originates in the macro `call_bar` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: an async construct yields a type which is itself awaitable
+  --> tests/ui/async_yields_async.rs:119:21
+   |
+LL |     let _ = async { CustomFutureType + CustomFutureType };
+   |                   --^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^--
+   |                   | |
+   |                   | awaitable value not awaited
+   |                   | help: consider awaiting this value: `(CustomFutureType + CustomFutureType).await`
+   |                   outer async construct
+
+error: aborting due to 10 previous errors
 
diff --git a/src/tools/clippy/tests/ui/bool_assert_comparison.fixed b/src/tools/clippy/tests/ui/bool_assert_comparison.fixed
index 721d8b2c2dc..ec76abbef05 100644
--- a/src/tools/clippy/tests/ui/bool_assert_comparison.fixed
+++ b/src/tools/clippy/tests/ui/bool_assert_comparison.fixed
@@ -1,7 +1,7 @@
 #![allow(unused, clippy::assertions_on_constants, clippy::const_is_empty)]
 #![warn(clippy::bool_assert_comparison)]
 
-use std::ops::Not;
+use std::ops::{Add, Not};
 
 macro_rules! a {
     () => {
@@ -62,6 +62,14 @@ impl Not for ImplNotTraitWithBool {
     }
 }
 
+impl Add for ImplNotTraitWithBool {
+    type Output = Self;
+
+    fn add(self, other: Self) -> Self::Output {
+        self
+    }
+}
+
 #[derive(Debug)]
 struct NonCopy;
 
@@ -94,7 +102,7 @@ fn main() {
     assert_eq!(a!(), "".is_empty());
     assert_eq!("".is_empty(), b!());
     assert_eq!(a, true);
-    assert!(b);
+    assert!(!!b);
     //~^ bool_assert_comparison
 
     assert_ne!("a".len(), 1);
@@ -122,7 +130,7 @@ fn main() {
     debug_assert_eq!(a!(), "".is_empty());
     debug_assert_eq!("".is_empty(), b!());
     debug_assert_eq!(a, true);
-    debug_assert!(b);
+    debug_assert!(!!b);
     //~^ bool_assert_comparison
 
     debug_assert_ne!("a".len(), 1);
@@ -167,7 +175,7 @@ fn main() {
 
     use debug_assert_eq as renamed;
     renamed!(a, true);
-    debug_assert!(b);
+    debug_assert!(!!b);
     //~^ bool_assert_comparison
 
     let non_copy = NonCopy;
@@ -199,4 +207,12 @@ fn main() {
     //~^ bool_assert_comparison
     debug_assert!(!"requires negation".is_empty());
     //~^ bool_assert_comparison
+    assert!(!b);
+    //~^ bool_assert_comparison
+    assert!(!(!b));
+    //~^ bool_assert_comparison
+    assert!(!!(b + b));
+    //~^ bool_assert_comparison
+    assert!(!(b + b));
+    //~^ bool_assert_comparison
 }
diff --git a/src/tools/clippy/tests/ui/bool_assert_comparison.rs b/src/tools/clippy/tests/ui/bool_assert_comparison.rs
index 5ab4f475b06..40824a23c82 100644
--- a/src/tools/clippy/tests/ui/bool_assert_comparison.rs
+++ b/src/tools/clippy/tests/ui/bool_assert_comparison.rs
@@ -1,7 +1,7 @@
 #![allow(unused, clippy::assertions_on_constants, clippy::const_is_empty)]
 #![warn(clippy::bool_assert_comparison)]
 
-use std::ops::Not;
+use std::ops::{Add, Not};
 
 macro_rules! a {
     () => {
@@ -62,6 +62,14 @@ impl Not for ImplNotTraitWithBool {
     }
 }
 
+impl Add for ImplNotTraitWithBool {
+    type Output = Self;
+
+    fn add(self, other: Self) -> Self::Output {
+        self
+    }
+}
+
 #[derive(Debug)]
 struct NonCopy;
 
@@ -199,4 +207,12 @@ fn main() {
     //~^ bool_assert_comparison
     debug_assert_eq!("requires negation".is_empty(), false);
     //~^ bool_assert_comparison
+    assert_eq!(!b, true);
+    //~^ bool_assert_comparison
+    assert_eq!(!b, false);
+    //~^ bool_assert_comparison
+    assert_eq!(b + b, true);
+    //~^ bool_assert_comparison
+    assert_eq!(b + b, false);
+    //~^ bool_assert_comparison
 }
diff --git a/src/tools/clippy/tests/ui/bool_assert_comparison.stderr b/src/tools/clippy/tests/ui/bool_assert_comparison.stderr
index a1d0af54361..72aa6303a20 100644
--- a/src/tools/clippy/tests/ui/bool_assert_comparison.stderr
+++ b/src/tools/clippy/tests/ui/bool_assert_comparison.stderr
@@ -1,5 +1,5 @@
 error: used `assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:87:5
+  --> tests/ui/bool_assert_comparison.rs:95:5
    |
 LL |     assert_eq!("a".is_empty(), false);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -13,7 +13,7 @@ LL +     assert!(!"a".is_empty());
    |
 
 error: used `assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:89:5
+  --> tests/ui/bool_assert_comparison.rs:97:5
    |
 LL |     assert_eq!("".is_empty(), true);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -25,7 +25,7 @@ LL +     assert!("".is_empty());
    |
 
 error: used `assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:91:5
+  --> tests/ui/bool_assert_comparison.rs:99:5
    |
 LL |     assert_eq!(true, "".is_empty());
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -37,7 +37,7 @@ LL +     assert!("".is_empty());
    |
 
 error: used `assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:97:5
+  --> tests/ui/bool_assert_comparison.rs:105:5
    |
 LL |     assert_eq!(b, true);
    |     ^^^^^^^^^^^^^^^^^^^
@@ -45,11 +45,11 @@ LL |     assert_eq!(b, true);
 help: replace it with `assert!(..)`
    |
 LL -     assert_eq!(b, true);
-LL +     assert!(b);
+LL +     assert!(!!b);
    |
 
 error: used `assert_ne!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:101:5
+  --> tests/ui/bool_assert_comparison.rs:109:5
    |
 LL |     assert_ne!("a".is_empty(), false);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -61,7 +61,7 @@ LL +     assert!("a".is_empty());
    |
 
 error: used `assert_ne!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:103:5
+  --> tests/ui/bool_assert_comparison.rs:111:5
    |
 LL |     assert_ne!("".is_empty(), true);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -73,7 +73,7 @@ LL +     assert!(!"".is_empty());
    |
 
 error: used `assert_ne!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:105:5
+  --> tests/ui/bool_assert_comparison.rs:113:5
    |
 LL |     assert_ne!(true, "".is_empty());
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -85,7 +85,7 @@ LL +     assert!(!"".is_empty());
    |
 
 error: used `assert_ne!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:111:5
+  --> tests/ui/bool_assert_comparison.rs:119:5
    |
 LL |     assert_ne!(b, true);
    |     ^^^^^^^^^^^^^^^^^^^
@@ -97,7 +97,7 @@ LL +     assert!(!b);
    |
 
 error: used `debug_assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:115:5
+  --> tests/ui/bool_assert_comparison.rs:123:5
    |
 LL |     debug_assert_eq!("a".is_empty(), false);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -109,7 +109,7 @@ LL +     debug_assert!(!"a".is_empty());
    |
 
 error: used `debug_assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:117:5
+  --> tests/ui/bool_assert_comparison.rs:125:5
    |
 LL |     debug_assert_eq!("".is_empty(), true);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -121,7 +121,7 @@ LL +     debug_assert!("".is_empty());
    |
 
 error: used `debug_assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:119:5
+  --> tests/ui/bool_assert_comparison.rs:127:5
    |
 LL |     debug_assert_eq!(true, "".is_empty());
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -133,7 +133,7 @@ LL +     debug_assert!("".is_empty());
    |
 
 error: used `debug_assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:125:5
+  --> tests/ui/bool_assert_comparison.rs:133:5
    |
 LL |     debug_assert_eq!(b, true);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -141,11 +141,11 @@ LL |     debug_assert_eq!(b, true);
 help: replace it with `debug_assert!(..)`
    |
 LL -     debug_assert_eq!(b, true);
-LL +     debug_assert!(b);
+LL +     debug_assert!(!!b);
    |
 
 error: used `debug_assert_ne!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:129:5
+  --> tests/ui/bool_assert_comparison.rs:137:5
    |
 LL |     debug_assert_ne!("a".is_empty(), false);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -157,7 +157,7 @@ LL +     debug_assert!("a".is_empty());
    |
 
 error: used `debug_assert_ne!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:131:5
+  --> tests/ui/bool_assert_comparison.rs:139:5
    |
 LL |     debug_assert_ne!("".is_empty(), true);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -169,7 +169,7 @@ LL +     debug_assert!(!"".is_empty());
    |
 
 error: used `debug_assert_ne!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:133:5
+  --> tests/ui/bool_assert_comparison.rs:141:5
    |
 LL |     debug_assert_ne!(true, "".is_empty());
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -181,7 +181,7 @@ LL +     debug_assert!(!"".is_empty());
    |
 
 error: used `debug_assert_ne!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:139:5
+  --> tests/ui/bool_assert_comparison.rs:147:5
    |
 LL |     debug_assert_ne!(b, true);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -193,7 +193,7 @@ LL +     debug_assert!(!b);
    |
 
 error: used `assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:145:5
+  --> tests/ui/bool_assert_comparison.rs:153:5
    |
 LL |     assert_eq!("a".is_empty(), false, "tadam {}", 1);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -205,7 +205,7 @@ LL +     assert!(!"a".is_empty(), "tadam {}", 1);
    |
 
 error: used `assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:147:5
+  --> tests/ui/bool_assert_comparison.rs:155:5
    |
 LL |     assert_eq!("a".is_empty(), false, "tadam {}", true);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -217,7 +217,7 @@ LL +     assert!(!"a".is_empty(), "tadam {}", true);
    |
 
 error: used `assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:149:5
+  --> tests/ui/bool_assert_comparison.rs:157:5
    |
 LL |     assert_eq!(false, "a".is_empty(), "tadam {}", true);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -229,7 +229,7 @@ LL +     assert!(!"a".is_empty(), "tadam {}", true);
    |
 
 error: used `debug_assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:155:5
+  --> tests/ui/bool_assert_comparison.rs:163:5
    |
 LL |     debug_assert_eq!("a".is_empty(), false, "tadam {}", 1);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -241,7 +241,7 @@ LL +     debug_assert!(!"a".is_empty(), "tadam {}", 1);
    |
 
 error: used `debug_assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:157:5
+  --> tests/ui/bool_assert_comparison.rs:165:5
    |
 LL |     debug_assert_eq!("a".is_empty(), false, "tadam {}", true);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -253,7 +253,7 @@ LL +     debug_assert!(!"a".is_empty(), "tadam {}", true);
    |
 
 error: used `debug_assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:159:5
+  --> tests/ui/bool_assert_comparison.rs:167:5
    |
 LL |     debug_assert_eq!(false, "a".is_empty(), "tadam {}", true);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -265,7 +265,7 @@ LL +     debug_assert!(!"a".is_empty(), "tadam {}", true);
    |
 
 error: used `assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:163:5
+  --> tests/ui/bool_assert_comparison.rs:171:5
    |
 LL |     assert_eq!(a!(), true);
    |     ^^^^^^^^^^^^^^^^^^^^^^
@@ -277,7 +277,7 @@ LL +     assert!(a!());
    |
 
 error: used `assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:165:5
+  --> tests/ui/bool_assert_comparison.rs:173:5
    |
 LL |     assert_eq!(true, b!());
    |     ^^^^^^^^^^^^^^^^^^^^^^
@@ -289,7 +289,7 @@ LL +     assert!(b!());
    |
 
 error: used `debug_assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:170:5
+  --> tests/ui/bool_assert_comparison.rs:178:5
    |
 LL |     renamed!(b, true);
    |     ^^^^^^^^^^^^^^^^^
@@ -297,11 +297,11 @@ LL |     renamed!(b, true);
 help: replace it with `debug_assert!(..)`
    |
 LL -     renamed!(b, true);
-LL +     debug_assert!(b);
+LL +     debug_assert!(!!b);
    |
 
 error: used `assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:185:5
+  --> tests/ui/bool_assert_comparison.rs:193:5
    |
 LL |     assert_eq!("".is_empty(), true);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -313,7 +313,7 @@ LL +     assert!("".is_empty());
    |
 
 error: used `assert_ne!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:187:5
+  --> tests/ui/bool_assert_comparison.rs:195:5
    |
 LL |     assert_ne!("".is_empty(), false);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -325,7 +325,7 @@ LL +     assert!("".is_empty());
    |
 
 error: used `assert_ne!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:189:5
+  --> tests/ui/bool_assert_comparison.rs:197:5
    |
 LL |     assert_ne!("requires negation".is_empty(), true);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -337,7 +337,7 @@ LL +     assert!(!"requires negation".is_empty());
    |
 
 error: used `assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:191:5
+  --> tests/ui/bool_assert_comparison.rs:199:5
    |
 LL |     assert_eq!("requires negation".is_empty(), false);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -349,7 +349,7 @@ LL +     assert!(!"requires negation".is_empty());
    |
 
 error: used `debug_assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:194:5
+  --> tests/ui/bool_assert_comparison.rs:202:5
    |
 LL |     debug_assert_eq!("".is_empty(), true);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -361,7 +361,7 @@ LL +     debug_assert!("".is_empty());
    |
 
 error: used `debug_assert_ne!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:196:5
+  --> tests/ui/bool_assert_comparison.rs:204:5
    |
 LL |     debug_assert_ne!("".is_empty(), false);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -373,7 +373,7 @@ LL +     debug_assert!("".is_empty());
    |
 
 error: used `debug_assert_ne!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:198:5
+  --> tests/ui/bool_assert_comparison.rs:206:5
    |
 LL |     debug_assert_ne!("requires negation".is_empty(), true);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -385,7 +385,7 @@ LL +     debug_assert!(!"requires negation".is_empty());
    |
 
 error: used `debug_assert_eq!` with a literal bool
-  --> tests/ui/bool_assert_comparison.rs:200:5
+  --> tests/ui/bool_assert_comparison.rs:208:5
    |
 LL |     debug_assert_eq!("requires negation".is_empty(), false);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -396,5 +396,53 @@ LL -     debug_assert_eq!("requires negation".is_empty(), false);
 LL +     debug_assert!(!"requires negation".is_empty());
    |
 
-error: aborting due to 33 previous errors
+error: used `assert_eq!` with a literal bool
+  --> tests/ui/bool_assert_comparison.rs:210:5
+   |
+LL |     assert_eq!(!b, true);
+   |     ^^^^^^^^^^^^^^^^^^^^
+   |
+help: replace it with `assert!(..)`
+   |
+LL -     assert_eq!(!b, true);
+LL +     assert!(!b);
+   |
+
+error: used `assert_eq!` with a literal bool
+  --> tests/ui/bool_assert_comparison.rs:212:5
+   |
+LL |     assert_eq!(!b, false);
+   |     ^^^^^^^^^^^^^^^^^^^^^
+   |
+help: replace it with `assert!(..)`
+   |
+LL -     assert_eq!(!b, false);
+LL +     assert!(!(!b));
+   |
+
+error: used `assert_eq!` with a literal bool
+  --> tests/ui/bool_assert_comparison.rs:214:5
+   |
+LL |     assert_eq!(b + b, true);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: replace it with `assert!(..)`
+   |
+LL -     assert_eq!(b + b, true);
+LL +     assert!(!!(b + b));
+   |
+
+error: used `assert_eq!` with a literal bool
+  --> tests/ui/bool_assert_comparison.rs:216:5
+   |
+LL |     assert_eq!(b + b, false);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: replace it with `assert!(..)`
+   |
+LL -     assert_eq!(b + b, false);
+LL +     assert!(!(b + b));
+   |
+
+error: aborting due to 37 previous errors
 
diff --git a/src/tools/clippy/tests/ui/bool_comparison.fixed b/src/tools/clippy/tests/ui/bool_comparison.fixed
index 166abbe549c..b0b60104c0b 100644
--- a/src/tools/clippy/tests/ui/bool_comparison.fixed
+++ b/src/tools/clippy/tests/ui/bool_comparison.fixed
@@ -1,97 +1,41 @@
 #![allow(non_local_definitions, clippy::needless_if)]
 #![warn(clippy::bool_comparison)]
-#![allow(clippy::non_canonical_partial_ord_impl, clippy::nonminimal_bool)]
+#![allow(clippy::non_canonical_partial_ord_impl)]
 
 fn main() {
     let x = true;
-    if x {
-        //~^ bool_comparison
-        "yes"
-    } else {
-        "no"
-    };
-    if !x {
-        //~^ bool_comparison
-        "yes"
-    } else {
-        "no"
-    };
-    if x {
-        //~^ bool_comparison
-        "yes"
-    } else {
-        "no"
-    };
-    if !x {
-        //~^ bool_comparison
-        "yes"
-    } else {
-        "no"
-    };
-    if !x {
-        //~^ bool_comparison
-        "yes"
-    } else {
-        "no"
-    };
-    if x {
-        //~^ bool_comparison
-        "yes"
-    } else {
-        "no"
-    };
-    if !x {
-        //~^ bool_comparison
-        "yes"
-    } else {
-        "no"
-    };
-    if x {
-        //~^ bool_comparison
-        "yes"
-    } else {
-        "no"
-    };
-    if !x {
-        //~^ bool_comparison
-        "yes"
-    } else {
-        "no"
-    };
-    if x {
-        //~^ bool_comparison
-        "yes"
-    } else {
-        "no"
-    };
-    if x {
-        //~^ bool_comparison
-        "yes"
-    } else {
-        "no"
-    };
-    if !x {
-        //~^ bool_comparison
-        "yes"
-    } else {
-        "no"
-    };
+    let _ = if x { "yes" } else { "no" };
+    //~^ bool_comparison
+    let _ = if !x { "yes" } else { "no" };
+    //~^ bool_comparison
+    let _ = if x { "yes" } else { "no" };
+    //~^ bool_comparison
+    let _ = if !x { "yes" } else { "no" };
+    //~^ bool_comparison
+    let _ = if !x { "yes" } else { "no" };
+    //~^ bool_comparison
+    let _ = if x { "yes" } else { "no" };
+    //~^ bool_comparison
+    let _ = if !x { "yes" } else { "no" };
+    //~^ bool_comparison
+    let _ = if x { "yes" } else { "no" };
+    //~^ bool_comparison
+    let _ = if !x { "yes" } else { "no" };
+    //~^ bool_comparison
+    let _ = if x { "yes" } else { "no" };
+    //~^ bool_comparison
+    let _ = if x { "yes" } else { "no" };
+    //~^ bool_comparison
+    let _ = if !x { "yes" } else { "no" };
+    //~^ bool_comparison
+
     let y = true;
-    if !x & y {
-        //~^ bool_comparison
-        "yes"
-    } else {
-        "no"
-    };
-    if x & !y {
-        //~^ bool_comparison
-        "yes"
-    } else {
-        "no"
-    };
+    let _ = if !x & y { "yes" } else { "no" };
+    //~^ bool_comparison
+    let _ = if x & !y { "yes" } else { "no" };
+    //~^ bool_comparison
 }
 
-#[allow(dead_code)]
 fn issue3703() {
     struct Foo;
     impl PartialEq<bool> for Foo {
@@ -127,26 +71,6 @@ fn issue3703() {
     if false < Foo {}
 }
 
-#[allow(dead_code)]
-fn issue4983() {
-    let a = true;
-    let b = false;
-
-    if a != b {};
-    //~^ bool_comparison
-    if a != b {};
-    //~^ bool_comparison
-    if a == b {};
-    if !a == !b {};
-
-    if b != a {};
-    //~^ bool_comparison
-    if b != a {};
-    //~^ bool_comparison
-    if b == a {};
-    if !b == !a {};
-}
-
 macro_rules! m {
     ($func:ident) => {
         $func()
@@ -157,7 +81,6 @@ fn func() -> bool {
     true
 }
 
-#[allow(dead_code)]
 fn issue3973() {
     // ok, don't lint on `cfg` invocation
     if false == cfg!(feature = "debugging") {}
@@ -196,6 +119,34 @@ fn issue9907() {
     //~^ bool_comparison
     // This is not part of the issue, but an unexpected found when fixing the issue,
     // the provided span was inside of macro rather than the macro callsite.
-    let _ = ((1 < 2) != m!(func)) as usize;
+    let _ = ((1 < 2) & !m!(func)) as usize;
     //~^ bool_comparison
 }
+
+#[allow(clippy::nonminimal_bool)]
+fn issue15367() {
+    let a = true;
+    let b = false;
+
+    // these cases are handled by `nonminimal_bool`, so don't double-lint
+    if a == !b {};
+    if !a == b {};
+    if b == !a {};
+    if !b == a {};
+}
+
+fn issue15497() {
+    fn func() -> bool {
+        true
+    }
+
+    fn foo(x: bool) -> bool {
+        x & !m!(func)
+        //~^ bool_comparison
+    }
+
+    fn bar(x: bool) -> bool {
+        !x & m!(func)
+        //~^ bool_comparison
+    }
+}
diff --git a/src/tools/clippy/tests/ui/bool_comparison.rs b/src/tools/clippy/tests/ui/bool_comparison.rs
index 7265c2b4c5a..1b1108d6ce5 100644
--- a/src/tools/clippy/tests/ui/bool_comparison.rs
+++ b/src/tools/clippy/tests/ui/bool_comparison.rs
@@ -1,97 +1,41 @@
 #![allow(non_local_definitions, clippy::needless_if)]
 #![warn(clippy::bool_comparison)]
-#![allow(clippy::non_canonical_partial_ord_impl, clippy::nonminimal_bool)]
+#![allow(clippy::non_canonical_partial_ord_impl)]
 
 fn main() {
     let x = true;
-    if x == true {
-        //~^ bool_comparison
-        "yes"
-    } else {
-        "no"
-    };
-    if x == false {
-        //~^ bool_comparison
-        "yes"
-    } else {
-        "no"
-    };
-    if true == x {
-        //~^ bool_comparison
-        "yes"
-    } else {
-        "no"
-    };
-    if false == x {
-        //~^ bool_comparison
-        "yes"
-    } else {
-        "no"
-    };
-    if x != true {
-        //~^ bool_comparison
-        "yes"
-    } else {
-        "no"
-    };
-    if x != false {
-        //~^ bool_comparison
-        "yes"
-    } else {
-        "no"
-    };
-    if true != x {
-        //~^ bool_comparison
-        "yes"
-    } else {
-        "no"
-    };
-    if false != x {
-        //~^ bool_comparison
-        "yes"
-    } else {
-        "no"
-    };
-    if x < true {
-        //~^ bool_comparison
-        "yes"
-    } else {
-        "no"
-    };
-    if false < x {
-        //~^ bool_comparison
-        "yes"
-    } else {
-        "no"
-    };
-    if x > false {
-        //~^ bool_comparison
-        "yes"
-    } else {
-        "no"
-    };
-    if true > x {
-        //~^ bool_comparison
-        "yes"
-    } else {
-        "no"
-    };
+    let _ = if x == true { "yes" } else { "no" };
+    //~^ bool_comparison
+    let _ = if x == false { "yes" } else { "no" };
+    //~^ bool_comparison
+    let _ = if true == x { "yes" } else { "no" };
+    //~^ bool_comparison
+    let _ = if false == x { "yes" } else { "no" };
+    //~^ bool_comparison
+    let _ = if x != true { "yes" } else { "no" };
+    //~^ bool_comparison
+    let _ = if x != false { "yes" } else { "no" };
+    //~^ bool_comparison
+    let _ = if true != x { "yes" } else { "no" };
+    //~^ bool_comparison
+    let _ = if false != x { "yes" } else { "no" };
+    //~^ bool_comparison
+    let _ = if x < true { "yes" } else { "no" };
+    //~^ bool_comparison
+    let _ = if false < x { "yes" } else { "no" };
+    //~^ bool_comparison
+    let _ = if x > false { "yes" } else { "no" };
+    //~^ bool_comparison
+    let _ = if true > x { "yes" } else { "no" };
+    //~^ bool_comparison
+
     let y = true;
-    if x < y {
-        //~^ bool_comparison
-        "yes"
-    } else {
-        "no"
-    };
-    if x > y {
-        //~^ bool_comparison
-        "yes"
-    } else {
-        "no"
-    };
+    let _ = if x < y { "yes" } else { "no" };
+    //~^ bool_comparison
+    let _ = if x > y { "yes" } else { "no" };
+    //~^ bool_comparison
 }
 
-#[allow(dead_code)]
 fn issue3703() {
     struct Foo;
     impl PartialEq<bool> for Foo {
@@ -127,26 +71,6 @@ fn issue3703() {
     if false < Foo {}
 }
 
-#[allow(dead_code)]
-fn issue4983() {
-    let a = true;
-    let b = false;
-
-    if a == !b {};
-    //~^ bool_comparison
-    if !a == b {};
-    //~^ bool_comparison
-    if a == b {};
-    if !a == !b {};
-
-    if b == !a {};
-    //~^ bool_comparison
-    if !b == a {};
-    //~^ bool_comparison
-    if b == a {};
-    if !b == !a {};
-}
-
 macro_rules! m {
     ($func:ident) => {
         $func()
@@ -157,7 +81,6 @@ fn func() -> bool {
     true
 }
 
-#[allow(dead_code)]
 fn issue3973() {
     // ok, don't lint on `cfg` invocation
     if false == cfg!(feature = "debugging") {}
@@ -196,6 +119,34 @@ fn issue9907() {
     //~^ bool_comparison
     // This is not part of the issue, but an unexpected found when fixing the issue,
     // the provided span was inside of macro rather than the macro callsite.
-    let _ = ((1 < 2) == !m!(func)) as usize;
+    let _ = ((1 < 2) > m!(func)) as usize;
     //~^ bool_comparison
 }
+
+#[allow(clippy::nonminimal_bool)]
+fn issue15367() {
+    let a = true;
+    let b = false;
+
+    // these cases are handled by `nonminimal_bool`, so don't double-lint
+    if a == !b {};
+    if !a == b {};
+    if b == !a {};
+    if !b == a {};
+}
+
+fn issue15497() {
+    fn func() -> bool {
+        true
+    }
+
+    fn foo(x: bool) -> bool {
+        x > m!(func)
+        //~^ bool_comparison
+    }
+
+    fn bar(x: bool) -> bool {
+        x < m!(func)
+        //~^ bool_comparison
+    }
+}
diff --git a/src/tools/clippy/tests/ui/bool_comparison.stderr b/src/tools/clippy/tests/ui/bool_comparison.stderr
index ddbb9ff78ed..98881a2d20f 100644
--- a/src/tools/clippy/tests/ui/bool_comparison.stderr
+++ b/src/tools/clippy/tests/ui/bool_comparison.stderr
@@ -1,155 +1,143 @@
 error: equality checks against true are unnecessary
-  --> tests/ui/bool_comparison.rs:7:8
+  --> tests/ui/bool_comparison.rs:7:16
    |
-LL |     if x == true {
-   |        ^^^^^^^^^ help: try simplifying it as shown: `x`
+LL |     let _ = if x == true { "yes" } else { "no" };
+   |                ^^^^^^^^^ help: try: `x`
    |
    = note: `-D clippy::bool-comparison` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::bool_comparison)]`
 
 error: equality checks against false can be replaced by a negation
-  --> tests/ui/bool_comparison.rs:13:8
+  --> tests/ui/bool_comparison.rs:9:16
    |
-LL |     if x == false {
-   |        ^^^^^^^^^^ help: try simplifying it as shown: `!x`
+LL |     let _ = if x == false { "yes" } else { "no" };
+   |                ^^^^^^^^^^ help: try: `!x`
 
 error: equality checks against true are unnecessary
-  --> tests/ui/bool_comparison.rs:19:8
+  --> tests/ui/bool_comparison.rs:11:16
    |
-LL |     if true == x {
-   |        ^^^^^^^^^ help: try simplifying it as shown: `x`
+LL |     let _ = if true == x { "yes" } else { "no" };
+   |                ^^^^^^^^^ help: try: `x`
 
 error: equality checks against false can be replaced by a negation
-  --> tests/ui/bool_comparison.rs:25:8
+  --> tests/ui/bool_comparison.rs:13:16
    |
-LL |     if false == x {
-   |        ^^^^^^^^^^ help: try simplifying it as shown: `!x`
+LL |     let _ = if false == x { "yes" } else { "no" };
+   |                ^^^^^^^^^^ help: try: `!x`
 
 error: inequality checks against true can be replaced by a negation
-  --> tests/ui/bool_comparison.rs:31:8
+  --> tests/ui/bool_comparison.rs:15:16
    |
-LL |     if x != true {
-   |        ^^^^^^^^^ help: try simplifying it as shown: `!x`
+LL |     let _ = if x != true { "yes" } else { "no" };
+   |                ^^^^^^^^^ help: try: `!x`
 
 error: inequality checks against false are unnecessary
-  --> tests/ui/bool_comparison.rs:37:8
+  --> tests/ui/bool_comparison.rs:17:16
    |
-LL |     if x != false {
-   |        ^^^^^^^^^^ help: try simplifying it as shown: `x`
+LL |     let _ = if x != false { "yes" } else { "no" };
+   |                ^^^^^^^^^^ help: try: `x`
 
 error: inequality checks against true can be replaced by a negation
-  --> tests/ui/bool_comparison.rs:43:8
+  --> tests/ui/bool_comparison.rs:19:16
    |
-LL |     if true != x {
-   |        ^^^^^^^^^ help: try simplifying it as shown: `!x`
+LL |     let _ = if true != x { "yes" } else { "no" };
+   |                ^^^^^^^^^ help: try: `!x`
 
 error: inequality checks against false are unnecessary
-  --> tests/ui/bool_comparison.rs:49:8
+  --> tests/ui/bool_comparison.rs:21:16
    |
-LL |     if false != x {
-   |        ^^^^^^^^^^ help: try simplifying it as shown: `x`
+LL |     let _ = if false != x { "yes" } else { "no" };
+   |                ^^^^^^^^^^ help: try: `x`
 
 error: less than comparison against true can be replaced by a negation
-  --> tests/ui/bool_comparison.rs:55:8
+  --> tests/ui/bool_comparison.rs:23:16
    |
-LL |     if x < true {
-   |        ^^^^^^^^ help: try simplifying it as shown: `!x`
+LL |     let _ = if x < true { "yes" } else { "no" };
+   |                ^^^^^^^^ help: try: `!x`
 
 error: greater than checks against false are unnecessary
-  --> tests/ui/bool_comparison.rs:61:8
+  --> tests/ui/bool_comparison.rs:25:16
    |
-LL |     if false < x {
-   |        ^^^^^^^^^ help: try simplifying it as shown: `x`
+LL |     let _ = if false < x { "yes" } else { "no" };
+   |                ^^^^^^^^^ help: try: `x`
 
 error: greater than checks against false are unnecessary
-  --> tests/ui/bool_comparison.rs:67:8
+  --> tests/ui/bool_comparison.rs:27:16
    |
-LL |     if x > false {
-   |        ^^^^^^^^^ help: try simplifying it as shown: `x`
+LL |     let _ = if x > false { "yes" } else { "no" };
+   |                ^^^^^^^^^ help: try: `x`
 
 error: less than comparison against true can be replaced by a negation
-  --> tests/ui/bool_comparison.rs:73:8
+  --> tests/ui/bool_comparison.rs:29:16
    |
-LL |     if true > x {
-   |        ^^^^^^^^ help: try simplifying it as shown: `!x`
+LL |     let _ = if true > x { "yes" } else { "no" };
+   |                ^^^^^^^^ help: try: `!x`
 
 error: order comparisons between booleans can be simplified
-  --> tests/ui/bool_comparison.rs:80:8
+  --> tests/ui/bool_comparison.rs:33:16
    |
-LL |     if x < y {
-   |        ^^^^^ help: try simplifying it as shown: `!x & y`
+LL |     let _ = if x < y { "yes" } else { "no" };
+   |                ^^^^^ help: try: `!x & y`
 
 error: order comparisons between booleans can be simplified
-  --> tests/ui/bool_comparison.rs:86:8
+  --> tests/ui/bool_comparison.rs:35:16
    |
-LL |     if x > y {
-   |        ^^^^^ help: try simplifying it as shown: `x & !y`
-
-error: this comparison might be written more concisely
-  --> tests/ui/bool_comparison.rs:135:8
-   |
-LL |     if a == !b {};
-   |        ^^^^^^^ help: try simplifying it as shown: `a != b`
-
-error: this comparison might be written more concisely
-  --> tests/ui/bool_comparison.rs:137:8
-   |
-LL |     if !a == b {};
-   |        ^^^^^^^ help: try simplifying it as shown: `a != b`
-
-error: this comparison might be written more concisely
-  --> tests/ui/bool_comparison.rs:142:8
-   |
-LL |     if b == !a {};
-   |        ^^^^^^^ help: try simplifying it as shown: `b != a`
-
-error: this comparison might be written more concisely
-  --> tests/ui/bool_comparison.rs:144:8
-   |
-LL |     if !b == a {};
-   |        ^^^^^^^ help: try simplifying it as shown: `b != a`
+LL |     let _ = if x > y { "yes" } else { "no" };
+   |                ^^^^^ help: try: `x & !y`
 
 error: equality checks against false can be replaced by a negation
-  --> tests/ui/bool_comparison.rs:169:8
+  --> tests/ui/bool_comparison.rs:92:8
    |
 LL |     if false == m!(func) {}
-   |        ^^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `!m!(func)`
+   |        ^^^^^^^^^^^^^^^^^ help: try: `!m!(func)`
 
 error: equality checks against false can be replaced by a negation
-  --> tests/ui/bool_comparison.rs:171:8
+  --> tests/ui/bool_comparison.rs:94:8
    |
 LL |     if m!(func) == false {}
-   |        ^^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `!m!(func)`
+   |        ^^^^^^^^^^^^^^^^^ help: try: `!m!(func)`
 
 error: equality checks against true are unnecessary
-  --> tests/ui/bool_comparison.rs:173:8
+  --> tests/ui/bool_comparison.rs:96:8
    |
 LL |     if true == m!(func) {}
-   |        ^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `m!(func)`
+   |        ^^^^^^^^^^^^^^^^ help: try: `m!(func)`
 
 error: equality checks against true are unnecessary
-  --> tests/ui/bool_comparison.rs:175:8
+  --> tests/ui/bool_comparison.rs:98:8
    |
 LL |     if m!(func) == true {}
-   |        ^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `m!(func)`
+   |        ^^^^^^^^^^^^^^^^ help: try: `m!(func)`
 
 error: equality checks against false can be replaced by a negation
-  --> tests/ui/bool_comparison.rs:193:14
+  --> tests/ui/bool_comparison.rs:116:14
    |
 LL |     let _ = ((1 < 2) == false) as usize;
-   |              ^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `1 >= 2`
+   |              ^^^^^^^^^^^^^^^^ help: try: `1 >= 2`
 
 error: equality checks against false can be replaced by a negation
-  --> tests/ui/bool_comparison.rs:195:14
+  --> tests/ui/bool_comparison.rs:118:14
    |
 LL |     let _ = (false == m!(func)) as usize;
-   |              ^^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `!m!(func)`
+   |              ^^^^^^^^^^^^^^^^^ help: try: `!m!(func)`
 
-error: this comparison might be written more concisely
-  --> tests/ui/bool_comparison.rs:199:14
+error: order comparisons between booleans can be simplified
+  --> tests/ui/bool_comparison.rs:122:14
+   |
+LL |     let _ = ((1 < 2) > m!(func)) as usize;
+   |              ^^^^^^^^^^^^^^^^^^ help: try: `(1 < 2) & !m!(func)`
+
+error: order comparisons between booleans can be simplified
+  --> tests/ui/bool_comparison.rs:144:9
+   |
+LL |         x > m!(func)
+   |         ^^^^^^^^^^^^ help: try: `x & !m!(func)`
+
+error: order comparisons between booleans can be simplified
+  --> tests/ui/bool_comparison.rs:149:9
    |
-LL |     let _ = ((1 < 2) == !m!(func)) as usize;
-   |              ^^^^^^^^^^^^^^^^^^^^ help: try simplifying it as shown: `(1 < 2) != m!(func)`
+LL |         x < m!(func)
+   |         ^^^^^^^^^^^^ help: try: `!x & m!(func)`
 
-error: aborting due to 25 previous errors
+error: aborting due to 23 previous errors
 
diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr.fixed b/src/tools/clippy/tests/ui/borrow_as_ptr.fixed
index 3f6e5245b87..bfe826508f3 100644
--- a/src/tools/clippy/tests/ui/borrow_as_ptr.fixed
+++ b/src/tools/clippy/tests/ui/borrow_as_ptr.fixed
@@ -1,6 +1,9 @@
+//@aux-build:proc_macros.rs
 #![warn(clippy::borrow_as_ptr)]
 #![allow(clippy::useless_vec)]
 
+extern crate proc_macros;
+
 fn a() -> i32 {
     0
 }
@@ -53,3 +56,12 @@ fn issue_15141() {
     // Don't lint cast to dyn trait pointers
     let b = &a as *const dyn std::any::Any;
 }
+
+fn issue15389() {
+    proc_macros::with_span! {
+        span
+        let var = 0u32;
+        // Don't lint in proc-macros
+        let _ = &var as *const u32;
+    };
+}
diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr.rs b/src/tools/clippy/tests/ui/borrow_as_ptr.rs
index 20f4f40e001..ce248f157c6 100644
--- a/src/tools/clippy/tests/ui/borrow_as_ptr.rs
+++ b/src/tools/clippy/tests/ui/borrow_as_ptr.rs
@@ -1,6 +1,9 @@
+//@aux-build:proc_macros.rs
 #![warn(clippy::borrow_as_ptr)]
 #![allow(clippy::useless_vec)]
 
+extern crate proc_macros;
+
 fn a() -> i32 {
     0
 }
@@ -53,3 +56,12 @@ fn issue_15141() {
     // Don't lint cast to dyn trait pointers
     let b = &a as *const dyn std::any::Any;
 }
+
+fn issue15389() {
+    proc_macros::with_span! {
+        span
+        let var = 0u32;
+        // Don't lint in proc-macros
+        let _ = &var as *const u32;
+    };
+}
diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr.stderr b/src/tools/clippy/tests/ui/borrow_as_ptr.stderr
index b1fcce49403..b371b477a50 100644
--- a/src/tools/clippy/tests/ui/borrow_as_ptr.stderr
+++ b/src/tools/clippy/tests/ui/borrow_as_ptr.stderr
@@ -1,5 +1,5 @@
 error: borrow as raw pointer
-  --> tests/ui/borrow_as_ptr.rs:11:14
+  --> tests/ui/borrow_as_ptr.rs:14:14
    |
 LL |     let _p = &val as *const i32;
    |              ^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::addr_of!(val)`
@@ -8,25 +8,25 @@ LL |     let _p = &val as *const i32;
    = help: to override `-D warnings` add `#[allow(clippy::borrow_as_ptr)]`
 
 error: borrow as raw pointer
-  --> tests/ui/borrow_as_ptr.rs:19:18
+  --> tests/ui/borrow_as_ptr.rs:22:18
    |
 LL |     let _p_mut = &mut val_mut as *mut i32;
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::addr_of_mut!(val_mut)`
 
 error: borrow as raw pointer
-  --> tests/ui/borrow_as_ptr.rs:23:16
+  --> tests/ui/borrow_as_ptr.rs:26:16
    |
 LL |     let _raw = (&mut x[1] as *mut i32).wrapping_offset(-1);
    |                ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::addr_of_mut!(x[1])`
 
 error: borrow as raw pointer
-  --> tests/ui/borrow_as_ptr.rs:29:17
+  --> tests/ui/borrow_as_ptr.rs:32:17
    |
 LL |     let _raw = (&mut x[1] as *mut i32).wrapping_offset(-1);
    |                 ^^^^^^^^^^^^^^^^^^^^^ help: try: `&raw mut x[1]`
 
 error: implicit borrow as raw pointer
-  --> tests/ui/borrow_as_ptr.rs:35:25
+  --> tests/ui/borrow_as_ptr.rs:38:25
    |
 LL |     let p: *const i32 = &val;
    |                         ^^^^
@@ -37,7 +37,7 @@ LL |     let p: *const i32 = &raw const val;
    |                          +++++++++
 
 error: implicit borrow as raw pointer
-  --> tests/ui/borrow_as_ptr.rs:39:23
+  --> tests/ui/borrow_as_ptr.rs:42:23
    |
 LL |     let p: *mut i32 = &mut val;
    |                       ^^^^^^^^
@@ -48,7 +48,7 @@ LL |     let p: *mut i32 = &raw mut val;
    |                        +++
 
 error: implicit borrow as raw pointer
-  --> tests/ui/borrow_as_ptr.rs:44:19
+  --> tests/ui/borrow_as_ptr.rs:47:19
    |
 LL |     core::ptr::eq(&val, &1);
    |                   ^^^^
diff --git a/src/tools/clippy/tests/ui/cast.rs b/src/tools/clippy/tests/ui/cast.rs
index 525be821650..fab02bf7b24 100644
--- a/src/tools/clippy/tests/ui/cast.rs
+++ b/src/tools/clippy/tests/ui/cast.rs
@@ -569,3 +569,16 @@ fn issue12721() {
     (255 % 999999u64) as u8;
     //~^ cast_possible_truncation
 }
+
+mod issue14150 {
+    #[clippy::msrv = "1.87"]
+    fn msrv_supports_cast_signed() {
+        _ = 1u8 as i8;
+        //~^ cast_possible_wrap
+    }
+    #[clippy::msrv = "1.86"]
+    fn msrv_doesnt_supports_cast_signed() {
+        _ = 1u8 as i8;
+        //~^ cast_possible_wrap
+    }
+}
diff --git a/src/tools/clippy/tests/ui/cast.stderr b/src/tools/clippy/tests/ui/cast.stderr
index 1cb30d95667..8c48855123f 100644
--- a/src/tools/clippy/tests/ui/cast.stderr
+++ b/src/tools/clippy/tests/ui/cast.stderr
@@ -194,7 +194,7 @@ error: casting `u8` to `i8` may wrap around the value
   --> tests/ui/cast.rs:88:5
    |
 LL |     1u8 as i8;
-   |     ^^^^^^^^^
+   |     ^^^^^^^^^ help: if this is intentional, use `cast_signed()` instead: `1u8.cast_signed()`
    |
    = note: `-D clippy::cast-possible-wrap` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::cast_possible_wrap)]`
@@ -203,25 +203,25 @@ error: casting `u16` to `i16` may wrap around the value
   --> tests/ui/cast.rs:91:5
    |
 LL |     1u16 as i16;
-   |     ^^^^^^^^^^^
+   |     ^^^^^^^^^^^ help: if this is intentional, use `cast_signed()` instead: `1u16.cast_signed()`
 
 error: casting `u32` to `i32` may wrap around the value
   --> tests/ui/cast.rs:94:5
    |
 LL |     1u32 as i32;
-   |     ^^^^^^^^^^^
+   |     ^^^^^^^^^^^ help: if this is intentional, use `cast_signed()` instead: `1u32.cast_signed()`
 
 error: casting `u64` to `i64` may wrap around the value
   --> tests/ui/cast.rs:97:5
    |
 LL |     1u64 as i64;
-   |     ^^^^^^^^^^^
+   |     ^^^^^^^^^^^ help: if this is intentional, use `cast_signed()` instead: `1u64.cast_signed()`
 
 error: casting `usize` to `isize` may wrap around the value
   --> tests/ui/cast.rs:100:5
    |
 LL |     1usize as isize;
-   |     ^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_signed()` instead: `1usize.cast_signed()`
 
 error: casting `usize` to `i8` may truncate the value
   --> tests/ui/cast.rs:104:5
@@ -321,43 +321,43 @@ error: casting `i32` to `u32` may lose the sign of the value
   --> tests/ui/cast.rs:138:5
    |
 LL |     -1i32 as u32;
-   |     ^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-1i32).cast_unsigned()`
 
 error: casting `isize` to `usize` may lose the sign of the value
   --> tests/ui/cast.rs:142:5
    |
 LL |     -1isize as usize;
-   |     ^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-1isize).cast_unsigned()`
 
 error: casting `i8` to `u8` may lose the sign of the value
   --> tests/ui/cast.rs:154:5
    |
 LL |     (i8::MIN).abs() as u8;
-   |     ^^^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(i8::MIN).abs().cast_unsigned()`
 
 error: casting `i64` to `u64` may lose the sign of the value
   --> tests/ui/cast.rs:159:5
    |
 LL |     (-1i64).abs() as u64;
-   |     ^^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-1i64).abs().cast_unsigned()`
 
 error: casting `isize` to `usize` may lose the sign of the value
   --> tests/ui/cast.rs:161:5
    |
 LL |     (-1isize).abs() as usize;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-1isize).abs().cast_unsigned()`
 
 error: casting `i64` to `u64` may lose the sign of the value
   --> tests/ui/cast.rs:169:5
    |
 LL |     (unsafe { (-1i64).checked_abs().unwrap_unchecked() }) as u64;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(unsafe { (-1i64).checked_abs().unwrap_unchecked() }).cast_unsigned()`
 
 error: casting `i64` to `u64` may lose the sign of the value
   --> tests/ui/cast.rs:185:5
    |
 LL |     (unsafe { (-1i64).checked_isqrt().unwrap_unchecked() }) as u64;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(unsafe { (-1i64).checked_isqrt().unwrap_unchecked() }).cast_unsigned()`
 
 error: casting `i64` to `i8` may truncate the value
   --> tests/ui/cast.rs:237:5
@@ -495,79 +495,79 @@ error: casting `i32` to `u32` may lose the sign of the value
   --> tests/ui/cast.rs:438:9
    |
 LL |         (x * x) as u32;
-   |         ^^^^^^^^^^^^^^
+   |         ^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(x * x).cast_unsigned()`
 
 error: casting `i32` to `u32` may lose the sign of the value
   --> tests/ui/cast.rs:444:32
    |
 LL |     let _a = |x: i32| -> u32 { (x * x * x * x) as u32 };
-   |                                ^^^^^^^^^^^^^^^^^^^^^^
+   |                                ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(x * x * x * x).cast_unsigned()`
 
 error: casting `i32` to `u32` may lose the sign of the value
   --> tests/ui/cast.rs:447:5
    |
 LL |     (2_i32).checked_pow(3).unwrap() as u32;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(2_i32).checked_pow(3).unwrap().cast_unsigned()`
 
 error: casting `i32` to `u32` may lose the sign of the value
   --> tests/ui/cast.rs:449:5
    |
 LL |     (-2_i32).pow(3) as u32;
-   |     ^^^^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-2_i32).pow(3).cast_unsigned()`
 
 error: casting `i32` to `u32` may lose the sign of the value
   --> tests/ui/cast.rs:454:5
    |
 LL |     (-5_i32 % 2) as u32;
-   |     ^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-5_i32 % 2).cast_unsigned()`
 
 error: casting `i32` to `u32` may lose the sign of the value
   --> tests/ui/cast.rs:457:5
    |
 LL |     (-5_i32 % -2) as u32;
-   |     ^^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-5_i32 % -2).cast_unsigned()`
 
 error: casting `i32` to `u32` may lose the sign of the value
   --> tests/ui/cast.rs:461:5
    |
 LL |     (-2_i32 >> 1) as u32;
-   |     ^^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(-2_i32 >> 1).cast_unsigned()`
 
 error: casting `i32` to `u32` may lose the sign of the value
   --> tests/ui/cast.rs:465:5
    |
 LL |     (x * x) as u32;
-   |     ^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(x * x).cast_unsigned()`
 
 error: casting `i32` to `u32` may lose the sign of the value
   --> tests/ui/cast.rs:467:5
    |
 LL |     (x * x * x) as u32;
-   |     ^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(x * x * x).cast_unsigned()`
 
 error: casting `i16` to `u16` may lose the sign of the value
   --> tests/ui/cast.rs:471:5
    |
 LL |     (y * y * y * y * -2) as u16;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(y * y * y * y * -2).cast_unsigned()`
 
 error: casting `i16` to `u16` may lose the sign of the value
   --> tests/ui/cast.rs:474:5
    |
 LL |     (y * y * y / y * 2) as u16;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(y * y * y / y * 2).cast_unsigned()`
 
 error: casting `i16` to `u16` may lose the sign of the value
   --> tests/ui/cast.rs:476:5
    |
 LL |     (y * y / y * 2) as u16;
-   |     ^^^^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(y * y / y * 2).cast_unsigned()`
 
 error: casting `i16` to `u16` may lose the sign of the value
   --> tests/ui/cast.rs:479:5
    |
 LL |     (y / y * y * -2) as u16;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(y / y * y * -2).cast_unsigned()`
 
 error: equal expressions as operands to `/`
   --> tests/ui/cast.rs:479:6
@@ -581,97 +581,97 @@ error: casting `i16` to `u16` may lose the sign of the value
   --> tests/ui/cast.rs:483:5
    |
 LL |     (y + y + y + -2) as u16;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(y + y + y + -2).cast_unsigned()`
 
 error: casting `i16` to `u16` may lose the sign of the value
   --> tests/ui/cast.rs:486:5
    |
 LL |     (y + y + y + 2) as u16;
-   |     ^^^^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(y + y + y + 2).cast_unsigned()`
 
 error: casting `i16` to `u16` may lose the sign of the value
   --> tests/ui/cast.rs:490:5
    |
 LL |     (z + -2) as u16;
-   |     ^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(z + -2).cast_unsigned()`
 
 error: casting `i16` to `u16` may lose the sign of the value
   --> tests/ui/cast.rs:493:5
    |
 LL |     (z + z + 2) as u16;
-   |     ^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(z + z + 2).cast_unsigned()`
 
 error: casting `i32` to `u32` may lose the sign of the value
   --> tests/ui/cast.rs:497:9
    |
 LL |         (a * a * b * b * c * c) as u32;
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a * a * b * b * c * c).cast_unsigned()`
 
 error: casting `i32` to `u32` may lose the sign of the value
   --> tests/ui/cast.rs:499:9
    |
 LL |         (a * b * c) as u32;
-   |         ^^^^^^^^^^^^^^^^^^
+   |         ^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a * b * c).cast_unsigned()`
 
 error: casting `i32` to `u32` may lose the sign of the value
   --> tests/ui/cast.rs:502:9
    |
 LL |         (a * -b * c) as u32;
-   |         ^^^^^^^^^^^^^^^^^^^
+   |         ^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a * -b * c).cast_unsigned()`
 
 error: casting `i32` to `u32` may lose the sign of the value
   --> tests/ui/cast.rs:505:9
    |
 LL |         (a * b * c * c) as u32;
-   |         ^^^^^^^^^^^^^^^^^^^^^^
+   |         ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a * b * c * c).cast_unsigned()`
 
 error: casting `i32` to `u32` may lose the sign of the value
   --> tests/ui/cast.rs:507:9
    |
 LL |         (a * -2) as u32;
-   |         ^^^^^^^^^^^^^^^
+   |         ^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a * -2).cast_unsigned()`
 
 error: casting `i32` to `u32` may lose the sign of the value
   --> tests/ui/cast.rs:510:9
    |
 LL |         (a * b * c * -2) as u32;
-   |         ^^^^^^^^^^^^^^^^^^^^^^^
+   |         ^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a * b * c * -2).cast_unsigned()`
 
 error: casting `i32` to `u32` may lose the sign of the value
   --> tests/ui/cast.rs:513:9
    |
 LL |         (a / b) as u32;
-   |         ^^^^^^^^^^^^^^
+   |         ^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a / b).cast_unsigned()`
 
 error: casting `i32` to `u32` may lose the sign of the value
   --> tests/ui/cast.rs:515:9
    |
 LL |         (a / b * c) as u32;
-   |         ^^^^^^^^^^^^^^^^^^
+   |         ^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a / b * c).cast_unsigned()`
 
 error: casting `i32` to `u32` may lose the sign of the value
   --> tests/ui/cast.rs:518:9
    |
 LL |         (a / b + b * c) as u32;
-   |         ^^^^^^^^^^^^^^^^^^^^^^
+   |         ^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a / b + b * c).cast_unsigned()`
 
 error: casting `i32` to `u32` may lose the sign of the value
   --> tests/ui/cast.rs:521:9
    |
 LL |         a.saturating_pow(3) as u32;
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `a.saturating_pow(3).cast_unsigned()`
 
 error: casting `i32` to `u32` may lose the sign of the value
   --> tests/ui/cast.rs:524:9
    |
 LL |         (a.abs() * b.pow(2) / c.abs()) as u32
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `(a.abs() * b.pow(2) / c.abs()).cast_unsigned()`
 
 error: casting `i32` to `u32` may lose the sign of the value
   --> tests/ui/cast.rs:532:21
    |
 LL |             let _ = i32::MIN as u32; // cast_sign_loss
-   |                     ^^^^^^^^^^^^^^^
+   |                     ^^^^^^^^^^^^^^^ help: if this is intentional, use `cast_unsigned()` instead: `i32::MIN.cast_unsigned()`
 ...
 LL |     m!();
    |     ---- in this macro invocation
@@ -752,5 +752,17 @@ LL -     (255 % 999999u64) as u8;
 LL +     u8::try_from(255 % 999999u64);
    |
 
-error: aborting due to 92 previous errors
+error: casting `u8` to `i8` may wrap around the value
+  --> tests/ui/cast.rs:576:13
+   |
+LL |         _ = 1u8 as i8;
+   |             ^^^^^^^^^ help: if this is intentional, use `cast_signed()` instead: `1u8.cast_signed()`
+
+error: casting `u8` to `i8` may wrap around the value
+  --> tests/ui/cast.rs:581:13
+   |
+LL |         _ = 1u8 as i8;
+   |             ^^^^^^^^^
+
+error: aborting due to 94 previous errors
 
diff --git a/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.fixed b/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.fixed
index bddcb0ebf64..3c1cf884595 100644
--- a/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.fixed
+++ b/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.fixed
@@ -1,30 +1,53 @@
 #![warn(clippy::cast_slice_from_raw_parts)]
 
-#[allow(unused_imports, unused_unsafe)]
+const fn require_raw_slice_ptr<T>(_: *const [T]) {}
+
 fn main() {
     let mut vec = vec![0u8; 1];
     let ptr: *const u8 = vec.as_ptr();
     let mptr = vec.as_mut_ptr();
-    let _: *const [u8] = unsafe { core::ptr::slice_from_raw_parts(ptr, 1) };
+    let _: *const [u8] = unsafe { std::ptr::slice_from_raw_parts(ptr, 1) };
     //~^ cast_slice_from_raw_parts
-    let _: *const [u8] = unsafe { core::ptr::slice_from_raw_parts_mut(mptr, 1) };
+    let _: *const [u8] = unsafe { std::ptr::slice_from_raw_parts_mut(mptr, 1) };
     //~^ cast_slice_from_raw_parts
-    let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1);
+    let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, 1);
     //~^ cast_slice_from_raw_parts
     {
         use core::slice;
-        let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1);
+        let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, 1);
         //~^ cast_slice_from_raw_parts
         use slice as one;
-        let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1);
+        let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, 1);
         //~^ cast_slice_from_raw_parts
     }
     {
         use std::slice;
-        let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1);
+        let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, 1);
         //~^ cast_slice_from_raw_parts
         use slice as one;
-        let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1);
+        let _: *const [u8] = std::ptr::slice_from_raw_parts(ptr, 1);
         //~^ cast_slice_from_raw_parts
     }
+
+    // implicit cast
+    {
+        let _: *const [u8] = unsafe { std::ptr::slice_from_raw_parts(ptr, 1) };
+        //~^ cast_slice_from_raw_parts
+        let _: *mut [u8] = unsafe { std::ptr::slice_from_raw_parts_mut(mptr, 1) };
+        //~^ cast_slice_from_raw_parts
+        require_raw_slice_ptr(unsafe { std::ptr::slice_from_raw_parts(ptr, 1) });
+        //~^ cast_slice_from_raw_parts
+    }
+
+    // implicit cast in const context
+    const {
+        const PTR: *const u8 = std::ptr::null();
+        const MPTR: *mut u8 = std::ptr::null_mut();
+        let _: *const [u8] = unsafe { std::ptr::slice_from_raw_parts(PTR, 1) };
+        //~^ cast_slice_from_raw_parts
+        let _: *mut [u8] = unsafe { std::ptr::slice_from_raw_parts_mut(MPTR, 1) };
+        //~^ cast_slice_from_raw_parts
+        require_raw_slice_ptr(unsafe { std::ptr::slice_from_raw_parts(PTR, 1) });
+        //~^ cast_slice_from_raw_parts
+    };
 }
diff --git a/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.rs b/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.rs
index 0a1eb276d5e..8f57b1f9619 100644
--- a/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.rs
+++ b/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.rs
@@ -1,6 +1,7 @@
 #![warn(clippy::cast_slice_from_raw_parts)]
 
-#[allow(unused_imports, unused_unsafe)]
+const fn require_raw_slice_ptr<T>(_: *const [T]) {}
+
 fn main() {
     let mut vec = vec![0u8; 1];
     let ptr: *const u8 = vec.as_ptr();
@@ -27,4 +28,26 @@ fn main() {
         let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8];
         //~^ cast_slice_from_raw_parts
     }
+
+    // implicit cast
+    {
+        let _: *const [u8] = unsafe { std::slice::from_raw_parts(ptr, 1) };
+        //~^ cast_slice_from_raw_parts
+        let _: *mut [u8] = unsafe { std::slice::from_raw_parts_mut(mptr, 1) };
+        //~^ cast_slice_from_raw_parts
+        require_raw_slice_ptr(unsafe { std::slice::from_raw_parts(ptr, 1) });
+        //~^ cast_slice_from_raw_parts
+    }
+
+    // implicit cast in const context
+    const {
+        const PTR: *const u8 = std::ptr::null();
+        const MPTR: *mut u8 = std::ptr::null_mut();
+        let _: *const [u8] = unsafe { std::slice::from_raw_parts(PTR, 1) };
+        //~^ cast_slice_from_raw_parts
+        let _: *mut [u8] = unsafe { std::slice::from_raw_parts_mut(MPTR, 1) };
+        //~^ cast_slice_from_raw_parts
+        require_raw_slice_ptr(unsafe { std::slice::from_raw_parts(PTR, 1) });
+        //~^ cast_slice_from_raw_parts
+    };
 }
diff --git a/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.stderr b/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.stderr
index 60794a988db..328dbafbafe 100644
--- a/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.stderr
+++ b/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast.stderr
@@ -1,47 +1,83 @@
 error: casting the result of `from_raw_parts` to *const [u8]
-  --> tests/ui/cast_raw_slice_pointer_cast.rs:8:35
+  --> tests/ui/cast_raw_slice_pointer_cast.rs:9:35
    |
 LL |     let _: *const [u8] = unsafe { std::slice::from_raw_parts(ptr, 1) as *const [u8] };
-   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)`
+   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::ptr::slice_from_raw_parts(ptr, 1)`
    |
    = note: `-D clippy::cast-slice-from-raw-parts` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::cast_slice_from_raw_parts)]`
 
 error: casting the result of `from_raw_parts_mut` to *mut [u8]
-  --> tests/ui/cast_raw_slice_pointer_cast.rs:10:35
+  --> tests/ui/cast_raw_slice_pointer_cast.rs:11:35
    |
 LL |     let _: *const [u8] = unsafe { std::slice::from_raw_parts_mut(mptr, 1) as *mut [u8] };
-   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts_mut(mptr, 1)`
+   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::ptr::slice_from_raw_parts_mut(mptr, 1)`
 
 error: casting the result of `from_raw_parts` to *const [u8]
-  --> tests/ui/cast_raw_slice_pointer_cast.rs:12:26
+  --> tests/ui/cast_raw_slice_pointer_cast.rs:13:26
    |
 LL |     let _: *const [u8] = unsafe { std::slice::from_raw_parts(ptr, 1) } as *const [u8];
-   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)`
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::ptr::slice_from_raw_parts(ptr, 1)`
 
 error: casting the result of `from_raw_parts` to *const [u8]
-  --> tests/ui/cast_raw_slice_pointer_cast.rs:16:30
+  --> tests/ui/cast_raw_slice_pointer_cast.rs:17:30
    |
 LL |         let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8];
-   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)`
+   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::ptr::slice_from_raw_parts(ptr, 1)`
 
 error: casting the result of `from_raw_parts` to *const [u8]
-  --> tests/ui/cast_raw_slice_pointer_cast.rs:19:30
+  --> tests/ui/cast_raw_slice_pointer_cast.rs:20:30
    |
 LL |         let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8];
-   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)`
+   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::ptr::slice_from_raw_parts(ptr, 1)`
 
 error: casting the result of `from_raw_parts` to *const [u8]
-  --> tests/ui/cast_raw_slice_pointer_cast.rs:24:30
+  --> tests/ui/cast_raw_slice_pointer_cast.rs:25:30
    |
 LL |         let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8];
-   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)`
+   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::ptr::slice_from_raw_parts(ptr, 1)`
 
 error: casting the result of `from_raw_parts` to *const [u8]
-  --> tests/ui/cast_raw_slice_pointer_cast.rs:27:30
+  --> tests/ui/cast_raw_slice_pointer_cast.rs:28:30
    |
 LL |         let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8];
-   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)`
+   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `std::ptr::slice_from_raw_parts(ptr, 1)`
 
-error: aborting due to 7 previous errors
+error: implicitly casting the result of `from_raw_parts` to `*const [u8]`
+  --> tests/ui/cast_raw_slice_pointer_cast.rs:34:39
+   |
+LL |         let _: *const [u8] = unsafe { std::slice::from_raw_parts(ptr, 1) };
+   |                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `std::ptr::slice_from_raw_parts(ptr, 1)`
+
+error: implicitly casting the result of `from_raw_parts_mut` to `*mut [u8]`
+  --> tests/ui/cast_raw_slice_pointer_cast.rs:36:37
+   |
+LL |         let _: *mut [u8] = unsafe { std::slice::from_raw_parts_mut(mptr, 1) };
+   |                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `std::ptr::slice_from_raw_parts_mut(mptr, 1)`
+
+error: implicitly casting the result of `from_raw_parts` to `*const [u8]`
+  --> tests/ui/cast_raw_slice_pointer_cast.rs:38:40
+   |
+LL |         require_raw_slice_ptr(unsafe { std::slice::from_raw_parts(ptr, 1) });
+   |                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `std::ptr::slice_from_raw_parts(ptr, 1)`
+
+error: implicitly casting the result of `from_raw_parts` to `*const [u8]`
+  --> tests/ui/cast_raw_slice_pointer_cast.rs:46:39
+   |
+LL |         let _: *const [u8] = unsafe { std::slice::from_raw_parts(PTR, 1) };
+   |                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `std::ptr::slice_from_raw_parts(PTR, 1)`
+
+error: implicitly casting the result of `from_raw_parts_mut` to `*mut [u8]`
+  --> tests/ui/cast_raw_slice_pointer_cast.rs:48:37
+   |
+LL |         let _: *mut [u8] = unsafe { std::slice::from_raw_parts_mut(MPTR, 1) };
+   |                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `std::ptr::slice_from_raw_parts_mut(MPTR, 1)`
+
+error: implicitly casting the result of `from_raw_parts` to `*const [u8]`
+  --> tests/ui/cast_raw_slice_pointer_cast.rs:50:40
+   |
+LL |         require_raw_slice_ptr(unsafe { std::slice::from_raw_parts(PTR, 1) });
+   |                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `std::ptr::slice_from_raw_parts(PTR, 1)`
+
+error: aborting due to 13 previous errors
 
diff --git a/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast_no_std.fixed b/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast_no_std.fixed
new file mode 100644
index 00000000000..f71fb8d863c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast_no_std.fixed
@@ -0,0 +1,55 @@
+#![warn(clippy::cast_slice_from_raw_parts)]
+#![no_std]
+#![crate_type = "lib"]
+
+const fn require_raw_slice_ptr<T>(_: *const [T]) {}
+
+fn main() {
+    let mut arr = [0u8; 1];
+    let ptr: *const u8 = arr.as_ptr();
+    let mptr = arr.as_mut_ptr();
+    let _: *const [u8] = unsafe { core::ptr::slice_from_raw_parts(ptr, 1) };
+    //~^ cast_slice_from_raw_parts
+    let _: *const [u8] = unsafe { core::ptr::slice_from_raw_parts_mut(mptr, 1) };
+    //~^ cast_slice_from_raw_parts
+    let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1);
+    //~^ cast_slice_from_raw_parts
+    {
+        use core::slice;
+        let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1);
+        //~^ cast_slice_from_raw_parts
+        use slice as one;
+        let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1);
+        //~^ cast_slice_from_raw_parts
+    }
+    {
+        use core::slice;
+        let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1);
+        //~^ cast_slice_from_raw_parts
+        use slice as one;
+        let _: *const [u8] = core::ptr::slice_from_raw_parts(ptr, 1);
+        //~^ cast_slice_from_raw_parts
+    }
+
+    // implicit cast
+    {
+        let _: *const [u8] = unsafe { core::ptr::slice_from_raw_parts(ptr, 1) };
+        //~^ cast_slice_from_raw_parts
+        let _: *mut [u8] = unsafe { core::ptr::slice_from_raw_parts_mut(mptr, 1) };
+        //~^ cast_slice_from_raw_parts
+        require_raw_slice_ptr(unsafe { core::ptr::slice_from_raw_parts(ptr, 1) });
+        //~^ cast_slice_from_raw_parts
+    }
+
+    // implicit cast in const context
+    const {
+        const PTR: *const u8 = core::ptr::null();
+        const MPTR: *mut u8 = core::ptr::null_mut();
+        let _: *const [u8] = unsafe { core::ptr::slice_from_raw_parts(PTR, 1) };
+        //~^ cast_slice_from_raw_parts
+        let _: *mut [u8] = unsafe { core::ptr::slice_from_raw_parts_mut(MPTR, 1) };
+        //~^ cast_slice_from_raw_parts
+        require_raw_slice_ptr(unsafe { core::ptr::slice_from_raw_parts(PTR, 1) });
+        //~^ cast_slice_from_raw_parts
+    };
+}
diff --git a/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast_no_std.rs b/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast_no_std.rs
new file mode 100644
index 00000000000..743e44c97dc
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast_no_std.rs
@@ -0,0 +1,55 @@
+#![warn(clippy::cast_slice_from_raw_parts)]
+#![no_std]
+#![crate_type = "lib"]
+
+const fn require_raw_slice_ptr<T>(_: *const [T]) {}
+
+fn main() {
+    let mut arr = [0u8; 1];
+    let ptr: *const u8 = arr.as_ptr();
+    let mptr = arr.as_mut_ptr();
+    let _: *const [u8] = unsafe { core::slice::from_raw_parts(ptr, 1) as *const [u8] };
+    //~^ cast_slice_from_raw_parts
+    let _: *const [u8] = unsafe { core::slice::from_raw_parts_mut(mptr, 1) as *mut [u8] };
+    //~^ cast_slice_from_raw_parts
+    let _: *const [u8] = unsafe { core::slice::from_raw_parts(ptr, 1) } as *const [u8];
+    //~^ cast_slice_from_raw_parts
+    {
+        use core::slice;
+        let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8];
+        //~^ cast_slice_from_raw_parts
+        use slice as one;
+        let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8];
+        //~^ cast_slice_from_raw_parts
+    }
+    {
+        use core::slice;
+        let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8];
+        //~^ cast_slice_from_raw_parts
+        use slice as one;
+        let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8];
+        //~^ cast_slice_from_raw_parts
+    }
+
+    // implicit cast
+    {
+        let _: *const [u8] = unsafe { core::slice::from_raw_parts(ptr, 1) };
+        //~^ cast_slice_from_raw_parts
+        let _: *mut [u8] = unsafe { core::slice::from_raw_parts_mut(mptr, 1) };
+        //~^ cast_slice_from_raw_parts
+        require_raw_slice_ptr(unsafe { core::slice::from_raw_parts(ptr, 1) });
+        //~^ cast_slice_from_raw_parts
+    }
+
+    // implicit cast in const context
+    const {
+        const PTR: *const u8 = core::ptr::null();
+        const MPTR: *mut u8 = core::ptr::null_mut();
+        let _: *const [u8] = unsafe { core::slice::from_raw_parts(PTR, 1) };
+        //~^ cast_slice_from_raw_parts
+        let _: *mut [u8] = unsafe { core::slice::from_raw_parts_mut(MPTR, 1) };
+        //~^ cast_slice_from_raw_parts
+        require_raw_slice_ptr(unsafe { core::slice::from_raw_parts(PTR, 1) });
+        //~^ cast_slice_from_raw_parts
+    };
+}
diff --git a/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast_no_std.stderr b/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast_no_std.stderr
new file mode 100644
index 00000000000..5488fbcfa1c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cast_raw_slice_pointer_cast_no_std.stderr
@@ -0,0 +1,83 @@
+error: casting the result of `from_raw_parts` to *const [u8]
+  --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:11:35
+   |
+LL |     let _: *const [u8] = unsafe { core::slice::from_raw_parts(ptr, 1) as *const [u8] };
+   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)`
+   |
+   = note: `-D clippy::cast-slice-from-raw-parts` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::cast_slice_from_raw_parts)]`
+
+error: casting the result of `from_raw_parts_mut` to *mut [u8]
+  --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:13:35
+   |
+LL |     let _: *const [u8] = unsafe { core::slice::from_raw_parts_mut(mptr, 1) as *mut [u8] };
+   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts_mut(mptr, 1)`
+
+error: casting the result of `from_raw_parts` to *const [u8]
+  --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:15:26
+   |
+LL |     let _: *const [u8] = unsafe { core::slice::from_raw_parts(ptr, 1) } as *const [u8];
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)`
+
+error: casting the result of `from_raw_parts` to *const [u8]
+  --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:19:30
+   |
+LL |         let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8];
+   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)`
+
+error: casting the result of `from_raw_parts` to *const [u8]
+  --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:22:30
+   |
+LL |         let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8];
+   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)`
+
+error: casting the result of `from_raw_parts` to *const [u8]
+  --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:27:30
+   |
+LL |         let _: *const [u8] = unsafe { slice::from_raw_parts(ptr, 1) } as *const [u8];
+   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)`
+
+error: casting the result of `from_raw_parts` to *const [u8]
+  --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:30:30
+   |
+LL |         let _: *const [u8] = unsafe { one::from_raw_parts(ptr, 1) } as *const [u8];
+   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `core::ptr::slice_from_raw_parts(ptr, 1)`
+
+error: implicitly casting the result of `from_raw_parts` to `*const [u8]`
+  --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:36:39
+   |
+LL |         let _: *const [u8] = unsafe { core::slice::from_raw_parts(ptr, 1) };
+   |                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `core::ptr::slice_from_raw_parts(ptr, 1)`
+
+error: implicitly casting the result of `from_raw_parts_mut` to `*mut [u8]`
+  --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:38:37
+   |
+LL |         let _: *mut [u8] = unsafe { core::slice::from_raw_parts_mut(mptr, 1) };
+   |                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `core::ptr::slice_from_raw_parts_mut(mptr, 1)`
+
+error: implicitly casting the result of `from_raw_parts` to `*const [u8]`
+  --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:40:40
+   |
+LL |         require_raw_slice_ptr(unsafe { core::slice::from_raw_parts(ptr, 1) });
+   |                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `core::ptr::slice_from_raw_parts(ptr, 1)`
+
+error: implicitly casting the result of `from_raw_parts` to `*const [u8]`
+  --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:48:39
+   |
+LL |         let _: *const [u8] = unsafe { core::slice::from_raw_parts(PTR, 1) };
+   |                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `core::ptr::slice_from_raw_parts(PTR, 1)`
+
+error: implicitly casting the result of `from_raw_parts_mut` to `*mut [u8]`
+  --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:50:37
+   |
+LL |         let _: *mut [u8] = unsafe { core::slice::from_raw_parts_mut(MPTR, 1) };
+   |                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `core::ptr::slice_from_raw_parts_mut(MPTR, 1)`
+
+error: implicitly casting the result of `from_raw_parts` to `*const [u8]`
+  --> tests/ui/cast_raw_slice_pointer_cast_no_std.rs:52:40
+   |
+LL |         require_raw_slice_ptr(unsafe { core::slice::from_raw_parts(PTR, 1) });
+   |                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace_with: `core::ptr::slice_from_raw_parts(PTR, 1)`
+
+error: aborting due to 13 previous errors
+
diff --git a/src/tools/clippy/tests/ui/char_indices_as_byte_indices.fixed b/src/tools/clippy/tests/ui/char_indices_as_byte_indices.fixed
index 04c8f6782c5..375a101c2e3 100644
--- a/src/tools/clippy/tests/ui/char_indices_as_byte_indices.fixed
+++ b/src/tools/clippy/tests/ui/char_indices_as_byte_indices.fixed
@@ -1,4 +1,3 @@
-#![feature(round_char_boundary)]
 #![warn(clippy::char_indices_as_byte_indices)]
 
 trait StrExt {
diff --git a/src/tools/clippy/tests/ui/char_indices_as_byte_indices.rs b/src/tools/clippy/tests/ui/char_indices_as_byte_indices.rs
index 773a4fc65f1..eebc39962a2 100644
--- a/src/tools/clippy/tests/ui/char_indices_as_byte_indices.rs
+++ b/src/tools/clippy/tests/ui/char_indices_as_byte_indices.rs
@@ -1,4 +1,3 @@
-#![feature(round_char_boundary)]
 #![warn(clippy::char_indices_as_byte_indices)]
 
 trait StrExt {
diff --git a/src/tools/clippy/tests/ui/char_indices_as_byte_indices.stderr b/src/tools/clippy/tests/ui/char_indices_as_byte_indices.stderr
index e2b4c1db78c..fae81fd772d 100644
--- a/src/tools/clippy/tests/ui/char_indices_as_byte_indices.stderr
+++ b/src/tools/clippy/tests/ui/char_indices_as_byte_indices.stderr
@@ -1,12 +1,12 @@
 error: indexing into a string with a character position where a byte index is expected
-  --> tests/ui/char_indices_as_byte_indices.rs:13:24
+  --> tests/ui/char_indices_as_byte_indices.rs:12:24
    |
 LL |         let _ = prim[..idx];
    |                        ^^^
    |
    = note: a character can take up more than one byte, so they are not interchangeable
 note: position comes from the enumerate iterator
-  --> tests/ui/char_indices_as_byte_indices.rs:12:10
+  --> tests/ui/char_indices_as_byte_indices.rs:11:10
    |
 LL |     for (idx, _) in prim.chars().enumerate() {
    |          ^^^                     ^^^^^^^^^^^
@@ -19,14 +19,14 @@ LL +     for (idx, _) in prim.char_indices() {
    |
 
 error: passing a character position to a method that expects a byte index
-  --> tests/ui/char_indices_as_byte_indices.rs:15:23
+  --> tests/ui/char_indices_as_byte_indices.rs:14:23
    |
 LL |         prim.split_at(idx);
    |                       ^^^
    |
    = note: a character can take up more than one byte, so they are not interchangeable
 note: position comes from the enumerate iterator
-  --> tests/ui/char_indices_as_byte_indices.rs:12:10
+  --> tests/ui/char_indices_as_byte_indices.rs:11:10
    |
 LL |     for (idx, _) in prim.chars().enumerate() {
    |          ^^^                     ^^^^^^^^^^^
@@ -37,14 +37,14 @@ LL +     for (idx, _) in prim.char_indices() {
    |
 
 error: passing a character position to a method that expects a byte index
-  --> tests/ui/char_indices_as_byte_indices.rs:19:49
+  --> tests/ui/char_indices_as_byte_indices.rs:18:49
    |
 LL |         let _ = prim[..prim.floor_char_boundary(idx)];
    |                                                 ^^^
    |
    = note: a character can take up more than one byte, so they are not interchangeable
 note: position comes from the enumerate iterator
-  --> tests/ui/char_indices_as_byte_indices.rs:12:10
+  --> tests/ui/char_indices_as_byte_indices.rs:11:10
    |
 LL |     for (idx, _) in prim.chars().enumerate() {
    |          ^^^                     ^^^^^^^^^^^
@@ -55,14 +55,14 @@ LL +     for (idx, _) in prim.char_indices() {
    |
 
 error: indexing into a string with a character position where a byte index is expected
-  --> tests/ui/char_indices_as_byte_indices.rs:29:24
+  --> tests/ui/char_indices_as_byte_indices.rs:28:24
    |
 LL |         let _ = prim[..c.0];
    |                        ^^^
    |
    = note: a character can take up more than one byte, so they are not interchangeable
 note: position comes from the enumerate iterator
-  --> tests/ui/char_indices_as_byte_indices.rs:28:9
+  --> tests/ui/char_indices_as_byte_indices.rs:27:9
    |
 LL |     for c in prim.chars().enumerate() {
    |         ^                 ^^^^^^^^^^^
@@ -73,14 +73,14 @@ LL +     for c in prim.char_indices() {
    |
 
 error: passing a character position to a method that expects a byte index
-  --> tests/ui/char_indices_as_byte_indices.rs:31:23
+  --> tests/ui/char_indices_as_byte_indices.rs:30:23
    |
 LL |         prim.split_at(c.0);
    |                       ^^^
    |
    = note: a character can take up more than one byte, so they are not interchangeable
 note: position comes from the enumerate iterator
-  --> tests/ui/char_indices_as_byte_indices.rs:28:9
+  --> tests/ui/char_indices_as_byte_indices.rs:27:9
    |
 LL |     for c in prim.chars().enumerate() {
    |         ^                 ^^^^^^^^^^^
@@ -91,14 +91,14 @@ LL +     for c in prim.char_indices() {
    |
 
 error: indexing into a string with a character position where a byte index is expected
-  --> tests/ui/char_indices_as_byte_indices.rs:36:26
+  --> tests/ui/char_indices_as_byte_indices.rs:35:26
    |
 LL |         let _ = string[..idx];
    |                          ^^^
    |
    = note: a character can take up more than one byte, so they are not interchangeable
 note: position comes from the enumerate iterator
-  --> tests/ui/char_indices_as_byte_indices.rs:35:10
+  --> tests/ui/char_indices_as_byte_indices.rs:34:10
    |
 LL |     for (idx, _) in string.chars().enumerate() {
    |          ^^^                       ^^^^^^^^^^^
@@ -109,14 +109,14 @@ LL +     for (idx, _) in string.char_indices() {
    |
 
 error: passing a character position to a method that expects a byte index
-  --> tests/ui/char_indices_as_byte_indices.rs:38:25
+  --> tests/ui/char_indices_as_byte_indices.rs:37:25
    |
 LL |         string.split_at(idx);
    |                         ^^^
    |
    = note: a character can take up more than one byte, so they are not interchangeable
 note: position comes from the enumerate iterator
-  --> tests/ui/char_indices_as_byte_indices.rs:35:10
+  --> tests/ui/char_indices_as_byte_indices.rs:34:10
    |
 LL |     for (idx, _) in string.chars().enumerate() {
    |          ^^^                       ^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.fixed b/src/tools/clippy/tests/ui/char_lit_as_u8.fixed
index 64aacedfd36..64aacedfd36 100644
--- a/src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.fixed
+++ b/src/tools/clippy/tests/ui/char_lit_as_u8.fixed
diff --git a/src/tools/clippy/tests/ui/char_lit_as_u8.rs b/src/tools/clippy/tests/ui/char_lit_as_u8.rs
index c8774c7f309..a8f39e27605 100644
--- a/src/tools/clippy/tests/ui/char_lit_as_u8.rs
+++ b/src/tools/clippy/tests/ui/char_lit_as_u8.rs
@@ -1,7 +1,12 @@
 #![warn(clippy::char_lit_as_u8)]
 
 fn main() {
-    // no suggestion, since a byte literal won't work.
-    let _ = '❤' as u8;
+    let _ = 'a' as u8;
+    //~^ char_lit_as_u8
+    let _ = '\n' as u8;
+    //~^ char_lit_as_u8
+    let _ = '\0' as u8;
+    //~^ char_lit_as_u8
+    let _ = '\x01' as u8;
     //~^ char_lit_as_u8
 }
diff --git a/src/tools/clippy/tests/ui/char_lit_as_u8.stderr b/src/tools/clippy/tests/ui/char_lit_as_u8.stderr
index ec02f1341c0..9bcded7b0ff 100644
--- a/src/tools/clippy/tests/ui/char_lit_as_u8.stderr
+++ b/src/tools/clippy/tests/ui/char_lit_as_u8.stderr
@@ -1,12 +1,36 @@
 error: casting a character literal to `u8` truncates
-  --> tests/ui/char_lit_as_u8.rs:5:13
+  --> tests/ui/char_lit_as_u8.rs:4:13
    |
-LL |     let _ = '❤' as u8;
-   |             ^^^^^^^^^
+LL |     let _ = 'a' as u8;
+   |             ^^^^^^^^^ help: use a byte literal instead: `b'a'`
    |
    = note: `char` is four bytes wide, but `u8` is a single byte
    = note: `-D clippy::char-lit-as-u8` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::char_lit_as_u8)]`
 
-error: aborting due to 1 previous error
+error: casting a character literal to `u8` truncates
+  --> tests/ui/char_lit_as_u8.rs:6:13
+   |
+LL |     let _ = '\n' as u8;
+   |             ^^^^^^^^^^ help: use a byte literal instead: `b'\n'`
+   |
+   = note: `char` is four bytes wide, but `u8` is a single byte
+
+error: casting a character literal to `u8` truncates
+  --> tests/ui/char_lit_as_u8.rs:8:13
+   |
+LL |     let _ = '\0' as u8;
+   |             ^^^^^^^^^^ help: use a byte literal instead: `b'\0'`
+   |
+   = note: `char` is four bytes wide, but `u8` is a single byte
+
+error: casting a character literal to `u8` truncates
+  --> tests/ui/char_lit_as_u8.rs:10:13
+   |
+LL |     let _ = '\x01' as u8;
+   |             ^^^^^^^^^^^^ help: use a byte literal instead: `b'\x01'`
+   |
+   = note: `char` is four bytes wide, but `u8` is a single byte
+
+error: aborting due to 4 previous errors
 
diff --git a/src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.rs b/src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.rs
deleted file mode 100644
index a8f39e27605..00000000000
--- a/src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.rs
+++ /dev/null
@@ -1,12 +0,0 @@
-#![warn(clippy::char_lit_as_u8)]
-
-fn main() {
-    let _ = 'a' as u8;
-    //~^ char_lit_as_u8
-    let _ = '\n' as u8;
-    //~^ char_lit_as_u8
-    let _ = '\0' as u8;
-    //~^ char_lit_as_u8
-    let _ = '\x01' as u8;
-    //~^ char_lit_as_u8
-}
diff --git a/src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.stderr b/src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.stderr
deleted file mode 100644
index 158dfd6bed2..00000000000
--- a/src/tools/clippy/tests/ui/char_lit_as_u8_suggestions.stderr
+++ /dev/null
@@ -1,36 +0,0 @@
-error: casting a character literal to `u8` truncates
-  --> tests/ui/char_lit_as_u8_suggestions.rs:4:13
-   |
-LL |     let _ = 'a' as u8;
-   |             ^^^^^^^^^ help: use a byte literal instead: `b'a'`
-   |
-   = note: `char` is four bytes wide, but `u8` is a single byte
-   = note: `-D clippy::char-lit-as-u8` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::char_lit_as_u8)]`
-
-error: casting a character literal to `u8` truncates
-  --> tests/ui/char_lit_as_u8_suggestions.rs:6:13
-   |
-LL |     let _ = '\n' as u8;
-   |             ^^^^^^^^^^ help: use a byte literal instead: `b'\n'`
-   |
-   = note: `char` is four bytes wide, but `u8` is a single byte
-
-error: casting a character literal to `u8` truncates
-  --> tests/ui/char_lit_as_u8_suggestions.rs:8:13
-   |
-LL |     let _ = '\0' as u8;
-   |             ^^^^^^^^^^ help: use a byte literal instead: `b'\0'`
-   |
-   = note: `char` is four bytes wide, but `u8` is a single byte
-
-error: casting a character literal to `u8` truncates
-  --> tests/ui/char_lit_as_u8_suggestions.rs:10:13
-   |
-LL |     let _ = '\x01' as u8;
-   |             ^^^^^^^^^^^^ help: use a byte literal instead: `b'\x01'`
-   |
-   = note: `char` is four bytes wide, but `u8` is a single byte
-
-error: aborting due to 4 previous errors
-
diff --git a/src/tools/clippy/tests/ui/char_lit_as_u8_unfixable.rs b/src/tools/clippy/tests/ui/char_lit_as_u8_unfixable.rs
new file mode 100644
index 00000000000..e5c094f158e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/char_lit_as_u8_unfixable.rs
@@ -0,0 +1,8 @@
+//@no-rustfix
+#![warn(clippy::char_lit_as_u8)]
+
+fn main() {
+    // no suggestion, since a byte literal won't work.
+    let _ = '❤' as u8;
+    //~^ char_lit_as_u8
+}
diff --git a/src/tools/clippy/tests/ui/char_lit_as_u8_unfixable.stderr b/src/tools/clippy/tests/ui/char_lit_as_u8_unfixable.stderr
new file mode 100644
index 00000000000..49e555ae638
--- /dev/null
+++ b/src/tools/clippy/tests/ui/char_lit_as_u8_unfixable.stderr
@@ -0,0 +1,12 @@
+error: casting a character literal to `u8` truncates
+  --> tests/ui/char_lit_as_u8_unfixable.rs:6:13
+   |
+LL |     let _ = '❤' as u8;
+   |             ^^^^^^^^^
+   |
+   = note: `char` is four bytes wide, but `u8` is a single byte
+   = note: `-D clippy::char-lit-as-u8` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::char_lit_as_u8)]`
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs
index 785b2473c05..bba264080b4 100644
--- a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs
+++ b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs
@@ -273,6 +273,28 @@ const ISSUE14763: fn(Option<String>) = |x| {
     }
 };
 
+fn issue12295() {
+    let option = Some(());
+
+    if option.is_some() {
+        println!("{:?}", option.unwrap());
+        //~^ unnecessary_unwrap
+    } else {
+        println!("{:?}", option.unwrap());
+        //~^ panicking_unwrap
+    }
+
+    let result = Ok::<(), ()>(());
+
+    if result.is_ok() {
+        println!("{:?}", result.unwrap());
+        //~^ unnecessary_unwrap
+    } else {
+        println!("{:?}", result.unwrap());
+        //~^ panicking_unwrap
+    }
+}
+
 fn check_expect() {
     let x = Some(());
     if x.is_some() {
diff --git a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr
index 26e360112b6..2007a859541 100644
--- a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr
+++ b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr
@@ -322,6 +322,40 @@ LL |         if x.is_some() {
 LL |             _ = x.unwrap();
    |                 ^^^^^^^^^^
 
+error: called `unwrap` on `option` after checking its variant with `is_some`
+  --> tests/ui/checked_unwrap/simple_conditionals.rs:280:26
+   |
+LL |     if option.is_some() {
+   |     ------------------- help: try: `if let Some(<item>) = option`
+LL |         println!("{:?}", option.unwrap());
+   |                          ^^^^^^^^^^^^^^^
+
+error: this call to `unwrap()` will always panic
+  --> tests/ui/checked_unwrap/simple_conditionals.rs:283:26
+   |
+LL |     if option.is_some() {
+   |        ---------------- because of this check
+...
+LL |         println!("{:?}", option.unwrap());
+   |                          ^^^^^^^^^^^^^^^
+
+error: called `unwrap` on `result` after checking its variant with `is_ok`
+  --> tests/ui/checked_unwrap/simple_conditionals.rs:290:26
+   |
+LL |     if result.is_ok() {
+   |     ----------------- help: try: `if let Ok(<item>) = result`
+LL |         println!("{:?}", result.unwrap());
+   |                          ^^^^^^^^^^^^^^^
+
+error: this call to `unwrap()` will always panic
+  --> tests/ui/checked_unwrap/simple_conditionals.rs:293:26
+   |
+LL |     if result.is_ok() {
+   |        -------------- because of this check
+...
+LL |         println!("{:?}", result.unwrap());
+   |                          ^^^^^^^^^^^^^^^
+
 error: creating a shared reference to mutable static
   --> tests/ui/checked_unwrap/simple_conditionals.rs:183:12
    |
@@ -330,7 +364,7 @@ LL |         if X.is_some() {
    |
    = note: for more information, see <https://doc.rust-lang.org/edition-guide/rust-2024/static-mut-references.html>
    = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives
-   = note: `#[deny(static_mut_refs)]` on by default
+   = note: `#[deny(static_mut_refs)]` (part of `#[deny(rust_2024_compatibility)]`) on by default
 
-error: aborting due to 36 previous errors
+error: aborting due to 40 previous errors
 
diff --git a/src/tools/clippy/tests/ui/collapsible_match.rs b/src/tools/clippy/tests/ui/collapsible_match.rs
index 71b82040ff6..8931a3aa09c 100644
--- a/src/tools/clippy/tests/ui/collapsible_match.rs
+++ b/src/tools/clippy/tests/ui/collapsible_match.rs
@@ -316,6 +316,47 @@ fn lint_emitted_at_right_node(opt: Option<Result<u64, String>>) {
     };
 }
 
+pub fn issue_14155() {
+    let mut arr = ["a", "b", "c"];
+    if let Some(last) = arr.last() {
+        match *last {
+            //~^ collapsible_match
+            "a" | "b" => {
+                unimplemented!()
+            },
+            _ => (),
+        }
+    }
+
+    if let Some(last) = arr.last() {
+        match &last {
+            //~^ collapsible_match
+            &&"a" | &&"b" => {
+                unimplemented!()
+            },
+            _ => (),
+        }
+    }
+
+    if let Some(mut last) = arr.last_mut() {
+        match &mut last {
+            //~^ collapsible_match
+            &mut &mut "a" | &mut &mut "b" => {
+                unimplemented!()
+            },
+            _ => (),
+        }
+    }
+
+    const NULL_PTR: *const &'static str = std::ptr::null();
+    if let Some(last) = arr.last() {
+        match &raw const *last {
+            NULL_PTR => unimplemented!(),
+            _ => (),
+        }
+    }
+}
+
 fn make<T>() -> T {
     unimplemented!()
 }
diff --git a/src/tools/clippy/tests/ui/collapsible_match.stderr b/src/tools/clippy/tests/ui/collapsible_match.stderr
index c290d84ec29..14b1c1b187e 100644
--- a/src/tools/clippy/tests/ui/collapsible_match.stderr
+++ b/src/tools/clippy/tests/ui/collapsible_match.stderr
@@ -250,5 +250,74 @@ LL |     if let Issue9647::A { a: Some(a), .. } = x {
 LL |         if let Some(u) = a {
    |                ^^^^^^^ with this pattern
 
-error: aborting due to 13 previous errors
+error: this `match` can be collapsed into the outer `if let`
+  --> tests/ui/collapsible_match.rs:322:9
+   |
+LL | /         match *last {
+LL | |
+LL | |             "a" | "b" => {
+LL | |                 unimplemented!()
+LL | |             },
+LL | |             _ => (),
+LL | |         }
+   | |_________^
+   |
+help: the outer pattern can be modified to include the inner pattern
+  --> tests/ui/collapsible_match.rs:321:17
+   |
+LL |     if let Some(last) = arr.last() {
+   |                 ^^^^    ---------- use: `arr.last().copied()`
+   |                 |
+   |                 replace this binding
+...
+LL |             "a" | "b" => {
+   |             ^^^^^^^^^ with this pattern
+
+error: this `match` can be collapsed into the outer `if let`
+  --> tests/ui/collapsible_match.rs:332:9
+   |
+LL | /         match &last {
+LL | |
+LL | |             &&"a" | &&"b" => {
+LL | |                 unimplemented!()
+LL | |             },
+LL | |             _ => (),
+LL | |         }
+   | |_________^
+   |
+help: the outer pattern can be modified to include the inner pattern
+  --> tests/ui/collapsible_match.rs:331:17
+   |
+LL |     if let Some(last) = arr.last() {
+   |                 ^^^^    ---------- use: `arr.last().as_ref()`
+   |                 |
+   |                 replace this binding
+...
+LL |             &&"a" | &&"b" => {
+   |             ^^^^^^^^^^^^^ with this pattern
+
+error: this `match` can be collapsed into the outer `if let`
+  --> tests/ui/collapsible_match.rs:342:9
+   |
+LL | /         match &mut last {
+LL | |
+LL | |             &mut &mut "a" | &mut &mut "b" => {
+LL | |                 unimplemented!()
+LL | |             },
+LL | |             _ => (),
+LL | |         }
+   | |_________^
+   |
+help: the outer pattern can be modified to include the inner pattern
+  --> tests/ui/collapsible_match.rs:341:17
+   |
+LL |     if let Some(mut last) = arr.last_mut() {
+   |                 ^^^^^^^^    -------------- use: `arr.last_mut().as_mut()`
+   |                 |
+   |                 replace this binding
+...
+LL |             &mut &mut "a" | &mut &mut "b" => {
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ with this pattern
+
+error: aborting due to 16 previous errors
 
diff --git a/src/tools/clippy/tests/ui/collapsible_match2.stderr b/src/tools/clippy/tests/ui/collapsible_match2.stderr
index 7b273063752..25970670999 100644
--- a/src/tools/clippy/tests/ui/collapsible_match2.stderr
+++ b/src/tools/clippy/tests/ui/collapsible_match2.stderr
@@ -77,6 +77,8 @@ LL | |         },
 help: the outer pattern can be modified to include the inner pattern
   --> tests/ui/collapsible_match2.rs:54:14
    |
+LL |     match Some(&[1]) {
+   |           ---------- use: `Some(&[1]).copied()`
 LL |         Some(s) => match *s {
    |              ^ replace this binding
 LL |
diff --git a/src/tools/clippy/tests/ui/crashes/ice-15657.rs b/src/tools/clippy/tests/ui/crashes/ice-15657.rs
new file mode 100644
index 00000000000..c6f6506cd8d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-15657.rs
@@ -0,0 +1,11 @@
+//@check-pass
+#![warn(clippy::len_zero)]
+
+pub struct S1;
+pub struct S2;
+
+impl S1 {
+    pub fn len(&self) -> S2 {
+        S2
+    }
+}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-15666.fixed b/src/tools/clippy/tests/ui/crashes/ice-15666.fixed
new file mode 100644
index 00000000000..53b618765c0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-15666.fixed
@@ -0,0 +1,6 @@
+#![warn(clippy::elidable_lifetime_names)]
+
+struct UnitVariantAccess<'a, 'b, 's>(&'a &'b &'s ());
+trait Trait<'de> {}
+impl<'de> Trait<'de> for UnitVariantAccess<'_, 'de, '_> {}
+//~^ elidable_lifetime_names
diff --git a/src/tools/clippy/tests/ui/crashes/ice-15666.rs b/src/tools/clippy/tests/ui/crashes/ice-15666.rs
new file mode 100644
index 00000000000..1414b3d2035
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-15666.rs
@@ -0,0 +1,6 @@
+#![warn(clippy::elidable_lifetime_names)]
+
+struct UnitVariantAccess<'a, 'b, 's>(&'a &'b &'s ());
+trait Trait<'de> {}
+impl<'de, 'a, 's> Trait<'de> for UnitVariantAccess<'a, 'de, 's> {}
+//~^ elidable_lifetime_names
diff --git a/src/tools/clippy/tests/ui/crashes/ice-15666.stderr b/src/tools/clippy/tests/ui/crashes/ice-15666.stderr
new file mode 100644
index 00000000000..b417c09b5c6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/crashes/ice-15666.stderr
@@ -0,0 +1,16 @@
+error: the following explicit lifetimes could be elided: 'a, 's
+  --> tests/ui/crashes/ice-15666.rs:5:11
+   |
+LL | impl<'de, 'a, 's> Trait<'de> for UnitVariantAccess<'a, 'de, 's> {}
+   |           ^^  ^^                                   ^^       ^^
+   |
+   = note: `-D clippy::elidable-lifetime-names` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::elidable_lifetime_names)]`
+help: elide the lifetimes
+   |
+LL - impl<'de, 'a, 's> Trait<'de> for UnitVariantAccess<'a, 'de, 's> {}
+LL + impl<'de> Trait<'de> for UnitVariantAccess<'_, 'de, '_> {}
+   |
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/clippy/tests/ui/crashes/ice-6255.rs b/src/tools/clippy/tests/ui/crashes/ice-6255.rs
index ef1e01f80ef..5f31696c791 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-6255.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-6255.rs
@@ -9,7 +9,7 @@ macro_rules! define_other_core {
 }
 
 fn main() {
-    core::panic!();
+    core::panic!(); //~ ERROR: `core` is ambiguous
 }
 
 define_other_core!();
diff --git a/src/tools/clippy/tests/ui/crashes/ice-6255.stderr b/src/tools/clippy/tests/ui/crashes/ice-6255.stderr
index 738e9d1bd5c..420e4af936f 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-6255.stderr
+++ b/src/tools/clippy/tests/ui/crashes/ice-6255.stderr
@@ -9,5 +9,25 @@ LL | define_other_core!();
    |
    = note: this error originates in the macro `define_other_core` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: aborting due to 1 previous error
+error[E0659]: `core` is ambiguous
+  --> tests/ui/crashes/ice-6255.rs:12:5
+   |
+LL |     core::panic!();
+   |     ^^^^ ambiguous name
+   |
+   = note: ambiguous because of a conflict between a macro-expanded name and a less macro-expanded name from outer scope during import or macro resolution
+   = note: `core` could refer to a built-in crate
+note: `core` could also refer to the crate imported here
+  --> tests/ui/crashes/ice-6255.rs:6:9
+   |
+LL |         extern crate std as core;
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | define_other_core!();
+   | -------------------- in this macro invocation
+   = help: use `crate::core` to refer to this crate unambiguously
+   = note: this error originates in the macro `define_other_core` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 2 previous errors
 
+For more information about this error, try `rustc --explain E0659`.
diff --git a/src/tools/clippy/tests/ui/crashes/ice-96721.stderr b/src/tools/clippy/tests/ui/crashes/ice-96721.stderr
index f0778a4b32b..23f7300178e 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-96721.stderr
+++ b/src/tools/clippy/tests/ui/crashes/ice-96721.stderr
@@ -3,6 +3,8 @@ error: malformed `path` attribute input
    |
 LL | #[path = foo!()]
    | ^^^^^^^^^^^^^^^^ help: must be of the form: `#[path = "file"]`
+   |
+   = note: for more information, visit <https://doc.rust-lang.org/reference/items/modules.html#the-path-attribute>
 
 error: aborting due to 1 previous error
 
diff --git a/src/tools/clippy/tests/ui/deref_addrof.fixed b/src/tools/clippy/tests/ui/deref_addrof.fixed
index 35dbd790e89..ffe7f7d1440 100644
--- a/src/tools/clippy/tests/ui/deref_addrof.fixed
+++ b/src/tools/clippy/tests/ui/deref_addrof.fixed
@@ -1,11 +1,11 @@
-//@aux-build:proc_macros.rs
-
-#![allow(clippy::return_self_not_must_use, clippy::useless_vec)]
+#![allow(
+    dangerous_implicit_autorefs,
+    clippy::explicit_auto_deref,
+    clippy::return_self_not_must_use,
+    clippy::useless_vec
+)]
 #![warn(clippy::deref_addrof)]
 
-extern crate proc_macros;
-use proc_macros::inline_macros;
-
 fn get_number() -> usize {
     10
 }
@@ -56,19 +56,75 @@ fn main() {
     //~^ deref_addrof
     // do NOT lint for array as semantic differences with/out `*&`.
     let _arr = *&[0, 1, 2, 3, 4];
+
+    // Do not lint when text comes from macro
+    macro_rules! mac {
+        (dr) => {
+            *&0
+        };
+        (dr $e:expr) => {
+            *&$e
+        };
+        (r $e:expr) => {
+            &$e
+        };
+    }
+    let b = mac!(dr);
+    let b = mac!(dr a);
+    let b = *mac!(r a);
 }
 
-#[derive(Copy, Clone)]
-pub struct S;
-#[inline_macros]
-impl S {
-    pub fn f(&self) -> &Self {
-        inline!($(@expr self))
-        //~^ deref_addrof
+fn issue14386() {
+    use std::mem::ManuallyDrop;
+
+    #[derive(Copy, Clone)]
+    struct Data {
+        num: u64,
     }
-    #[allow(unused_mut)] // mut will be unused, once the macro is fixed
-    pub fn f_mut(mut self) -> Self {
-        inline!($(@expr self))
+
+    #[derive(Clone, Copy)]
+    struct M {
+        md: ManuallyDrop<[u8; 4]>,
+    }
+
+    union DataWithPadding<'lt> {
+        data: ManuallyDrop<Data>,
+        prim: ManuallyDrop<u64>,
+        padding: [u8; size_of::<Data>()],
+        tup: (ManuallyDrop<Data>, ()),
+        indirect: M,
+        indirect_arr: [M; 2],
+        indirect_ref: &'lt mut M,
+    }
+
+    let mut a = DataWithPadding {
+        padding: [0; size_of::<DataWithPadding>()],
+    };
+    unsafe {
+        a.padding = [1; size_of::<DataWithPadding>()];
         //~^ deref_addrof
+        a.tup.1 = ();
+        //~^ deref_addrof
+        *a.prim = 0;
+        //~^ deref_addrof
+
+        (*a.data).num = 42;
+        //~^ deref_addrof
+        (*a.indirect.md)[3] = 1;
+        //~^ deref_addrof
+        (*a.indirect_arr[1].md)[3] = 1;
+        //~^ deref_addrof
+        (*a.indirect_ref.md)[3] = 1;
+        //~^ deref_addrof
+
+        // Check that raw pointers are properly considered as well
+        *a.prim = 0;
+        //~^ deref_addrof
+        (*a.data).num = 42;
+        //~^ deref_addrof
+
+        // Do not lint, as the dereference happens later, we cannot
+        // just remove `&mut`
+        (*&mut a.tup).0.num = 42;
     }
 }
diff --git a/src/tools/clippy/tests/ui/deref_addrof.rs b/src/tools/clippy/tests/ui/deref_addrof.rs
index 96d1b92ef7b..bc253716aff 100644
--- a/src/tools/clippy/tests/ui/deref_addrof.rs
+++ b/src/tools/clippy/tests/ui/deref_addrof.rs
@@ -1,11 +1,11 @@
-//@aux-build:proc_macros.rs
-
-#![allow(clippy::return_self_not_must_use, clippy::useless_vec)]
+#![allow(
+    dangerous_implicit_autorefs,
+    clippy::explicit_auto_deref,
+    clippy::return_self_not_must_use,
+    clippy::useless_vec
+)]
 #![warn(clippy::deref_addrof)]
 
-extern crate proc_macros;
-use proc_macros::inline_macros;
-
 fn get_number() -> usize {
     10
 }
@@ -56,19 +56,75 @@ fn main() {
     //~^ deref_addrof
     // do NOT lint for array as semantic differences with/out `*&`.
     let _arr = *&[0, 1, 2, 3, 4];
+
+    // Do not lint when text comes from macro
+    macro_rules! mac {
+        (dr) => {
+            *&0
+        };
+        (dr $e:expr) => {
+            *&$e
+        };
+        (r $e:expr) => {
+            &$e
+        };
+    }
+    let b = mac!(dr);
+    let b = mac!(dr a);
+    let b = *mac!(r a);
 }
 
-#[derive(Copy, Clone)]
-pub struct S;
-#[inline_macros]
-impl S {
-    pub fn f(&self) -> &Self {
-        inline!(*& $(@expr self))
-        //~^ deref_addrof
+fn issue14386() {
+    use std::mem::ManuallyDrop;
+
+    #[derive(Copy, Clone)]
+    struct Data {
+        num: u64,
     }
-    #[allow(unused_mut)] // mut will be unused, once the macro is fixed
-    pub fn f_mut(mut self) -> Self {
-        inline!(*&mut $(@expr self))
+
+    #[derive(Clone, Copy)]
+    struct M {
+        md: ManuallyDrop<[u8; 4]>,
+    }
+
+    union DataWithPadding<'lt> {
+        data: ManuallyDrop<Data>,
+        prim: ManuallyDrop<u64>,
+        padding: [u8; size_of::<Data>()],
+        tup: (ManuallyDrop<Data>, ()),
+        indirect: M,
+        indirect_arr: [M; 2],
+        indirect_ref: &'lt mut M,
+    }
+
+    let mut a = DataWithPadding {
+        padding: [0; size_of::<DataWithPadding>()],
+    };
+    unsafe {
+        (*&mut a.padding) = [1; size_of::<DataWithPadding>()];
         //~^ deref_addrof
+        (*&mut a.tup).1 = ();
+        //~^ deref_addrof
+        **&mut a.prim = 0;
+        //~^ deref_addrof
+
+        (*&mut a.data).num = 42;
+        //~^ deref_addrof
+        (*&mut a.indirect.md)[3] = 1;
+        //~^ deref_addrof
+        (*&mut a.indirect_arr[1].md)[3] = 1;
+        //~^ deref_addrof
+        (*&mut a.indirect_ref.md)[3] = 1;
+        //~^ deref_addrof
+
+        // Check that raw pointers are properly considered as well
+        **&raw mut a.prim = 0;
+        //~^ deref_addrof
+        (*&raw mut a.data).num = 42;
+        //~^ deref_addrof
+
+        // Do not lint, as the dereference happens later, we cannot
+        // just remove `&mut`
+        (*&mut a.tup).0.num = 42;
     }
 }
diff --git a/src/tools/clippy/tests/ui/deref_addrof.stderr b/src/tools/clippy/tests/ui/deref_addrof.stderr
index 81414b625b2..65dd904a8f7 100644
--- a/src/tools/clippy/tests/ui/deref_addrof.stderr
+++ b/src/tools/clippy/tests/ui/deref_addrof.stderr
@@ -56,20 +56,58 @@ LL |     let _repeat = *&[0; 64];
    |                   ^^^^^^^^^ help: try: `[0; 64]`
 
 error: immediately dereferencing a reference
-  --> tests/ui/deref_addrof.rs:66:17
+  --> tests/ui/deref_addrof.rs:104:9
    |
-LL |         inline!(*& $(@expr self))
-   |                 ^^^^^^^^^^^^^^^^ help: try: `$(@expr self)`
+LL |         (*&mut a.padding) = [1; size_of::<DataWithPadding>()];
+   |         ^^^^^^^^^^^^^^^^^ help: try: `a.padding`
+
+error: immediately dereferencing a reference
+  --> tests/ui/deref_addrof.rs:106:9
+   |
+LL |         (*&mut a.tup).1 = ();
+   |         ^^^^^^^^^^^^^ help: try: `a.tup`
+
+error: immediately dereferencing a reference
+  --> tests/ui/deref_addrof.rs:108:10
    |
-   = note: this error originates in the macro `__inline_mac_impl` (in Nightly builds, run with -Z macro-backtrace for more info)
+LL |         **&mut a.prim = 0;
+   |          ^^^^^^^^^^^^ help: try: `a.prim`
 
 error: immediately dereferencing a reference
-  --> tests/ui/deref_addrof.rs:71:17
+  --> tests/ui/deref_addrof.rs:111:9
    |
-LL |         inline!(*&mut $(@expr self))
-   |                 ^^^^^^^^^^^^^^^^^^^ help: try: `$(@expr self)`
+LL |         (*&mut a.data).num = 42;
+   |         ^^^^^^^^^^^^^^ help: try: `(*a.data)`
+
+error: immediately dereferencing a reference
+  --> tests/ui/deref_addrof.rs:113:9
+   |
+LL |         (*&mut a.indirect.md)[3] = 1;
+   |         ^^^^^^^^^^^^^^^^^^^^^ help: try: `(*a.indirect.md)`
+
+error: immediately dereferencing a reference
+  --> tests/ui/deref_addrof.rs:115:9
+   |
+LL |         (*&mut a.indirect_arr[1].md)[3] = 1;
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(*a.indirect_arr[1].md)`
+
+error: immediately dereferencing a reference
+  --> tests/ui/deref_addrof.rs:117:9
+   |
+LL |         (*&mut a.indirect_ref.md)[3] = 1;
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(*a.indirect_ref.md)`
+
+error: immediately dereferencing a reference
+  --> tests/ui/deref_addrof.rs:121:10
+   |
+LL |         **&raw mut a.prim = 0;
+   |          ^^^^^^^^^^^^^^^^ help: try: `a.prim`
+
+error: immediately dereferencing a reference
+  --> tests/ui/deref_addrof.rs:123:9
    |
-   = note: this error originates in the macro `__inline_mac_impl` (in Nightly builds, run with -Z macro-backtrace for more info)
+LL |         (*&raw mut a.data).num = 42;
+   |         ^^^^^^^^^^^^^^^^^^ help: try: `(*a.data)`
 
-error: aborting due to 11 previous errors
+error: aborting due to 18 previous errors
 
diff --git a/src/tools/clippy/tests/ui/derivable_impls.fixed b/src/tools/clippy/tests/ui/derivable_impls.fixed
index 65bfded3883..9f9e4e253c3 100644
--- a/src/tools/clippy/tests/ui/derivable_impls.fixed
+++ b/src/tools/clippy/tests/ui/derivable_impls.fixed
@@ -1,4 +1,6 @@
 #![allow(dead_code)]
+#![feature(const_trait_impl)]
+#![feature(const_default)]
 
 use std::collections::HashMap;
 
@@ -326,4 +328,40 @@ mod issue11368 {
     }
 }
 
+mod issue15493 {
+    #[derive(Copy, Clone)]
+    #[repr(transparent)]
+    struct Foo(u64);
+
+    impl const Default for Foo {
+        fn default() -> Self {
+            Self(0)
+        }
+    }
+
+    #[derive(Copy, Clone)]
+    enum Bar {
+        A,
+        B,
+    }
+
+    impl const Default for Bar {
+        fn default() -> Self {
+            Bar::A
+        }
+    }
+}
+
+mod issue15536 {
+    #[derive(Copy, Clone)]
+    #[derive(Default)]
+    enum Bar {
+        #[default]
+        A,
+        B,
+    }
+
+    
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/derivable_impls.rs b/src/tools/clippy/tests/ui/derivable_impls.rs
index 4826c5497b4..74a793b9a70 100644
--- a/src/tools/clippy/tests/ui/derivable_impls.rs
+++ b/src/tools/clippy/tests/ui/derivable_impls.rs
@@ -1,4 +1,6 @@
 #![allow(dead_code)]
+#![feature(const_trait_impl)]
+#![feature(const_default)]
 
 use std::collections::HashMap;
 
@@ -396,4 +398,43 @@ mod issue11368 {
     }
 }
 
+mod issue15493 {
+    #[derive(Copy, Clone)]
+    #[repr(transparent)]
+    struct Foo(u64);
+
+    impl const Default for Foo {
+        fn default() -> Self {
+            Self(0)
+        }
+    }
+
+    #[derive(Copy, Clone)]
+    enum Bar {
+        A,
+        B,
+    }
+
+    impl const Default for Bar {
+        fn default() -> Self {
+            Bar::A
+        }
+    }
+}
+
+mod issue15536 {
+    #[derive(Copy, Clone)]
+    enum Bar {
+        A,
+        B,
+    }
+
+    impl Default for Bar {
+        //~^ derivable_impls
+        fn default() -> Self {
+            Self::A
+        }
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/derivable_impls.stderr b/src/tools/clippy/tests/ui/derivable_impls.stderr
index 0f73ad55a85..cd46414cb4a 100644
--- a/src/tools/clippy/tests/ui/derivable_impls.stderr
+++ b/src/tools/clippy/tests/ui/derivable_impls.stderr
@@ -1,5 +1,5 @@
 error: this `impl` can be derived
-  --> tests/ui/derivable_impls.rs:20:1
+  --> tests/ui/derivable_impls.rs:22:1
    |
 LL | / impl std::default::Default for FooDefault<'_> {
 LL | |
@@ -18,7 +18,7 @@ LL ~ struct FooDefault<'a> {
    |
 
 error: this `impl` can be derived
-  --> tests/ui/derivable_impls.rs:42:1
+  --> tests/ui/derivable_impls.rs:44:1
    |
 LL | / impl std::default::Default for TupleDefault {
 LL | |
@@ -35,7 +35,7 @@ LL ~ struct TupleDefault(bool, i32, u64);
    |
 
 error: this `impl` can be derived
-  --> tests/ui/derivable_impls.rs:95:1
+  --> tests/ui/derivable_impls.rs:97:1
    |
 LL | / impl Default for StrDefault<'_> {
 LL | |
@@ -52,7 +52,7 @@ LL ~ struct StrDefault<'a>(&'a str);
    |
 
 error: this `impl` can be derived
-  --> tests/ui/derivable_impls.rs:122:1
+  --> tests/ui/derivable_impls.rs:124:1
    |
 LL | / impl Default for Y {
 LL | |
@@ -69,7 +69,7 @@ LL ~ struct Y(u32);
    |
 
 error: this `impl` can be derived
-  --> tests/ui/derivable_impls.rs:162:1
+  --> tests/ui/derivable_impls.rs:164:1
    |
 LL | / impl Default for WithoutSelfCurly {
 LL | |
@@ -86,7 +86,7 @@ LL ~ struct WithoutSelfCurly {
    |
 
 error: this `impl` can be derived
-  --> tests/ui/derivable_impls.rs:171:1
+  --> tests/ui/derivable_impls.rs:173:1
    |
 LL | / impl Default for WithoutSelfParan {
 LL | |
@@ -103,7 +103,7 @@ LL ~ struct WithoutSelfParan(bool);
    |
 
 error: this `impl` can be derived
-  --> tests/ui/derivable_impls.rs:194:1
+  --> tests/ui/derivable_impls.rs:196:1
    |
 LL | / impl Default for DirectDefaultDefaultCall {
 LL | |
@@ -119,7 +119,7 @@ LL ~ pub struct DirectDefaultDefaultCall {
    |
 
 error: this `impl` can be derived
-  --> tests/ui/derivable_impls.rs:206:1
+  --> tests/ui/derivable_impls.rs:208:1
    |
 LL | / impl Default for EquivalentToDefaultDefaultCallVec {
 LL | |
@@ -135,7 +135,7 @@ LL ~ pub struct EquivalentToDefaultDefaultCallVec {
    |
 
 error: this `impl` can be derived
-  --> tests/ui/derivable_impls.rs:234:1
+  --> tests/ui/derivable_impls.rs:236:1
    |
 LL | / impl Default for EquivalentToDefaultDefaultCallLocal {
 LL | |
@@ -151,7 +151,7 @@ LL ~ pub struct EquivalentToDefaultDefaultCallLocal {
    |
 
 error: this `impl` can be derived
-  --> tests/ui/derivable_impls.rs:274:1
+  --> tests/ui/derivable_impls.rs:276:1
    |
 LL | / impl Default for RepeatDefault1 {
 LL | |
@@ -168,7 +168,7 @@ LL ~ pub struct RepeatDefault1 {
    |
 
 error: this `impl` can be derived
-  --> tests/ui/derivable_impls.rs:309:1
+  --> tests/ui/derivable_impls.rs:311:1
    |
 LL | / impl Default for SimpleEnum {
 LL | |
@@ -187,5 +187,28 @@ LL ~     #[default]
 LL ~     Bar,
    |
 
-error: aborting due to 11 previous errors
+error: this `impl` can be derived
+  --> tests/ui/derivable_impls.rs:432:5
+   |
+LL | /     impl Default for Bar {
+LL | |
+LL | |         fn default() -> Self {
+LL | |             Self::A
+LL | |         }
+LL | |     }
+   | |_____^
+   |
+help: replace the manual implementation with a derive attribute and mark the default variant
+   |
+LL ~     #[derive(Default)]
+LL ~     enum Bar {
+LL ~         #[default]
+LL ~         A,
+LL |         B,
+LL |     }
+LL |
+LL ~     
+   |
+
+error: aborting due to 12 previous errors
 
diff --git a/src/tools/clippy/tests/ui/derivable_impls_derive_const.fixed b/src/tools/clippy/tests/ui/derivable_impls_derive_const.fixed
new file mode 100644
index 00000000000..f0d8d2d2409
--- /dev/null
+++ b/src/tools/clippy/tests/ui/derivable_impls_derive_const.fixed
@@ -0,0 +1,25 @@
+#![allow(dead_code)]
+#![feature(const_trait_impl)]
+#![feature(const_default)]
+#![feature(derive_const)]
+
+mod issue15493 {
+    #[derive(Copy, Clone)]
+    #[repr(transparent)]
+    #[derive_const(Default)]
+    struct Foo(u64);
+
+    
+
+    #[derive(Copy, Clone)]
+    #[derive_const(Default)]
+    enum Bar {
+        #[default]
+        A,
+        B,
+    }
+
+    
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/derivable_impls_derive_const.rs b/src/tools/clippy/tests/ui/derivable_impls_derive_const.rs
new file mode 100644
index 00000000000..7d70db1c097
--- /dev/null
+++ b/src/tools/clippy/tests/ui/derivable_impls_derive_const.rs
@@ -0,0 +1,32 @@
+#![allow(dead_code)]
+#![feature(const_trait_impl)]
+#![feature(const_default)]
+#![feature(derive_const)]
+
+mod issue15493 {
+    #[derive(Copy, Clone)]
+    #[repr(transparent)]
+    struct Foo(u64);
+
+    impl const Default for Foo {
+        //~^ derivable_impls
+        fn default() -> Self {
+            Self(0)
+        }
+    }
+
+    #[derive(Copy, Clone)]
+    enum Bar {
+        A,
+        B,
+    }
+
+    impl const Default for Bar {
+        //~^ derivable_impls
+        fn default() -> Self {
+            Bar::A
+        }
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/derivable_impls_derive_const.stderr b/src/tools/clippy/tests/ui/derivable_impls_derive_const.stderr
new file mode 100644
index 00000000000..196bac185dd
--- /dev/null
+++ b/src/tools/clippy/tests/ui/derivable_impls_derive_const.stderr
@@ -0,0 +1,46 @@
+error: this `impl` can be derived
+  --> tests/ui/derivable_impls_derive_const.rs:11:5
+   |
+LL | /     impl const Default for Foo {
+LL | |
+LL | |         fn default() -> Self {
+LL | |             Self(0)
+LL | |         }
+LL | |     }
+   | |_____^
+   |
+   = note: `-D clippy::derivable-impls` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::derivable_impls)]`
+help: replace the manual implementation with a derive attribute
+   |
+LL ~     #[derive_const(Default)]
+LL ~     struct Foo(u64);
+LL |
+LL ~     
+   |
+
+error: this `impl` can be derived
+  --> tests/ui/derivable_impls_derive_const.rs:24:5
+   |
+LL | /     impl const Default for Bar {
+LL | |
+LL | |         fn default() -> Self {
+LL | |             Bar::A
+LL | |         }
+LL | |     }
+   | |_____^
+   |
+help: replace the manual implementation with a derive attribute and mark the default variant
+   |
+LL ~     #[derive_const(Default)]
+LL ~     enum Bar {
+LL ~         #[default]
+LL ~         A,
+LL |         B,
+LL |     }
+LL |
+LL ~     
+   |
+
+error: aborting due to 2 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 bbbd5973036..46695dc929a 100644
--- a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed
+++ b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed
@@ -73,8 +73,9 @@ fn test_units() {
 /// GPLv2 GPLv3
 /// GitHub GitLab
 /// IPv4 IPv6
+/// InfiniBand RoCE
 /// ClojureScript CoffeeScript JavaScript PostScript PureScript TypeScript
-/// WebAssembly
+/// PowerPC WebAssembly
 /// NaN NaNs
 /// OAuth GraphQL
 /// OCaml
diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.rs b/src/tools/clippy/tests/ui/doc/doc-fixable.rs
index 1077d3580d3..4082fa5b56f 100644
--- a/src/tools/clippy/tests/ui/doc/doc-fixable.rs
+++ b/src/tools/clippy/tests/ui/doc/doc-fixable.rs
@@ -73,8 +73,9 @@ fn test_units() {
 /// GPLv2 GPLv3
 /// GitHub GitLab
 /// IPv4 IPv6
+/// InfiniBand RoCE
 /// ClojureScript CoffeeScript JavaScript PostScript PureScript TypeScript
-/// WebAssembly
+/// PowerPC WebAssembly
 /// NaN NaNs
 /// OAuth GraphQL
 /// OCaml
diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.stderr b/src/tools/clippy/tests/ui/doc/doc-fixable.stderr
index 98c26e6bec2..2a94a8f3165 100644
--- a/src/tools/clippy/tests/ui/doc/doc-fixable.stderr
+++ b/src/tools/clippy/tests/ui/doc/doc-fixable.stderr
@@ -145,7 +145,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it`
    |
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:90:5
+  --> tests/ui/doc/doc-fixable.rs:91:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -157,7 +157,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it`
    |
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:108:5
+  --> tests/ui/doc/doc-fixable.rs:109:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -169,7 +169,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it`
    |
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:117:8
+  --> tests/ui/doc/doc-fixable.rs:118:8
    |
 LL | /// ## CamelCaseThing
    |        ^^^^^^^^^^^^^^
@@ -181,7 +181,7 @@ LL + /// ## `CamelCaseThing`
    |
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:121:7
+  --> tests/ui/doc/doc-fixable.rs:122:7
    |
 LL | /// # CamelCaseThing
    |       ^^^^^^^^^^^^^^
@@ -193,7 +193,7 @@ LL + /// # `CamelCaseThing`
    |
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:124:22
+  --> tests/ui/doc/doc-fixable.rs:125:22
    |
 LL | /// Not a title #897 CamelCaseThing
    |                      ^^^^^^^^^^^^^^
@@ -205,7 +205,7 @@ LL + /// Not a title #897 `CamelCaseThing`
    |
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:126:5
+  --> tests/ui/doc/doc-fixable.rs:127:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -217,7 +217,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it`
    |
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:134:5
+  --> tests/ui/doc/doc-fixable.rs:135:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -229,7 +229,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it`
    |
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:148:5
+  --> tests/ui/doc/doc-fixable.rs:149:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -241,7 +241,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it`
    |
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:160:43
+  --> tests/ui/doc/doc-fixable.rs:161:43
    |
 LL | /** E.g., serialization of an empty list: FooBar
    |                                           ^^^^^^
@@ -253,7 +253,7 @@ LL + /** E.g., serialization of an empty list: `FooBar`
    |
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:165:5
+  --> tests/ui/doc/doc-fixable.rs:166:5
    |
 LL | And BarQuz too.
    |     ^^^^^^
@@ -265,7 +265,7 @@ LL + And `BarQuz` too.
    |
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:166:1
+  --> tests/ui/doc/doc-fixable.rs:167:1
    |
 LL | be_sure_we_got_to_the_end_of_it
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -277,7 +277,7 @@ LL + `be_sure_we_got_to_the_end_of_it`
    |
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:174:43
+  --> tests/ui/doc/doc-fixable.rs:175:43
    |
 LL | /** E.g., serialization of an empty list: FooBar
    |                                           ^^^^^^
@@ -289,7 +289,7 @@ LL + /** E.g., serialization of an empty list: `FooBar`
    |
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:179:5
+  --> tests/ui/doc/doc-fixable.rs:180:5
    |
 LL | And BarQuz too.
    |     ^^^^^^
@@ -301,7 +301,7 @@ LL + And `BarQuz` too.
    |
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:180:1
+  --> tests/ui/doc/doc-fixable.rs:181:1
    |
 LL | be_sure_we_got_to_the_end_of_it
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -313,7 +313,7 @@ LL + `be_sure_we_got_to_the_end_of_it`
    |
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:194:5
+  --> tests/ui/doc/doc-fixable.rs:195:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -325,7 +325,7 @@ LL + /// `be_sure_we_got_to_the_end_of_it`
    |
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:214:22
+  --> tests/ui/doc/doc-fixable.rs:215:22
    |
 LL | /// An iterator over mycrate::Collection's values.
    |                      ^^^^^^^^^^^^^^^^^^^
@@ -337,7 +337,7 @@ LL + /// An iterator over `mycrate::Collection`'s values.
    |
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:239:34
+  --> tests/ui/doc/doc-fixable.rs:240:34
    |
 LL | /// Foo \[bar\] \[baz\] \[qux\]. DocMarkdownLint
    |                                  ^^^^^^^^^^^^^^^
@@ -349,7 +349,7 @@ LL + /// Foo \[bar\] \[baz\] \[qux\]. `DocMarkdownLint`
    |
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:263:22
+  --> tests/ui/doc/doc-fixable.rs:264:22
    |
 LL | /// There is no try (do() or do_not()).
    |                      ^^^^
@@ -361,7 +361,7 @@ LL + /// There is no try (`do()` or do_not()).
    |
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:263:30
+  --> tests/ui/doc/doc-fixable.rs:264:30
    |
 LL | /// There is no try (do() or do_not()).
    |                              ^^^^^^^^
@@ -373,7 +373,7 @@ LL + /// There is no try (do() or `do_not()`).
    |
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:268:5
+  --> tests/ui/doc/doc-fixable.rs:269:5
    |
 LL | /// ABes
    |     ^^^^
@@ -385,7 +385,7 @@ LL + /// `ABes`
    |
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:275:9
+  --> tests/ui/doc/doc-fixable.rs:276:9
    |
 LL |     /// foo()
    |         ^^^^^
@@ -397,7 +397,7 @@ LL +     /// `foo()`
    |
 
 error: you should put bare URLs between `<`/`>` or make a proper Markdown link
-  --> tests/ui/doc/doc-fixable.rs:280:5
+  --> tests/ui/doc/doc-fixable.rs:281: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/double_ended_iterator_last.fixed b/src/tools/clippy/tests/ui/double_ended_iterator_last.fixed
index be31ee5fb48..180a513d0f8 100644
--- a/src/tools/clippy/tests/ui/double_ended_iterator_last.fixed
+++ b/src/tools/clippy/tests/ui/double_ended_iterator_last.fixed
@@ -81,6 +81,11 @@ fn issue_14139() {
     let (subindex, _) = (index.by_ref().take(3), 42);
     let _ = subindex.last();
     let _ = index.next();
+
+    let mut index = [true, true, false, false, false, true].iter();
+    let subindex = (index.by_ref().take(3), 42);
+    let _ = subindex.0.last();
+    let _ = index.next();
 }
 
 fn drop_order() {
@@ -108,6 +113,12 @@ fn drop_order() {
     let mut v = DropDeIterator(v.into_iter());
     println!("Last element is {}", v.next_back().unwrap().0);
     //~^ ERROR: called `Iterator::last` on a `DoubleEndedIterator`
+
+    let v = vec![S("four"), S("five"), S("six")];
+    let mut v = (DropDeIterator(v.into_iter()), 42);
+    println!("Last element is {}", v.0.next_back().unwrap().0);
+    //~^ ERROR: called `Iterator::last` on a `DoubleEndedIterator`
+
     println!("Done");
 }
 
diff --git a/src/tools/clippy/tests/ui/double_ended_iterator_last.rs b/src/tools/clippy/tests/ui/double_ended_iterator_last.rs
index 30864e15bce..3dd72cfeaac 100644
--- a/src/tools/clippy/tests/ui/double_ended_iterator_last.rs
+++ b/src/tools/clippy/tests/ui/double_ended_iterator_last.rs
@@ -81,6 +81,11 @@ fn issue_14139() {
     let (subindex, _) = (index.by_ref().take(3), 42);
     let _ = subindex.last();
     let _ = index.next();
+
+    let mut index = [true, true, false, false, false, true].iter();
+    let subindex = (index.by_ref().take(3), 42);
+    let _ = subindex.0.last();
+    let _ = index.next();
 }
 
 fn drop_order() {
@@ -108,6 +113,12 @@ fn drop_order() {
     let v = DropDeIterator(v.into_iter());
     println!("Last element is {}", v.last().unwrap().0);
     //~^ ERROR: called `Iterator::last` on a `DoubleEndedIterator`
+
+    let v = vec![S("four"), S("five"), S("six")];
+    let v = (DropDeIterator(v.into_iter()), 42);
+    println!("Last element is {}", v.0.last().unwrap().0);
+    //~^ ERROR: called `Iterator::last` on a `DoubleEndedIterator`
+
     println!("Done");
 }
 
diff --git a/src/tools/clippy/tests/ui/double_ended_iterator_last.stderr b/src/tools/clippy/tests/ui/double_ended_iterator_last.stderr
index 72a6ead47a9..0f0056be376 100644
--- a/src/tools/clippy/tests/ui/double_ended_iterator_last.stderr
+++ b/src/tools/clippy/tests/ui/double_ended_iterator_last.stderr
@@ -18,7 +18,7 @@ LL |     let _ = DeIterator.last();
    |                        help: try: `next_back()`
 
 error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator
-  --> tests/ui/double_ended_iterator_last.rs:109:36
+  --> tests/ui/double_ended_iterator_last.rs:114:36
    |
 LL |     println!("Last element is {}", v.last().unwrap().0);
    |                                    ^^^^^^^^
@@ -30,5 +30,18 @@ LL ~     let mut v = DropDeIterator(v.into_iter());
 LL ~     println!("Last element is {}", v.next_back().unwrap().0);
    |
 
-error: aborting due to 3 previous errors
+error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator
+  --> tests/ui/double_ended_iterator_last.rs:119:36
+   |
+LL |     println!("Last element is {}", v.0.last().unwrap().0);
+   |                                    ^^^^^^^^^^
+   |
+   = note: this change will alter drop order which may be undesirable
+help: try
+   |
+LL ~     let mut v = (DropDeIterator(v.into_iter()), 42);
+LL ~     println!("Last element is {}", v.0.next_back().unwrap().0);
+   |
+
+error: aborting due to 4 previous errors
 
diff --git a/src/tools/clippy/tests/ui/double_ended_iterator_last_unfixable.rs b/src/tools/clippy/tests/ui/double_ended_iterator_last_unfixable.rs
deleted file mode 100644
index 73f62ac1246..00000000000
--- a/src/tools/clippy/tests/ui/double_ended_iterator_last_unfixable.rs
+++ /dev/null
@@ -1,39 +0,0 @@
-//@no-rustfix: requires manual changes
-#![warn(clippy::double_ended_iterator_last)]
-
-// Should not be linted because applying the lint would move the original iterator. This can only be
-// linted if the iterator is used thereafter.
-fn main() {
-    let mut index = [true, true, false, false, false, true].iter();
-    let subindex = (index.by_ref().take(3), 42);
-    let _ = subindex.0.last();
-    let _ = index.next();
-}
-
-fn drop_order() {
-    struct DropDeIterator(std::vec::IntoIter<S>);
-    impl Iterator for DropDeIterator {
-        type Item = S;
-        fn next(&mut self) -> Option<Self::Item> {
-            self.0.next()
-        }
-    }
-    impl DoubleEndedIterator for DropDeIterator {
-        fn next_back(&mut self) -> Option<Self::Item> {
-            self.0.next_back()
-        }
-    }
-
-    struct S(&'static str);
-    impl std::ops::Drop for S {
-        fn drop(&mut self) {
-            println!("Dropping {}", self.0);
-        }
-    }
-
-    let v = vec![S("one"), S("two"), S("three")];
-    let v = (DropDeIterator(v.into_iter()), 42);
-    println!("Last element is {}", v.0.last().unwrap().0);
-    //~^ ERROR: called `Iterator::last` on a `DoubleEndedIterator`
-    println!("Done");
-}
diff --git a/src/tools/clippy/tests/ui/double_ended_iterator_last_unfixable.stderr b/src/tools/clippy/tests/ui/double_ended_iterator_last_unfixable.stderr
deleted file mode 100644
index e330a22a354..00000000000
--- a/src/tools/clippy/tests/ui/double_ended_iterator_last_unfixable.stderr
+++ /dev/null
@@ -1,19 +0,0 @@
-error: called `Iterator::last` on a `DoubleEndedIterator`; this will needlessly iterate the entire iterator
-  --> tests/ui/double_ended_iterator_last_unfixable.rs:36:36
-   |
-LL |     println!("Last element is {}", v.0.last().unwrap().0);
-   |                                    ^^^^------
-   |                                        |
-   |                                        help: try: `next_back()`
-   |
-   = note: this change will alter drop order which may be undesirable
-note: this must be made mutable to use `.next_back()`
-  --> tests/ui/double_ended_iterator_last_unfixable.rs:36:36
-   |
-LL |     println!("Last element is {}", v.0.last().unwrap().0);
-   |                                    ^^^
-   = note: `-D clippy::double-ended-iterator-last` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::double_ended_iterator_last)]`
-
-error: aborting due to 1 previous error
-
diff --git a/src/tools/clippy/tests/ui/elidable_lifetime_names.fixed b/src/tools/clippy/tests/ui/elidable_lifetime_names.fixed
index abeee5c4cef..a6c4cb7a36a 100644
--- a/src/tools/clippy/tests/ui/elidable_lifetime_names.fixed
+++ b/src/tools/clippy/tests/ui/elidable_lifetime_names.fixed
@@ -192,3 +192,85 @@ mod issue13923 {
         x.b
     }
 }
+
+fn issue15666_original() {
+    struct UnitVariantAccess<'a, 'b, 's>(&'a &'b &'s ());
+
+    trait Trait<'de> {}
+
+    //~v elidable_lifetime_names
+    impl<'de> Trait<'de> for UnitVariantAccess<'_, 'de, '_> {}
+    //        ^^  ^^                                   ^^       ^^
+}
+
+#[allow(clippy::upper_case_acronyms)]
+fn issue15666() {
+    struct S1<'a>(&'a ());
+    struct S2<'a, 'b>(&'a &'b ());
+    struct S3<'a, 'b, 'c>(&'a &'b &'c ());
+
+    trait T {}
+    trait TA<'a> {}
+    trait TB<'b> {}
+    trait TC<'c> {}
+    trait TAB<'a, 'b> {}
+    trait TAC<'a, 'c> {}
+    trait TBC<'b, 'c> {}
+    trait TABC<'a, 'b, 'c> {}
+
+    // 1 lifetime
+
+    impl<'a> TA<'a> for S1<'a> {}
+
+    //~v elidable_lifetime_names
+    impl T for S1<'_> {}
+    //   ^^
+
+    // 2 lifetimes
+
+    impl<'a, 'b> TAB<'a, 'b> for S2<'a, 'b> {}
+
+    //~v elidable_lifetime_names
+    impl<'a> TA<'a> for S2<'a, '_> {}
+    //       ^^
+
+    //~v elidable_lifetime_names
+    impl<'b> TB<'b> for S2<'_, 'b> {}
+    //   ^^
+
+    //~v elidable_lifetime_names
+    impl T for S2<'_, '_> {}
+    //   ^^  ^^
+
+    // 3 lifetimes
+
+    impl<'a, 'b, 'c> TABC<'a, 'b, 'c> for S3<'a, 'b, 'c> {}
+
+    //~v elidable_lifetime_names
+    impl<'a, 'b> TAB<'a, 'b> for S3<'a, 'b, '_> {}
+    //           ^^
+
+    //~v elidable_lifetime_names
+    impl<'a, 'c> TAC<'a, 'c> for S3<'a, '_, 'c> {}
+    //       ^^
+
+    //~v elidable_lifetime_names
+    impl<'a> TA<'a> for S3<'a, '_, '_> {}
+    //       ^^  ^^
+
+    //~v elidable_lifetime_names
+    impl<'b, 'c> TBC<'b, 'c> for S3<'_, 'b, 'c> {}
+    //   ^^
+
+    //~v elidable_lifetime_names
+    impl<'b> TB<'b> for S3<'_, 'b, '_> {}
+    //   ^^      ^^
+
+    //~v elidable_lifetime_names
+    impl<'c> TC<'c> for S3<'_, '_, 'c> {}
+    //   ^^  ^^
+
+    //~v elidable_lifetime_names
+    impl T for S3<'_, '_, '_> {}
+    //   ^^  ^^  ^^
+}
diff --git a/src/tools/clippy/tests/ui/elidable_lifetime_names.rs b/src/tools/clippy/tests/ui/elidable_lifetime_names.rs
index fae3577a8e9..e08056b2fb5 100644
--- a/src/tools/clippy/tests/ui/elidable_lifetime_names.rs
+++ b/src/tools/clippy/tests/ui/elidable_lifetime_names.rs
@@ -192,3 +192,85 @@ mod issue13923 {
         x.b
     }
 }
+
+fn issue15666_original() {
+    struct UnitVariantAccess<'a, 'b, 's>(&'a &'b &'s ());
+
+    trait Trait<'de> {}
+
+    //~v elidable_lifetime_names
+    impl<'de, 'a, 's> Trait<'de> for UnitVariantAccess<'a, 'de, 's> {}
+    //        ^^  ^^                                   ^^       ^^
+}
+
+#[allow(clippy::upper_case_acronyms)]
+fn issue15666() {
+    struct S1<'a>(&'a ());
+    struct S2<'a, 'b>(&'a &'b ());
+    struct S3<'a, 'b, 'c>(&'a &'b &'c ());
+
+    trait T {}
+    trait TA<'a> {}
+    trait TB<'b> {}
+    trait TC<'c> {}
+    trait TAB<'a, 'b> {}
+    trait TAC<'a, 'c> {}
+    trait TBC<'b, 'c> {}
+    trait TABC<'a, 'b, 'c> {}
+
+    // 1 lifetime
+
+    impl<'a> TA<'a> for S1<'a> {}
+
+    //~v elidable_lifetime_names
+    impl<'a> T for S1<'a> {}
+    //   ^^
+
+    // 2 lifetimes
+
+    impl<'a, 'b> TAB<'a, 'b> for S2<'a, 'b> {}
+
+    //~v elidable_lifetime_names
+    impl<'a, 'b> TA<'a> for S2<'a, 'b> {}
+    //       ^^
+
+    //~v elidable_lifetime_names
+    impl<'a, 'b> TB<'b> for S2<'a, 'b> {}
+    //   ^^
+
+    //~v elidable_lifetime_names
+    impl<'a, 'b> T for S2<'a, 'b> {}
+    //   ^^  ^^
+
+    // 3 lifetimes
+
+    impl<'a, 'b, 'c> TABC<'a, 'b, 'c> for S3<'a, 'b, 'c> {}
+
+    //~v elidable_lifetime_names
+    impl<'a, 'b, 'c> TAB<'a, 'b> for S3<'a, 'b, 'c> {}
+    //           ^^
+
+    //~v elidable_lifetime_names
+    impl<'a, 'b, 'c> TAC<'a, 'c> for S3<'a, 'b, 'c> {}
+    //       ^^
+
+    //~v elidable_lifetime_names
+    impl<'a, 'b, 'c> TA<'a> for S3<'a, 'b, 'c> {}
+    //       ^^  ^^
+
+    //~v elidable_lifetime_names
+    impl<'a, 'b, 'c> TBC<'b, 'c> for S3<'a, 'b, 'c> {}
+    //   ^^
+
+    //~v elidable_lifetime_names
+    impl<'a, 'b, 'c> TB<'b> for S3<'a, 'b, 'c> {}
+    //   ^^      ^^
+
+    //~v elidable_lifetime_names
+    impl<'a, 'b, 'c> TC<'c> for S3<'a, 'b, 'c> {}
+    //   ^^  ^^
+
+    //~v elidable_lifetime_names
+    impl<'a, 'b, 'c> T for S3<'a, 'b, 'c> {}
+    //   ^^  ^^  ^^
+}
diff --git a/src/tools/clippy/tests/ui/elidable_lifetime_names.stderr b/src/tools/clippy/tests/ui/elidable_lifetime_names.stderr
index a60dfc69756..03fe383b8f6 100644
--- a/src/tools/clippy/tests/ui/elidable_lifetime_names.stderr
+++ b/src/tools/clippy/tests/ui/elidable_lifetime_names.stderr
@@ -158,5 +158,149 @@ LL |             o: &'t str,
 LL ~         ) -> Content<'t, '_> {
    |
 
-error: aborting due to 12 previous errors
+error: the following explicit lifetimes could be elided: 'a, 's
+  --> tests/ui/elidable_lifetime_names.rs:202:15
+   |
+LL |     impl<'de, 'a, 's> Trait<'de> for UnitVariantAccess<'a, 'de, 's> {}
+   |               ^^  ^^                                   ^^       ^^
+   |
+help: elide the lifetimes
+   |
+LL -     impl<'de, 'a, 's> Trait<'de> for UnitVariantAccess<'a, 'de, 's> {}
+LL +     impl<'de> Trait<'de> for UnitVariantAccess<'_, 'de, '_> {}
+   |
+
+error: the following explicit lifetimes could be elided: 'a
+  --> tests/ui/elidable_lifetime_names.rs:226:10
+   |
+LL |     impl<'a> T for S1<'a> {}
+   |          ^^           ^^
+   |
+help: elide the lifetimes
+   |
+LL -     impl<'a> T for S1<'a> {}
+LL +     impl T for S1<'_> {}
+   |
+
+error: the following explicit lifetimes could be elided: 'b
+  --> tests/ui/elidable_lifetime_names.rs:234:14
+   |
+LL |     impl<'a, 'b> TA<'a> for S2<'a, 'b> {}
+   |              ^^                    ^^
+   |
+help: elide the lifetimes
+   |
+LL -     impl<'a, 'b> TA<'a> for S2<'a, 'b> {}
+LL +     impl<'a> TA<'a> for S2<'a, '_> {}
+   |
+
+error: the following explicit lifetimes could be elided: 'a
+  --> tests/ui/elidable_lifetime_names.rs:238:10
+   |
+LL |     impl<'a, 'b> TB<'b> for S2<'a, 'b> {}
+   |          ^^                    ^^
+   |
+help: elide the lifetimes
+   |
+LL -     impl<'a, 'b> TB<'b> for S2<'a, 'b> {}
+LL +     impl<'b> TB<'b> for S2<'_, 'b> {}
+   |
+
+error: the following explicit lifetimes could be elided: 'a, 'b
+  --> tests/ui/elidable_lifetime_names.rs:242:10
+   |
+LL |     impl<'a, 'b> T for S2<'a, 'b> {}
+   |          ^^  ^^           ^^  ^^
+   |
+help: elide the lifetimes
+   |
+LL -     impl<'a, 'b> T for S2<'a, 'b> {}
+LL +     impl T for S2<'_, '_> {}
+   |
+
+error: the following explicit lifetimes could be elided: 'c
+  --> tests/ui/elidable_lifetime_names.rs:250:18
+   |
+LL |     impl<'a, 'b, 'c> TAB<'a, 'b> for S3<'a, 'b, 'c> {}
+   |                  ^^                             ^^
+   |
+help: elide the lifetimes
+   |
+LL -     impl<'a, 'b, 'c> TAB<'a, 'b> for S3<'a, 'b, 'c> {}
+LL +     impl<'a, 'b> TAB<'a, 'b> for S3<'a, 'b, '_> {}
+   |
+
+error: the following explicit lifetimes could be elided: 'b
+  --> tests/ui/elidable_lifetime_names.rs:254:14
+   |
+LL |     impl<'a, 'b, 'c> TAC<'a, 'c> for S3<'a, 'b, 'c> {}
+   |              ^^                             ^^
+   |
+help: elide the lifetimes
+   |
+LL -     impl<'a, 'b, 'c> TAC<'a, 'c> for S3<'a, 'b, 'c> {}
+LL +     impl<'a, 'c> TAC<'a, 'c> for S3<'a, '_, 'c> {}
+   |
+
+error: the following explicit lifetimes could be elided: 'b, 'c
+  --> tests/ui/elidable_lifetime_names.rs:258:14
+   |
+LL |     impl<'a, 'b, 'c> TA<'a> for S3<'a, 'b, 'c> {}
+   |              ^^  ^^                    ^^  ^^
+   |
+help: elide the lifetimes
+   |
+LL -     impl<'a, 'b, 'c> TA<'a> for S3<'a, 'b, 'c> {}
+LL +     impl<'a> TA<'a> for S3<'a, '_, '_> {}
+   |
+
+error: the following explicit lifetimes could be elided: 'a
+  --> tests/ui/elidable_lifetime_names.rs:262:10
+   |
+LL |     impl<'a, 'b, 'c> TBC<'b, 'c> for S3<'a, 'b, 'c> {}
+   |          ^^                             ^^
+   |
+help: elide the lifetimes
+   |
+LL -     impl<'a, 'b, 'c> TBC<'b, 'c> for S3<'a, 'b, 'c> {}
+LL +     impl<'b, 'c> TBC<'b, 'c> for S3<'_, 'b, 'c> {}
+   |
+
+error: the following explicit lifetimes could be elided: 'a, 'c
+  --> tests/ui/elidable_lifetime_names.rs:266:10
+   |
+LL |     impl<'a, 'b, 'c> TB<'b> for S3<'a, 'b, 'c> {}
+   |          ^^      ^^                ^^      ^^
+   |
+help: elide the lifetimes
+   |
+LL -     impl<'a, 'b, 'c> TB<'b> for S3<'a, 'b, 'c> {}
+LL +     impl<'b> TB<'b> for S3<'_, 'b, '_> {}
+   |
+
+error: the following explicit lifetimes could be elided: 'a, 'b
+  --> tests/ui/elidable_lifetime_names.rs:270:10
+   |
+LL |     impl<'a, 'b, 'c> TC<'c> for S3<'a, 'b, 'c> {}
+   |          ^^  ^^                    ^^  ^^
+   |
+help: elide the lifetimes
+   |
+LL -     impl<'a, 'b, 'c> TC<'c> for S3<'a, 'b, 'c> {}
+LL +     impl<'c> TC<'c> for S3<'_, '_, 'c> {}
+   |
+
+error: the following explicit lifetimes could be elided: 'a, 'b, 'c
+  --> tests/ui/elidable_lifetime_names.rs:274:10
+   |
+LL |     impl<'a, 'b, 'c> T for S3<'a, 'b, 'c> {}
+   |          ^^  ^^  ^^           ^^  ^^  ^^
+   |
+help: elide the lifetimes
+   |
+LL -     impl<'a, 'b, 'c> T for S3<'a, 'b, 'c> {}
+LL +     impl T for S3<'_, '_, '_> {}
+   |
+
+error: aborting due to 24 previous errors
 
diff --git a/src/tools/clippy/tests/ui/entry_unfixable.stderr b/src/tools/clippy/tests/ui/entry_unfixable.stderr
index 0197d2ab4cf..f92f472512f 100644
--- a/src/tools/clippy/tests/ui/entry_unfixable.stderr
+++ b/src/tools/clippy/tests/ui/entry_unfixable.stderr
@@ -10,6 +10,7 @@ LL | |                 false
 LL | |             }
    | |_____________^
    |
+   = help: consider using the `Entry` API: https://doc.rust-lang.org/std/collections/struct.HashMap.html#entry-api
    = note: `-D clippy::map-entry` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::map_entry)]`
 
@@ -24,6 +25,8 @@ LL | |     } else {
 LL | |         hm.insert(key, true);
 LL | |     }
    | |_____^
+   |
+   = help: consider using the `Entry` API: https://doc.rust-lang.org/std/collections/struct.HashMap.html#entry-api
 
 error: usage of `contains_key` followed by `insert` on a `HashMap`
   --> tests/ui/entry_unfixable.rs:80:13
@@ -36,6 +39,8 @@ LL | |                 let interner = INTERNER.lock().unwrap();
 LL | |                 return Err(interner.resolve(name).unwrap().to_owned());
 LL | |             }
    | |_____________^
+   |
+   = help: consider using the `Entry` API: https://doc.rust-lang.org/std/collections/struct.HashMap.html#entry-api
 
 error: aborting due to 3 previous errors
 
diff --git a/src/tools/clippy/tests/ui/eta.fixed b/src/tools/clippy/tests/ui/eta.fixed
index c93b83f53ec..107318e5323 100644
--- a/src/tools/clippy/tests/ui/eta.fixed
+++ b/src/tools/clippy/tests/ui/eta.fixed
@@ -1,3 +1,6 @@
+// we have some HELP annotations -- don't complain about them not being present everywhere
+//@require-annotations-for-level: ERROR
+
 #![warn(clippy::redundant_closure, clippy::redundant_closure_for_method_calls)]
 #![allow(unused)]
 #![allow(
@@ -561,3 +564,82 @@ fn issue_14789() {
         std::convert::identity,
     );
 }
+
+fn issue_15072() {
+    use std::ops::Deref;
+
+    struct Foo;
+    impl Deref for Foo {
+        type Target = fn() -> &'static str;
+
+        fn deref(&self) -> &Self::Target {
+            fn hello() -> &'static str {
+                "Hello, world!"
+            }
+            &(hello as fn() -> &'static str)
+        }
+    }
+
+    fn accepts_fn(f: impl Fn() -> &'static str) {
+        println!("{}", f());
+    }
+
+    fn some_fn() -> &'static str {
+        todo!()
+    }
+
+    let f = &Foo;
+    accepts_fn(**f);
+    //~^ redundant_closure
+
+    let g = &some_fn;
+    accepts_fn(g);
+    //~^ redundant_closure
+
+    struct Bar(Foo);
+    impl Deref for Bar {
+        type Target = Foo;
+
+        fn deref(&self) -> &Self::Target {
+            &self.0
+        }
+    }
+
+    let b = &Bar(Foo);
+    accepts_fn(***b);
+    //~^ redundant_closure
+}
+
+fn issue8817() {
+    fn f(_: u32) -> u32 {
+        todo!()
+    }
+    let g = |_: u32| -> u32 { todo!() };
+    struct S(u32);
+    enum MyError {
+        A(S),
+    }
+
+    Some(5)
+        .map(f)
+        //~^ redundant_closure
+        //~| HELP: replace the closure with the function itself
+        .map(g)
+        //~^ redundant_closure
+        //~| HELP: replace the closure with the function itself
+        .map(S)
+        //~^ redundant_closure
+        //~| HELP: replace the closure with the tuple struct itself
+        .map(MyError::A)
+        //~^ redundant_closure
+        //~| HELP: replace the closure with the tuple variant itself
+        .unwrap(); // just for nicer formatting
+}
+
+async fn issue13892<'a, T, F>(maybe: Option<&'a T>, visitor: F)
+where
+    F: AsyncFn(&'a T),
+    T: 'a,
+{
+    maybe.map(|x| visitor(x));
+}
diff --git a/src/tools/clippy/tests/ui/eta.rs b/src/tools/clippy/tests/ui/eta.rs
index 273c8b21f4a..b85e8e75153 100644
--- a/src/tools/clippy/tests/ui/eta.rs
+++ b/src/tools/clippy/tests/ui/eta.rs
@@ -1,3 +1,6 @@
+// we have some HELP annotations -- don't complain about them not being present everywhere
+//@require-annotations-for-level: ERROR
+
 #![warn(clippy::redundant_closure, clippy::redundant_closure_for_method_calls)]
 #![allow(unused)]
 #![allow(
@@ -561,3 +564,82 @@ fn issue_14789() {
         std::convert::identity,
     );
 }
+
+fn issue_15072() {
+    use std::ops::Deref;
+
+    struct Foo;
+    impl Deref for Foo {
+        type Target = fn() -> &'static str;
+
+        fn deref(&self) -> &Self::Target {
+            fn hello() -> &'static str {
+                "Hello, world!"
+            }
+            &(hello as fn() -> &'static str)
+        }
+    }
+
+    fn accepts_fn(f: impl Fn() -> &'static str) {
+        println!("{}", f());
+    }
+
+    fn some_fn() -> &'static str {
+        todo!()
+    }
+
+    let f = &Foo;
+    accepts_fn(|| f());
+    //~^ redundant_closure
+
+    let g = &some_fn;
+    accepts_fn(|| g());
+    //~^ redundant_closure
+
+    struct Bar(Foo);
+    impl Deref for Bar {
+        type Target = Foo;
+
+        fn deref(&self) -> &Self::Target {
+            &self.0
+        }
+    }
+
+    let b = &Bar(Foo);
+    accepts_fn(|| b());
+    //~^ redundant_closure
+}
+
+fn issue8817() {
+    fn f(_: u32) -> u32 {
+        todo!()
+    }
+    let g = |_: u32| -> u32 { todo!() };
+    struct S(u32);
+    enum MyError {
+        A(S),
+    }
+
+    Some(5)
+        .map(|n| f(n))
+        //~^ redundant_closure
+        //~| HELP: replace the closure with the function itself
+        .map(|n| g(n))
+        //~^ redundant_closure
+        //~| HELP: replace the closure with the function itself
+        .map(|n| S(n))
+        //~^ redundant_closure
+        //~| HELP: replace the closure with the tuple struct itself
+        .map(|n| MyError::A(n))
+        //~^ redundant_closure
+        //~| HELP: replace the closure with the tuple variant itself
+        .unwrap(); // just for nicer formatting
+}
+
+async fn issue13892<'a, T, F>(maybe: Option<&'a T>, visitor: F)
+where
+    F: AsyncFn(&'a T),
+    T: 'a,
+{
+    maybe.map(|x| visitor(x));
+}
diff --git a/src/tools/clippy/tests/ui/eta.stderr b/src/tools/clippy/tests/ui/eta.stderr
index 8bc08add2fa..0b401cdea98 100644
--- a/src/tools/clippy/tests/ui/eta.stderr
+++ b/src/tools/clippy/tests/ui/eta.stderr
@@ -1,5 +1,5 @@
 error: redundant closure
-  --> tests/ui/eta.rs:31:27
+  --> tests/ui/eta.rs:34:27
    |
 LL |     let a = Some(1u8).map(|a| foo(a));
    |                           ^^^^^^^^^^ help: replace the closure with the function itself: `foo`
@@ -8,31 +8,31 @@ LL |     let a = Some(1u8).map(|a| foo(a));
    = help: to override `-D warnings` add `#[allow(clippy::redundant_closure)]`
 
 error: redundant closure
-  --> tests/ui/eta.rs:36:40
+  --> tests/ui/eta.rs:39:40
    |
 LL |     let _: Option<Vec<u8>> = true.then(|| vec![]); // special case vec!
    |                                        ^^^^^^^^^ help: replace the closure with `Vec::new`: `std::vec::Vec::new`
 
 error: redundant closure
-  --> tests/ui/eta.rs:39:35
+  --> tests/ui/eta.rs:42:35
    |
 LL |     let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted?
    |                                   ^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo2`
 
 error: redundant closure
-  --> tests/ui/eta.rs:42:26
+  --> tests/ui/eta.rs:45:26
    |
 LL |     all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted
    |                          ^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `below`
 
 error: redundant closure
-  --> tests/ui/eta.rs:51:27
+  --> tests/ui/eta.rs:54:27
    |
 LL |     let e = Some(1u8).map(|a| generic(a));
    |                           ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `generic`
 
 error: redundant closure
-  --> tests/ui/eta.rs:104:51
+  --> tests/ui/eta.rs:107:51
    |
 LL |     let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo());
    |                                                   ^^^^^^^^^^^ help: replace the closure with the method itself: `TestStruct::foo`
@@ -41,178 +41,220 @@ LL |     let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo());
    = help: to override `-D warnings` add `#[allow(clippy::redundant_closure_for_method_calls)]`
 
 error: redundant closure
-  --> tests/ui/eta.rs:106:51
+  --> tests/ui/eta.rs:109:51
    |
 LL |     let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo());
    |                                                   ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `TestTrait::trait_foo`
 
 error: redundant closure
-  --> tests/ui/eta.rs:109:42
+  --> tests/ui/eta.rs:112:42
    |
 LL |     let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear());
    |                                          ^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::vec::Vec::clear`
 
 error: redundant closure
-  --> tests/ui/eta.rs:114:29
+  --> tests/ui/eta.rs:117:29
    |
 LL |     let e = Some("str").map(|s| s.to_string());
    |                             ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::string::ToString::to_string`
 
 error: redundant closure
-  --> tests/ui/eta.rs:116:27
+  --> tests/ui/eta.rs:119:27
    |
 LL |     let e = Some('a').map(|s| s.to_uppercase());
    |                           ^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_uppercase`
 
 error: redundant closure
-  --> tests/ui/eta.rs:119:65
+  --> tests/ui/eta.rs:122:65
    |
 LL |     let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect();
    |                                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_ascii_uppercase`
 
 error: redundant closure
-  --> tests/ui/eta.rs:136:23
+  --> tests/ui/eta.rs:139:23
    |
 LL |         let _ = x.map(|x| x.parse::<i16>());
    |                       ^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `str::parse::<i16>`
 
 error: redundant closure
-  --> tests/ui/eta.rs:189:22
+  --> tests/ui/eta.rs:192:22
    |
 LL |     requires_fn_once(|| x());
    |                      ^^^^^^ help: replace the closure with the function itself: `x`
 
 error: redundant closure
-  --> tests/ui/eta.rs:197:27
+  --> tests/ui/eta.rs:200:27
    |
 LL |     let a = Some(1u8).map(|a| foo_ptr(a));
    |                           ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo_ptr`
 
 error: redundant closure
-  --> tests/ui/eta.rs:203:27
+  --> tests/ui/eta.rs:206:27
    |
 LL |     let a = Some(1u8).map(|a| closure(a));
    |                           ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `closure`
 
 error: redundant closure
-  --> tests/ui/eta.rs:236:28
+  --> tests/ui/eta.rs:239:28
    |
 LL |     x.into_iter().for_each(|x| add_to_res(x));
    |                            ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res`
 
 error: redundant closure
-  --> tests/ui/eta.rs:238:28
+  --> tests/ui/eta.rs:241:28
    |
 LL |     y.into_iter().for_each(|x| add_to_res(x));
    |                            ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res`
 
 error: redundant closure
-  --> tests/ui/eta.rs:240:28
+  --> tests/ui/eta.rs:243:28
    |
 LL |     z.into_iter().for_each(|x| add_to_res(x));
    |                            ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `add_to_res`
 
 error: redundant closure
-  --> tests/ui/eta.rs:248:21
+  --> tests/ui/eta.rs:251:21
    |
 LL |         Some(1).map(|n| closure(n));
    |                     ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut closure`
 
 error: redundant closure
-  --> tests/ui/eta.rs:253:21
+  --> tests/ui/eta.rs:256:21
    |
 LL |         Some(1).map(|n| in_loop(n));
    |                     ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `in_loop`
 
 error: redundant closure
-  --> tests/ui/eta.rs:347:18
+  --> tests/ui/eta.rs:350:18
    |
 LL |     takes_fn_mut(|| f());
    |                  ^^^^^^ help: replace the closure with the function itself: `&mut f`
 
 error: redundant closure
-  --> tests/ui/eta.rs:351:19
+  --> tests/ui/eta.rs:354:19
    |
 LL |     takes_fn_once(|| f());
    |                   ^^^^^^ help: replace the closure with the function itself: `&mut f`
 
 error: redundant closure
-  --> tests/ui/eta.rs:356:26
+  --> tests/ui/eta.rs:359:26
    |
 LL |     move || takes_fn_mut(|| f_used_once())
    |                          ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut f_used_once`
 
 error: redundant closure
-  --> tests/ui/eta.rs:369:19
+  --> tests/ui/eta.rs:372:19
    |
 LL |     array_opt.map(|a| a.as_slice());
    |                   ^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<[u8; 3]>::as_slice`
 
 error: redundant closure
-  --> tests/ui/eta.rs:373:19
+  --> tests/ui/eta.rs:376:19
    |
 LL |     slice_opt.map(|s| s.len());
    |                   ^^^^^^^^^^^ help: replace the closure with the method itself: `<[u8]>::len`
 
 error: redundant closure
-  --> tests/ui/eta.rs:377:17
+  --> tests/ui/eta.rs:380:17
    |
 LL |     ptr_opt.map(|p| p.is_null());
    |                 ^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<*const usize>::is_null`
 
 error: redundant closure
-  --> tests/ui/eta.rs:382:17
+  --> tests/ui/eta.rs:385:17
    |
 LL |     dyn_opt.map(|d| d.method_on_dyn());
    |                 ^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<dyn TestTrait>::method_on_dyn`
 
 error: redundant closure
-  --> tests/ui/eta.rs:443:19
+  --> tests/ui/eta.rs:446:19
    |
 LL |     let _ = f(&0, |x, y| f2(x, y));
    |                   ^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `f2`
 
 error: redundant closure
-  --> tests/ui/eta.rs:472:22
+  --> tests/ui/eta.rs:475:22
    |
 LL |             test.map(|t| t.method())
    |                      ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `Test::method`
 
 error: redundant closure
-  --> tests/ui/eta.rs:477:22
+  --> tests/ui/eta.rs:480:22
    |
 LL |             test.map(|t| t.method())
    |                      ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `super::Outer::method`
 
 error: redundant closure
-  --> tests/ui/eta.rs:491:18
+  --> tests/ui/eta.rs:494:18
    |
 LL |         test.map(|t| t.method())
    |                  ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `test_mod::Test::method`
 
 error: redundant closure
-  --> tests/ui/eta.rs:499:30
+  --> tests/ui/eta.rs:502:30
    |
 LL |                     test.map(|t| t.method())
    |                              ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `crate::issue_10854::d::Test::method`
 
 error: redundant closure
-  --> tests/ui/eta.rs:519:38
+  --> tests/ui/eta.rs:522:38
    |
 LL |         let x = Box::new(|| None.map(|x| f(x)));
    |                                      ^^^^^^^^ help: replace the closure with the function itself: `&f`
 
 error: redundant closure
-  --> tests/ui/eta.rs:524:38
+  --> tests/ui/eta.rs:527:38
    |
 LL |         let x = Box::new(|| None.map(|x| f(x)));
    |                                      ^^^^^^^^ help: replace the closure with the function itself: `f`
 
 error: redundant closure
-  --> tests/ui/eta.rs:542:35
+  --> tests/ui/eta.rs:545:35
    |
 LL |         let _field = bind.or_else(|| get_default()).unwrap();
    |                                   ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `get_default`
 
-error: aborting due to 35 previous errors
+error: redundant closure
+  --> tests/ui/eta.rs:592:16
+   |
+LL |     accepts_fn(|| f());
+   |                ^^^^^^ help: replace the closure with the function itself: `**f`
+
+error: redundant closure
+  --> tests/ui/eta.rs:596:16
+   |
+LL |     accepts_fn(|| g());
+   |                ^^^^^^ help: replace the closure with the function itself: `g`
+
+error: redundant closure
+  --> tests/ui/eta.rs:609:16
+   |
+LL |     accepts_fn(|| b());
+   |                ^^^^^^ help: replace the closure with the function itself: `***b`
+
+error: redundant closure
+  --> tests/ui/eta.rs:633:14
+   |
+LL |         .map(|n| MyError::A(n))
+   |              ^^^^^^^^^^^^^^^^^ help: replace the closure with the tuple variant itself: `MyError::A`
+
+error: redundant closure
+  --> tests/ui/eta.rs:630:14
+   |
+LL |         .map(|n| S(n))
+   |              ^^^^^^^^ help: replace the closure with the tuple struct itself: `S`
+
+error: redundant closure
+  --> tests/ui/eta.rs:627:14
+   |
+LL |         .map(|n| g(n))
+   |              ^^^^^^^^ help: replace the closure with the function itself: `g`
+
+error: redundant closure
+  --> tests/ui/eta.rs:624:14
+   |
+LL |         .map(|n| f(n))
+   |              ^^^^^^^^ help: replace the closure with the function itself: `f`
+
+error: aborting due to 42 previous errors
 
diff --git a/src/tools/clippy/tests/ui/excessive_precision.fixed b/src/tools/clippy/tests/ui/excessive_precision.fixed
index 8a8c2e1939c..8158d4b332a 100644
--- a/src/tools/clippy/tests/ui/excessive_precision.fixed
+++ b/src/tools/clippy/tests/ui/excessive_precision.fixed
@@ -4,9 +4,16 @@
     overflowing_literals,
     unused_variables,
     clippy::print_literal,
-    clippy::useless_vec
+    clippy::useless_vec,
+    clippy::approx_constant
 )]
 
+macro_rules! make_pi {
+    ($i:ident : $t:ty) => {
+        const $i: $t = 3.14159265358979323846264338327950288419716939937510582097494459230781640628;
+    };
+}
+
 fn main() {
     // Consts
     const GOOD32: f32 = 0.123_456;
@@ -101,4 +108,40 @@ fn main() {
     const _: f64 = 3.0;
     //~^ excessive_precision
     const _: f64 = 3.0000000000000000;
+
+    // Overly specified constants
+    let _: f32 = 1.012_345_7;
+    //~^ excessive_precision
+    let _: f64 = 1.012_345_678_901_234_6;
+    //~^ excessive_precision
+    const _: f32 = 1.01234567890123456789012345678901234567890;
+    const _: f64 = 1.01234567890123456789012345678901234567890;
+
+    static STATIC1: f32 = 1.01234567890123456789012345678901234567890;
+    static STATIC2: f64 = 1.01234567890123456789012345678901234567890;
+
+    static mut STATIC_MUT1: f32 = 1.01234567890123456789012345678901234567890;
+    static mut STATIC_MUT2: f64 = 1.01234567890123456789012345678901234567890;
+
+    // From issue #13855
+    let gamma = 0.577_215_664_901_532_9;
+    //~^ excessive_precision
+    const GAMMA: f64 = 0.5772156649015328606065120900824024310421;
+
+    make_pi!(P32: f32);
+    make_pi!(P64: f64);
+}
+
+trait ExcessivelyPreciseTrait {
+    // Overly specified constants
+    const GOOD1: f32 = 1.01234567890123456789012345678901234567890;
+    const GOOD2: f64 = 1.01234567890123456789012345678901234567890;
+}
+
+struct ExcessivelyPreciseStruct;
+
+impl ExcessivelyPreciseStruct {
+    // Overly specified constants
+    const GOOD1: f32 = 1.01234567890123456789012345678901234567890;
+    const GOOD2: f64 = 1.01234567890123456789012345678901234567890;
 }
diff --git a/src/tools/clippy/tests/ui/excessive_precision.rs b/src/tools/clippy/tests/ui/excessive_precision.rs
index 5dcf55cb927..7ee6247ee5a 100644
--- a/src/tools/clippy/tests/ui/excessive_precision.rs
+++ b/src/tools/clippy/tests/ui/excessive_precision.rs
@@ -4,9 +4,16 @@
     overflowing_literals,
     unused_variables,
     clippy::print_literal,
-    clippy::useless_vec
+    clippy::useless_vec,
+    clippy::approx_constant
 )]
 
+macro_rules! make_pi {
+    ($i:ident : $t:ty) => {
+        const $i: $t = 3.14159265358979323846264338327950288419716939937510582097494459230781640628;
+    };
+}
+
 fn main() {
     // Consts
     const GOOD32: f32 = 0.123_456;
@@ -101,4 +108,40 @@ fn main() {
     const _: f64 = 3.0000000000000000e+00;
     //~^ excessive_precision
     const _: f64 = 3.0000000000000000;
+
+    // Overly specified constants
+    let _: f32 = 1.01234567890123456789012345678901234567890;
+    //~^ excessive_precision
+    let _: f64 = 1.01234567890123456789012345678901234567890;
+    //~^ excessive_precision
+    const _: f32 = 1.01234567890123456789012345678901234567890;
+    const _: f64 = 1.01234567890123456789012345678901234567890;
+
+    static STATIC1: f32 = 1.01234567890123456789012345678901234567890;
+    static STATIC2: f64 = 1.01234567890123456789012345678901234567890;
+
+    static mut STATIC_MUT1: f32 = 1.01234567890123456789012345678901234567890;
+    static mut STATIC_MUT2: f64 = 1.01234567890123456789012345678901234567890;
+
+    // From issue #13855
+    let gamma = 0.5772156649015328606065120900824024310421;
+    //~^ excessive_precision
+    const GAMMA: f64 = 0.5772156649015328606065120900824024310421;
+
+    make_pi!(P32: f32);
+    make_pi!(P64: f64);
+}
+
+trait ExcessivelyPreciseTrait {
+    // Overly specified constants
+    const GOOD1: f32 = 1.01234567890123456789012345678901234567890;
+    const GOOD2: f64 = 1.01234567890123456789012345678901234567890;
+}
+
+struct ExcessivelyPreciseStruct;
+
+impl ExcessivelyPreciseStruct {
+    // Overly specified constants
+    const GOOD1: f32 = 1.01234567890123456789012345678901234567890;
+    const GOOD2: f64 = 1.01234567890123456789012345678901234567890;
 }
diff --git a/src/tools/clippy/tests/ui/excessive_precision.stderr b/src/tools/clippy/tests/ui/excessive_precision.stderr
index f5eeadf0c8c..40806d67487 100644
--- a/src/tools/clippy/tests/ui/excessive_precision.stderr
+++ b/src/tools/clippy/tests/ui/excessive_precision.stderr
@@ -1,5 +1,5 @@
 error: float has excessive precision
-  --> tests/ui/excessive_precision.rs:20:26
+  --> tests/ui/excessive_precision.rs:27:26
    |
 LL |     const BAD32_1: f32 = 0.123_456_789_f32;
    |                          ^^^^^^^^^^^^^^^^^
@@ -13,7 +13,7 @@ LL +     const BAD32_1: f32 = 0.123_456_79_f32;
    |
 
 error: float has excessive precision
-  --> tests/ui/excessive_precision.rs:22:26
+  --> tests/ui/excessive_precision.rs:29:26
    |
 LL |     const BAD32_2: f32 = 0.123_456_789;
    |                          ^^^^^^^^^^^^^
@@ -25,7 +25,7 @@ LL +     const BAD32_2: f32 = 0.123_456_79;
    |
 
 error: float has excessive precision
-  --> tests/ui/excessive_precision.rs:24:26
+  --> tests/ui/excessive_precision.rs:31:26
    |
 LL |     const BAD32_3: f32 = 0.100_000_000_000_1;
    |                          ^^^^^^^^^^^^^^^^^^^
@@ -37,7 +37,7 @@ LL +     const BAD32_3: f32 = 0.1;
    |
 
 error: float has excessive precision
-  --> tests/ui/excessive_precision.rs:26:29
+  --> tests/ui/excessive_precision.rs:33:29
    |
 LL |     const BAD32_EDGE: f32 = 1.000_000_9;
    |                             ^^^^^^^^^^^
@@ -49,7 +49,7 @@ LL +     const BAD32_EDGE: f32 = 1.000_001;
    |
 
 error: float has excessive precision
-  --> tests/ui/excessive_precision.rs:31:26
+  --> tests/ui/excessive_precision.rs:38:26
    |
 LL |     const BAD64_3: f64 = 0.100_000_000_000_000_000_1;
    |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -61,7 +61,7 @@ LL +     const BAD64_3: f64 = 0.1;
    |
 
 error: float has excessive precision
-  --> tests/ui/excessive_precision.rs:35:22
+  --> tests/ui/excessive_precision.rs:42:22
    |
 LL |     println!("{:?}", 8.888_888_888_888_888_888_888);
    |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -73,7 +73,7 @@ LL +     println!("{:?}", 8.888_888_888_888_89);
    |
 
 error: float has excessive precision
-  --> tests/ui/excessive_precision.rs:47:22
+  --> tests/ui/excessive_precision.rs:54:22
    |
 LL |     let bad32: f32 = 1.123_456_789;
    |                      ^^^^^^^^^^^^^
@@ -85,7 +85,7 @@ LL +     let bad32: f32 = 1.123_456_8;
    |
 
 error: float has excessive precision
-  --> tests/ui/excessive_precision.rs:49:26
+  --> tests/ui/excessive_precision.rs:56:26
    |
 LL |     let bad32_suf: f32 = 1.123_456_789_f32;
    |                          ^^^^^^^^^^^^^^^^^
@@ -97,7 +97,7 @@ LL +     let bad32_suf: f32 = 1.123_456_8_f32;
    |
 
 error: float has excessive precision
-  --> tests/ui/excessive_precision.rs:51:21
+  --> tests/ui/excessive_precision.rs:58:21
    |
 LL |     let bad32_inf = 1.123_456_789_f32;
    |                     ^^^^^^^^^^^^^^^^^
@@ -109,7 +109,7 @@ LL +     let bad32_inf = 1.123_456_8_f32;
    |
 
 error: float has excessive precision
-  --> tests/ui/excessive_precision.rs:62:36
+  --> tests/ui/excessive_precision.rs:69:36
    |
 LL |     let bad_vec32: Vec<f32> = vec![0.123_456_789];
    |                                    ^^^^^^^^^^^^^
@@ -121,7 +121,7 @@ LL +     let bad_vec32: Vec<f32> = vec![0.123_456_79];
    |
 
 error: float has excessive precision
-  --> tests/ui/excessive_precision.rs:64:36
+  --> tests/ui/excessive_precision.rs:71:36
    |
 LL |     let bad_vec64: Vec<f64> = vec![0.123_456_789_123_456_789];
    |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -133,7 +133,7 @@ LL +     let bad_vec64: Vec<f64> = vec![0.123_456_789_123_456_78];
    |
 
 error: float has excessive precision
-  --> tests/ui/excessive_precision.rs:69:24
+  --> tests/ui/excessive_precision.rs:76:24
    |
 LL |     let bad_e32: f32 = 1.123_456_788_888e-10;
    |                        ^^^^^^^^^^^^^^^^^^^^^
@@ -145,7 +145,7 @@ LL +     let bad_e32: f32 = 1.123_456_8e-10;
    |
 
 error: float has excessive precision
-  --> tests/ui/excessive_precision.rs:73:27
+  --> tests/ui/excessive_precision.rs:80:27
    |
 LL |     let bad_bige32: f32 = 1.123_456_788_888E-10;
    |                           ^^^^^^^^^^^^^^^^^^^^^
@@ -157,7 +157,7 @@ LL +     let bad_bige32: f32 = 1.123_456_8E-10;
    |
 
 error: float has excessive precision
-  --> tests/ui/excessive_precision.rs:86:13
+  --> tests/ui/excessive_precision.rs:93:13
    |
 LL |     let _ = 2.225_073_858_507_201_1e-308_f64;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -169,7 +169,7 @@ LL +     let _ = 2.225_073_858_507_201e-308_f64;
    |
 
 error: float has excessive precision
-  --> tests/ui/excessive_precision.rs:90:13
+  --> tests/ui/excessive_precision.rs:97:13
    |
 LL |     let _ = 1.000_000_000_000_001e-324_f64;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -181,7 +181,7 @@ LL +     let _ = 0_f64;
    |
 
 error: float has excessive precision
-  --> tests/ui/excessive_precision.rs:101:20
+  --> tests/ui/excessive_precision.rs:108:20
    |
 LL |     const _: f64 = 3.0000000000000000e+00;
    |                    ^^^^^^^^^^^^^^^^^^^^^^
@@ -192,5 +192,56 @@ LL -     const _: f64 = 3.0000000000000000e+00;
 LL +     const _: f64 = 3.0;
    |
 
-error: aborting due to 16 previous errors
+error: float has excessive precision
+  --> tests/ui/excessive_precision.rs:113:18
+   |
+LL |     let _: f32 = 1.01234567890123456789012345678901234567890;
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: consider making it a `const` item
+  --> tests/ui/excessive_precision.rs:113:5
+   |
+LL |     let _: f32 = 1.01234567890123456789012345678901234567890;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: consider changing the type or truncating it to
+   |
+LL -     let _: f32 = 1.01234567890123456789012345678901234567890;
+LL +     let _: f32 = 1.012_345_7;
+   |
+
+error: float has excessive precision
+  --> tests/ui/excessive_precision.rs:115:18
+   |
+LL |     let _: f64 = 1.01234567890123456789012345678901234567890;
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: consider making it a `const` item
+  --> tests/ui/excessive_precision.rs:115:5
+   |
+LL |     let _: f64 = 1.01234567890123456789012345678901234567890;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: consider changing the type or truncating it to
+   |
+LL -     let _: f64 = 1.01234567890123456789012345678901234567890;
+LL +     let _: f64 = 1.012_345_678_901_234_6;
+   |
+
+error: float has excessive precision
+  --> tests/ui/excessive_precision.rs:127:17
+   |
+LL |     let gamma = 0.5772156649015328606065120900824024310421;
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: consider making it a `const` item
+  --> tests/ui/excessive_precision.rs:127:5
+   |
+LL |     let gamma = 0.5772156649015328606065120900824024310421;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: consider changing the type or truncating it to
+   |
+LL -     let gamma = 0.5772156649015328606065120900824024310421;
+LL +     let gamma = 0.577_215_664_901_532_9;
+   |
+
+error: aborting due to 19 previous errors
 
diff --git a/src/tools/clippy/tests/ui/float_equality_without_abs.rs b/src/tools/clippy/tests/ui/float_equality_without_abs.rs
index a1548db6710..e1dd7902683 100644
--- a/src/tools/clippy/tests/ui/float_equality_without_abs.rs
+++ b/src/tools/clippy/tests/ui/float_equality_without_abs.rs
@@ -1,8 +1,8 @@
+#![feature(f128)]
+#![feature(f16)]
 #![warn(clippy::float_equality_without_abs)]
 //@no-rustfix: suggestions cause type ambiguity
 
-// FIXME(f16_f128): add tests for these types when abs is available
-
 pub fn is_roughly_equal(a: f32, b: f32) -> bool {
     (a - b) < f32::EPSILON
     //~^ float_equality_without_abs
@@ -44,10 +44,20 @@ pub fn main() {
     let _ = f32::EPSILON > 1.0 - 2.0;
     //~^ float_equality_without_abs
 
+    let _ = (a as f16 - b as f16) < f16::EPSILON;
+    //~^ float_equality_without_abs
+
+    let _ = (a as f128 - b as f128) < f128::EPSILON;
+    //~^ float_equality_without_abs
+
     // those are correct
+    let _ = (a as f16 - b as f16).abs() < f16::EPSILON;
     let _ = (a - b).abs() < f32::EPSILON;
     let _ = (a as f64 - b as f64).abs() < f64::EPSILON;
+    let _ = (a as f128 - b as f128).abs() < f128::EPSILON;
 
+    let _ = f16::EPSILON > (a as f16 - b as f16).abs();
     let _ = f32::EPSILON > (a - b).abs();
     let _ = f64::EPSILON > (a as f64 - b as f64).abs();
+    let _ = f128::EPSILON > (a as f128 - b as f128).abs();
 }
diff --git a/src/tools/clippy/tests/ui/float_equality_without_abs.stderr b/src/tools/clippy/tests/ui/float_equality_without_abs.stderr
index d4c89ce72ba..55a150dead5 100644
--- a/src/tools/clippy/tests/ui/float_equality_without_abs.stderr
+++ b/src/tools/clippy/tests/ui/float_equality_without_abs.stderr
@@ -89,5 +89,21 @@ LL |     let _ = f32::EPSILON > 1.0 - 2.0;
    |                            |
    |                            help: add `.abs()`: `(1.0 - 2.0).abs()`
 
-error: aborting due to 11 previous errors
+error: float equality check without `.abs()`
+  --> tests/ui/float_equality_without_abs.rs:47:13
+   |
+LL |     let _ = (a as f16 - b as f16) < f16::EPSILON;
+   |             ---------------------^^^^^^^^^^^^^^^
+   |             |
+   |             help: add `.abs()`: `(a as f16 - b as f16).abs()`
+
+error: float equality check without `.abs()`
+  --> tests/ui/float_equality_without_abs.rs:50:13
+   |
+LL |     let _ = (a as f128 - b as f128) < f128::EPSILON;
+   |             -----------------------^^^^^^^^^^^^^^^^
+   |             |
+   |             help: add `.abs()`: `(a as f128 - b as f128).abs()`
+
+error: aborting due to 13 previous errors
 
diff --git a/src/tools/clippy/tests/ui/from_str_radix_10.fixed b/src/tools/clippy/tests/ui/from_str_radix_10.fixed
index 4b8fd778685..47d24167e56 100644
--- a/src/tools/clippy/tests/ui/from_str_radix_10.fixed
+++ b/src/tools/clippy/tests/ui/from_str_radix_10.fixed
@@ -74,3 +74,13 @@ fn issue_12731() {
         let _ = u32::from_str_radix("123", 10);
     }
 }
+
+fn fix_str_ref_check() {
+    #![allow(clippy::needless_borrow)]
+    let s = "1";
+    let _ = s.parse::<u32>().unwrap();
+    //~^ from_str_radix_10
+    let s_ref = &s;
+    let _ = s_ref.parse::<u32>().unwrap();
+    //~^ from_str_radix_10
+}
diff --git a/src/tools/clippy/tests/ui/from_str_radix_10.rs b/src/tools/clippy/tests/ui/from_str_radix_10.rs
index 89002b11a99..952e19b57a0 100644
--- a/src/tools/clippy/tests/ui/from_str_radix_10.rs
+++ b/src/tools/clippy/tests/ui/from_str_radix_10.rs
@@ -74,3 +74,13 @@ fn issue_12731() {
         let _ = u32::from_str_radix("123", 10);
     }
 }
+
+fn fix_str_ref_check() {
+    #![allow(clippy::needless_borrow)]
+    let s = "1";
+    let _ = u32::from_str_radix(&s, 10).unwrap();
+    //~^ from_str_radix_10
+    let s_ref = &s;
+    let _ = u32::from_str_radix(&s_ref, 10).unwrap();
+    //~^ from_str_radix_10
+}
diff --git a/src/tools/clippy/tests/ui/from_str_radix_10.stderr b/src/tools/clippy/tests/ui/from_str_radix_10.stderr
index c693e8f50ff..d4e6c3657f2 100644
--- a/src/tools/clippy/tests/ui/from_str_radix_10.stderr
+++ b/src/tools/clippy/tests/ui/from_str_radix_10.stderr
@@ -49,5 +49,17 @@ error: this call to `from_str_radix` can be replaced with a call to `str::parse`
 LL |     i32::from_str_radix(&stringier, 10)?;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `stringier.parse::<i32>()`
 
-error: aborting due to 8 previous errors
+error: this call to `from_str_radix` can be replaced with a call to `str::parse`
+  --> tests/ui/from_str_radix_10.rs:81:13
+   |
+LL |     let _ = u32::from_str_radix(&s, 10).unwrap();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s.parse::<u32>()`
+
+error: this call to `from_str_radix` can be replaced with a call to `str::parse`
+  --> tests/ui/from_str_radix_10.rs:84:13
+   |
+LL |     let _ = u32::from_str_radix(&s_ref, 10).unwrap();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `s_ref.parse::<u32>()`
+
+error: aborting due to 10 previous errors
 
diff --git a/src/tools/clippy/tests/ui/functions_maxlines.rs b/src/tools/clippy/tests/ui/functions_maxlines.rs
index e0990dadaaa..b0714552461 100644
--- a/src/tools/clippy/tests/ui/functions_maxlines.rs
+++ b/src/tools/clippy/tests/ui/functions_maxlines.rs
@@ -1,3 +1,4 @@
+#![allow(clippy::unused_unit, clippy::missing_safety_doc)]
 #![warn(clippy::too_many_lines)]
 
 fn good_lines() {
@@ -55,7 +56,8 @@ fn good_lines() {
     println!("This is good.");
 }
 
-fn bad_lines() {
+#[allow(unused)] // the attr shouldn't get included in the highlight
+pub async unsafe extern "Rust" fn bad_lines() -> () {
     //~^ too_many_lines
 
     println!("Dont get confused by braces: {{}}");
@@ -162,4 +164,115 @@ fn bad_lines() {
     println!("This is bad.");
 }
 
+struct Foo;
+impl Foo {
+    #[allow(unused)] // the attr shouldn't get included in the highlight
+    pub async unsafe extern "Rust" fn bad_lines() -> () {
+        //~^ too_many_lines
+
+        println!("Dont get confused by braces: {{}}");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+        println!("This is bad.");
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/functions_maxlines.stderr b/src/tools/clippy/tests/ui/functions_maxlines.stderr
index f42a2b2a22a..4c3faf45c47 100644
--- a/src/tools/clippy/tests/ui/functions_maxlines.stderr
+++ b/src/tools/clippy/tests/ui/functions_maxlines.stderr
@@ -1,17 +1,17 @@
-error: this function has too many lines (102/100)
-  --> tests/ui/functions_maxlines.rs:58:1
+error: this function has too many lines (104/100)
+  --> tests/ui/functions_maxlines.rs:60:1
    |
-LL | / fn bad_lines() {
-LL | |
-LL | |
-LL | |     println!("Dont get confused by braces: {{}}");
-...  |
-LL | |     println!("This is bad.");
-LL | | }
-   | |_^
+LL | pub async unsafe extern "Rust" fn bad_lines() -> () {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::too-many-lines` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::too_many_lines)]`
 
-error: aborting due to 1 previous error
+error: this function has too many lines (104/100)
+  --> tests/ui/functions_maxlines.rs:170:5
+   |
+LL |     pub async unsafe extern "Rust" fn bad_lines() -> () {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
 
diff --git a/src/tools/clippy/tests/ui/if_then_some_else_none.fixed b/src/tools/clippy/tests/ui/if_then_some_else_none.fixed
index d14a805b666..0fd130609ae 100644
--- a/src/tools/clippy/tests/ui/if_then_some_else_none.fixed
+++ b/src/tools/clippy/tests/ui/if_then_some_else_none.fixed
@@ -165,3 +165,44 @@ mod issue15257 {
         do_something((i % 2 == 0).then_some(closure_fn));
     }
 }
+
+fn issue15005() {
+    struct Counter {
+        count: u32,
+    }
+
+    impl Counter {
+        fn new() -> Counter {
+            Counter { count: 0 }
+        }
+    }
+
+    impl Iterator for Counter {
+        type Item = u32;
+
+        fn next(&mut self) -> Option<Self::Item> {
+            //~v if_then_some_else_none
+            (self.count < 5).then(||  { self.count += 1; self.count })
+        }
+    }
+}
+
+fn statements_from_macro() {
+    macro_rules! mac {
+        () => {
+            println!("foo");
+            println!("bar");
+        };
+    }
+    //~v if_then_some_else_none
+    let _ = true.then(||  { mac!(); 42 });
+}
+
+fn dont_lint_inside_macros() {
+    macro_rules! mac {
+        ($cond:expr, $res:expr) => {
+            if $cond { Some($res) } else { None }
+        };
+    }
+    let _: Option<u32> = mac!(true, 42);
+}
diff --git a/src/tools/clippy/tests/ui/if_then_some_else_none.rs b/src/tools/clippy/tests/ui/if_then_some_else_none.rs
index bb0072f3157..640828aa9bf 100644
--- a/src/tools/clippy/tests/ui/if_then_some_else_none.rs
+++ b/src/tools/clippy/tests/ui/if_then_some_else_none.rs
@@ -211,3 +211,54 @@ mod issue15257 {
         });
     }
 }
+
+fn issue15005() {
+    struct Counter {
+        count: u32,
+    }
+
+    impl Counter {
+        fn new() -> Counter {
+            Counter { count: 0 }
+        }
+    }
+
+    impl Iterator for Counter {
+        type Item = u32;
+
+        fn next(&mut self) -> Option<Self::Item> {
+            //~v if_then_some_else_none
+            if self.count < 5 {
+                self.count += 1;
+                Some(self.count)
+            } else {
+                None
+            }
+        }
+    }
+}
+
+fn statements_from_macro() {
+    macro_rules! mac {
+        () => {
+            println!("foo");
+            println!("bar");
+        };
+    }
+    //~v if_then_some_else_none
+    let _ = if true {
+        mac!();
+        Some(42)
+    } else {
+        None
+    };
+}
+
+fn dont_lint_inside_macros() {
+    macro_rules! mac {
+        ($cond:expr, $res:expr) => {
+            if $cond { Some($res) } else { None }
+        };
+    }
+    let _: Option<u32> = mac!(true, 42);
+}
diff --git a/src/tools/clippy/tests/ui/if_then_some_else_none.stderr b/src/tools/clippy/tests/ui/if_then_some_else_none.stderr
index c2e624a0a73..58651a05594 100644
--- a/src/tools/clippy/tests/ui/if_then_some_else_none.stderr
+++ b/src/tools/clippy/tests/ui/if_then_some_else_none.stderr
@@ -116,5 +116,28 @@ LL | |             None
 LL | |         });
    | |_________^ help: try: `(i % 2 == 0).then_some(closure_fn)`
 
-error: aborting due to 11 previous errors
+error: this could be simplified with `bool::then`
+  --> tests/ui/if_then_some_else_none.rs:231:13
+   |
+LL | /             if self.count < 5 {
+LL | |                 self.count += 1;
+LL | |                 Some(self.count)
+LL | |             } else {
+LL | |                 None
+LL | |             }
+   | |_____________^ help: try: `(self.count < 5).then(||  { self.count += 1; self.count })`
+
+error: this could be simplified with `bool::then`
+  --> tests/ui/if_then_some_else_none.rs:249:13
+   |
+LL |       let _ = if true {
+   |  _____________^
+LL | |         mac!();
+LL | |         Some(42)
+LL | |     } else {
+LL | |         None
+LL | |     };
+   | |_____^ help: try: `true.then(||  { mac!(); 42 })`
+
+error: aborting due to 13 previous errors
 
diff --git a/src/tools/clippy/tests/ui/infinite_loops.rs b/src/tools/clippy/tests/ui/infinite_loops.rs
index fcd1f795fff..7d01a7fb61f 100644
--- a/src/tools/clippy/tests/ui/infinite_loops.rs
+++ b/src/tools/clippy/tests/ui/infinite_loops.rs
@@ -450,4 +450,90 @@ mod issue_12338 {
     }
 }
 
+#[allow(clippy::let_underscore_future, clippy::empty_loop)]
+mod issue_14000 {
+    use super::do_something;
+
+    async fn foo() {
+        let _ = async move {
+            loop {
+                //~^ infinite_loop
+                do_something();
+            }
+        }
+        .await;
+        let _ = async move {
+            loop {
+                //~^ infinite_loop
+                continue;
+            }
+        }
+        .await;
+    }
+
+    fn bar() {
+        let _ = async move {
+            loop {
+                do_something();
+            }
+        };
+
+        let _ = async move {
+            loop {
+                continue;
+            }
+        };
+    }
+}
+
+#[allow(clippy::let_underscore_future)]
+mod tokio_spawn_test {
+    use super::do_something;
+
+    fn install_ticker() {
+        // This should NOT trigger the lint because the async block is spawned, not awaited
+        std::thread::spawn(move || {
+            async move {
+                loop {
+                    // This loop should not trigger infinite_loop lint
+                    do_something();
+                }
+            }
+        });
+    }
+
+    fn spawn_async_block() {
+        // This should NOT trigger the lint because the async block is not awaited
+        let _handle = async move {
+            loop {
+                do_something();
+            }
+        };
+    }
+
+    fn await_async_block() {
+        // This SHOULD trigger the lint because the async block is awaited
+        let _ = async move {
+            loop {
+                do_something();
+            }
+        };
+    }
+}
+
+mod issue15541 {
+    async fn good() -> ! {
+        loop {
+            std::future::pending().await
+        }
+    }
+
+    async fn bad() {
+        //~v infinite_loop
+        loop {
+            std::future::pending().await
+        }
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/infinite_loops.stderr b/src/tools/clippy/tests/ui/infinite_loops.stderr
index 4d02636ef4a..319f1e5012b 100644
--- a/src/tools/clippy/tests/ui/infinite_loops.stderr
+++ b/src/tools/clippy/tests/ui/infinite_loops.stderr
@@ -311,5 +311,37 @@ help: if this is intentional, consider specifying `!` as function return
 LL | fn continue_outer() -> ! {
    |                     ++++
 
-error: aborting due to 21 previous errors
+error: infinite loop detected
+  --> tests/ui/infinite_loops.rs:459:13
+   |
+LL | /             loop {
+LL | |
+LL | |                 do_something();
+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:466:13
+   |
+LL | /             loop {
+LL | |
+LL | |                 continue;
+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:533:9
+   |
+LL | /         loop {
+LL | |             std::future::pending().await
+LL | |         }
+   | |_________^
+   |
+   = help: if this is not intended, try adding a `break` or `return` condition in the loop
+
+error: aborting due to 24 previous errors
 
diff --git a/src/tools/clippy/tests/ui/invalid_upcast_comparisons.rs b/src/tools/clippy/tests/ui/invalid_upcast_comparisons.rs
index 4f3194869f4..88ecca65e15 100644
--- a/src/tools/clippy/tests/ui/invalid_upcast_comparisons.rs
+++ b/src/tools/clippy/tests/ui/invalid_upcast_comparisons.rs
@@ -133,3 +133,15 @@ fn main() {
 
     -5 == (u32 as i32);
 }
+
+fn issue15662() {
+    macro_rules! add_one {
+        ($x:expr) => {
+            $x + 1
+        };
+    }
+
+    let x: u8 = 1;
+    (add_one!(x) as u32) > 300;
+    //~^ invalid_upcast_comparisons
+}
diff --git a/src/tools/clippy/tests/ui/invalid_upcast_comparisons.stderr b/src/tools/clippy/tests/ui/invalid_upcast_comparisons.stderr
index ef36f18eabc..cc042a7c4b0 100644
--- a/src/tools/clippy/tests/ui/invalid_upcast_comparisons.stderr
+++ b/src/tools/clippy/tests/ui/invalid_upcast_comparisons.stderr
@@ -163,5 +163,11 @@ error: because of the numeric bounds on `u8` prior to casting, this expression i
 LL |     -5 >= (u8 as i32);
    |     ^^^^^^^^^^^^^^^^^
 
-error: aborting due to 27 previous errors
+error: because of the numeric bounds on `add_one!(x)` prior to casting, this expression is always false
+  --> tests/ui/invalid_upcast_comparisons.rs:145:5
+   |
+LL |     (add_one!(x) as u32) > 300;
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 28 previous errors
 
diff --git a/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed b/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed
index b0e548f1790..4171f19469a 100644
--- a/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed
+++ b/src/tools/clippy/tests/ui/iter_overeager_cloned.fixed
@@ -1,4 +1,4 @@
-#![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)]
+#![warn(clippy::iter_overeager_cloned, clippy::redundant_iter_cloned, clippy::filter_next)]
 #![allow(
     dead_code,
     clippy::let_unit_value,
@@ -16,7 +16,7 @@ fn main() {
     //~^ iter_overeager_cloned
 
     let _: usize = vec.iter().filter(|x| x == &"2").count();
-    //~^ redundant_clone
+    //~^ redundant_iter_cloned
 
     let _: Vec<_> = vec.iter().take(2).cloned().collect();
     //~^ iter_overeager_cloned
@@ -77,19 +77,19 @@ fn main() {
     }
 
     let _ = vec.iter().map(|x| x.len());
-    //~^ redundant_clone
+    //~^ redundant_iter_cloned
 
     // This would fail if changed.
     let _ = vec.iter().cloned().map(|x| x + "2");
 
     let _ = vec.iter().for_each(|x| assert!(!x.is_empty()));
-    //~^ redundant_clone
+    //~^ redundant_iter_cloned
 
     let _ = vec.iter().all(|x| x.len() == 1);
-    //~^ redundant_clone
+    //~^ redundant_iter_cloned
 
     let _ = vec.iter().any(|x| x.len() == 1);
-    //~^ redundant_clone
+    //~^ redundant_iter_cloned
 
     // Should probably stay as it is.
     let _ = [0, 1, 2, 3, 4].iter().cloned().take(10);
diff --git a/src/tools/clippy/tests/ui/iter_overeager_cloned.rs b/src/tools/clippy/tests/ui/iter_overeager_cloned.rs
index cedf62a6b47..fe6aba24dd3 100644
--- a/src/tools/clippy/tests/ui/iter_overeager_cloned.rs
+++ b/src/tools/clippy/tests/ui/iter_overeager_cloned.rs
@@ -1,4 +1,4 @@
-#![warn(clippy::iter_overeager_cloned, clippy::redundant_clone, clippy::filter_next)]
+#![warn(clippy::iter_overeager_cloned, clippy::redundant_iter_cloned, clippy::filter_next)]
 #![allow(
     dead_code,
     clippy::let_unit_value,
@@ -16,7 +16,7 @@ fn main() {
     //~^ iter_overeager_cloned
 
     let _: usize = vec.iter().filter(|x| x == &"2").cloned().count();
-    //~^ redundant_clone
+    //~^ redundant_iter_cloned
 
     let _: Vec<_> = vec.iter().cloned().take(2).collect();
     //~^ iter_overeager_cloned
@@ -78,19 +78,19 @@ fn main() {
     }
 
     let _ = vec.iter().cloned().map(|x| x.len());
-    //~^ redundant_clone
+    //~^ redundant_iter_cloned
 
     // This would fail if changed.
     let _ = vec.iter().cloned().map(|x| x + "2");
 
     let _ = vec.iter().cloned().for_each(|x| assert!(!x.is_empty()));
-    //~^ redundant_clone
+    //~^ redundant_iter_cloned
 
     let _ = vec.iter().cloned().all(|x| x.len() == 1);
-    //~^ redundant_clone
+    //~^ redundant_iter_cloned
 
     let _ = vec.iter().cloned().any(|x| x.len() == 1);
-    //~^ redundant_clone
+    //~^ redundant_iter_cloned
 
     // Should probably stay as it is.
     let _ = [0, 1, 2, 3, 4].iter().cloned().take(10);
diff --git a/src/tools/clippy/tests/ui/iter_overeager_cloned.stderr b/src/tools/clippy/tests/ui/iter_overeager_cloned.stderr
index 1616dec95b7..f234d19e4aa 100644
--- a/src/tools/clippy/tests/ui/iter_overeager_cloned.stderr
+++ b/src/tools/clippy/tests/ui/iter_overeager_cloned.stderr
@@ -25,8 +25,8 @@ LL |     let _: usize = vec.iter().filter(|x| x == &"2").cloned().count();
    |                                                    |
    |                                                    help: try: `.count()`
    |
-   = note: `-D clippy::redundant-clone` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::redundant_clone)]`
+   = note: `-D clippy::redundant-iter-cloned` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::redundant_iter_cloned)]`
 
 error: unnecessarily eager cloning of iterator items
   --> tests/ui/iter_overeager_cloned.rs:21:21
diff --git a/src/tools/clippy/tests/ui/manual_is_ascii_check.fixed b/src/tools/clippy/tests/ui/manual_is_ascii_check.fixed
index 7b0d1906834..406336dbd09 100644
--- a/src/tools/clippy/tests/ui/manual_is_ascii_check.fixed
+++ b/src/tools/clippy/tests/ui/manual_is_ascii_check.fixed
@@ -108,6 +108,8 @@ fn generics() {
     //~^ manual_is_ascii_check
     take_while(|c: char| c.is_ascii_uppercase());
     //~^ manual_is_ascii_check
+    take_while(|c: char| c.is_ascii_uppercase());
+    //~^ manual_is_ascii_check
 }
 
 fn adds_type_reference() {
@@ -115,4 +117,6 @@ fn adds_type_reference() {
     //~^ manual_is_ascii_check
     let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c: &&mut char| c.is_ascii_digit()).collect();
     //~^ manual_is_ascii_check
+    let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c: &&mut char| c.is_ascii_digit()).collect();
+    //~^ manual_is_ascii_check
 }
diff --git a/src/tools/clippy/tests/ui/manual_is_ascii_check.rs b/src/tools/clippy/tests/ui/manual_is_ascii_check.rs
index e4f7fe9f583..a624497e75e 100644
--- a/src/tools/clippy/tests/ui/manual_is_ascii_check.rs
+++ b/src/tools/clippy/tests/ui/manual_is_ascii_check.rs
@@ -108,6 +108,8 @@ fn generics() {
     //~^ manual_is_ascii_check
     take_while(|c: char| ('A'..='Z').contains(&c));
     //~^ manual_is_ascii_check
+    take_while(|c| matches!(c, 'A'..='Z'));
+    //~^ manual_is_ascii_check
 }
 
 fn adds_type_reference() {
@@ -115,4 +117,6 @@ fn adds_type_reference() {
     //~^ manual_is_ascii_check
     let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c| ('0'..='9').contains(c)).collect();
     //~^ manual_is_ascii_check
+    let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c| matches!(c, '0'..='9')).collect();
+    //~^ manual_is_ascii_check
 }
diff --git a/src/tools/clippy/tests/ui/manual_is_ascii_check.stderr b/src/tools/clippy/tests/ui/manual_is_ascii_check.stderr
index 9fd7f457b42..cb2548ea731 100644
--- a/src/tools/clippy/tests/ui/manual_is_ascii_check.stderr
+++ b/src/tools/clippy/tests/ui/manual_is_ascii_check.stderr
@@ -176,7 +176,19 @@ LL |     take_while(|c: char| ('A'..='Z').contains(&c));
    |                          ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `c.is_ascii_uppercase()`
 
 error: manual check for common ascii range
-  --> tests/ui/manual_is_ascii_check.rs:114:63
+  --> tests/ui/manual_is_ascii_check.rs:111:20
+   |
+LL |     take_while(|c| matches!(c, 'A'..='Z'));
+   |                    ^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL -     take_while(|c| matches!(c, 'A'..='Z'));
+LL +     take_while(|c: char| c.is_ascii_uppercase());
+   |
+
+error: manual check for common ascii range
+  --> tests/ui/manual_is_ascii_check.rs:116:63
    |
 LL |     let digits: Vec<&char> = ['1', 'A'].iter().take_while(|c| ('0'..='9').contains(c)).collect();
    |                                                               ^^^^^^^^^^^^^^^^^^^^^^^
@@ -188,7 +200,7 @@ LL +     let digits: Vec<&char> = ['1', 'A'].iter().take_while(|c: &&char| c.is_
    |
 
 error: manual check for common ascii range
-  --> tests/ui/manual_is_ascii_check.rs:116:71
+  --> tests/ui/manual_is_ascii_check.rs:118:71
    |
 LL |     let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c| ('0'..='9').contains(c)).collect();
    |                                                                       ^^^^^^^^^^^^^^^^^^^^^^^
@@ -199,5 +211,17 @@ LL -     let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c| ('0'.
 LL +     let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c: &&mut char| c.is_ascii_digit()).collect();
    |
 
-error: aborting due to 29 previous errors
+error: manual check for common ascii range
+  --> tests/ui/manual_is_ascii_check.rs:120:71
+   |
+LL |     let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c| matches!(c, '0'..='9')).collect();
+   |                                                                       ^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL -     let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c| matches!(c, '0'..='9')).collect();
+LL +     let digits: Vec<&mut char> = ['1', 'A'].iter_mut().take_while(|c: &&mut char| c.is_ascii_digit()).collect();
+   |
+
+error: aborting due to 31 previous errors
 
diff --git a/src/tools/clippy/tests/ui/map_identity.fixed b/src/tools/clippy/tests/ui/map_identity.fixed
index 6c971ba6338..a10aae8cc12 100644
--- a/src/tools/clippy/tests/ui/map_identity.fixed
+++ b/src/tools/clippy/tests/ui/map_identity.fixed
@@ -1,3 +1,4 @@
+//@require-annotations-for-level: ERROR
 #![warn(clippy::map_identity)]
 #![allow(clippy::needless_return, clippy::disallowed_names)]
 
@@ -72,20 +73,33 @@ fn issue11764() {
 }
 
 fn issue13904() {
-    // don't lint: `it.next()` would not be legal as `it` is immutable
-    let it = [1, 2, 3].into_iter();
-    let _ = it.map(|x| x).next();
+    // lint, but there's a catch:
+    // when we remove the `.map()`, `it.next()` would require `it` to be mutable
+    // therefore, include that in the suggestion as well
+    let mut it = [1, 2, 3].into_iter();
+    let _ = it.next();
+    //~^ map_identity
+    //~| HELP: remove the call to `map`, and make `it` mutable
+
+    // lint
+    let mut index = [1, 2, 3].into_iter();
+    let mut subindex = (index.by_ref().take(3), 42);
+    let _ = subindex.0.next();
+    //~^ map_identity
+    //~| HELP: remove the call to `map`, and make `subindex` mutable
 
     // lint
     #[allow(unused_mut)]
     let mut it = [1, 2, 3].into_iter();
     let _ = it.next();
     //~^ map_identity
+    //~| HELP: remove the call to `map`
 
     // lint
     let it = [1, 2, 3].into_iter();
     let _ = { it }.next();
     //~^ map_identity
+    //~| HELP: remove the call to `map`
 }
 
 // same as `issue11764`, but for arrays
diff --git a/src/tools/clippy/tests/ui/map_identity.rs b/src/tools/clippy/tests/ui/map_identity.rs
index 59dcfcda3b6..fc6e018f924 100644
--- a/src/tools/clippy/tests/ui/map_identity.rs
+++ b/src/tools/clippy/tests/ui/map_identity.rs
@@ -1,3 +1,4 @@
+//@require-annotations-for-level: ERROR
 #![warn(clippy::map_identity)]
 #![allow(clippy::needless_return, clippy::disallowed_names)]
 
@@ -78,20 +79,33 @@ fn issue11764() {
 }
 
 fn issue13904() {
-    // don't lint: `it.next()` would not be legal as `it` is immutable
+    // lint, but there's a catch:
+    // when we remove the `.map()`, `it.next()` would require `it` to be mutable
+    // therefore, include that in the suggestion as well
     let it = [1, 2, 3].into_iter();
     let _ = it.map(|x| x).next();
+    //~^ map_identity
+    //~| HELP: remove the call to `map`, and make `it` mutable
+
+    // lint
+    let mut index = [1, 2, 3].into_iter();
+    let subindex = (index.by_ref().take(3), 42);
+    let _ = subindex.0.map(|n| n).next();
+    //~^ map_identity
+    //~| HELP: remove the call to `map`, and make `subindex` mutable
 
     // lint
     #[allow(unused_mut)]
     let mut it = [1, 2, 3].into_iter();
     let _ = it.map(|x| x).next();
     //~^ map_identity
+    //~| HELP: remove the call to `map`
 
     // lint
     let it = [1, 2, 3].into_iter();
     let _ = { it }.map(|x| x).next();
     //~^ map_identity
+    //~| HELP: remove the call to `map`
 }
 
 // same as `issue11764`, but for arrays
diff --git a/src/tools/clippy/tests/ui/map_identity.stderr b/src/tools/clippy/tests/ui/map_identity.stderr
index a50c0d6b87b..8d19d62cdc6 100644
--- a/src/tools/clippy/tests/ui/map_identity.stderr
+++ b/src/tools/clippy/tests/ui/map_identity.stderr
@@ -1,5 +1,5 @@
 error: unnecessary map of the identity function
-  --> tests/ui/map_identity.rs:7:47
+  --> tests/ui/map_identity.rs:8:47
    |
 LL |     let _: Vec<_> = x.iter().map(not_identity).map(|x| return x).collect();
    |                                               ^^^^^^^^^^^^^^^^^^ help: remove the call to `map`
@@ -8,25 +8,25 @@ LL |     let _: Vec<_> = x.iter().map(not_identity).map(|x| return x).collect();
    = help: to override `-D warnings` add `#[allow(clippy::map_identity)]`
 
 error: unnecessary map of the identity function
-  --> tests/ui/map_identity.rs:9:57
+  --> tests/ui/map_identity.rs:10:57
    |
 LL |     let _: Vec<_> = x.iter().map(std::convert::identity).map(|y| y).collect();
    |                                                         ^^^^^^^^^^^ help: remove the call to `map`
 
 error: unnecessary map of the identity function
-  --> tests/ui/map_identity.rs:9:29
+  --> tests/ui/map_identity.rs:10:29
    |
 LL |     let _: Vec<_> = x.iter().map(std::convert::identity).map(|y| y).collect();
    |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map`
 
 error: unnecessary map of the identity function
-  --> tests/ui/map_identity.rs:12:32
+  --> tests/ui/map_identity.rs:13:32
    |
 LL |     let _: Option<u8> = Some(3).map(|x| x);
    |                                ^^^^^^^^^^^ help: remove the call to `map`
 
 error: unnecessary map of the identity function
-  --> tests/ui/map_identity.rs:14:36
+  --> tests/ui/map_identity.rs:15:36
    |
 LL |       let _: Result<i8, f32> = Ok(-3).map(|x| {
    |  ____________________________________^
@@ -36,19 +36,19 @@ LL | |     });
    | |______^ help: remove the call to `map`
 
 error: unnecessary map of the identity function
-  --> tests/ui/map_identity.rs:25:36
+  --> tests/ui/map_identity.rs:26:36
    |
 LL |     let _: Result<u32, u32> = Ok(1).map_err(|a| a);
    |                                    ^^^^^^^^^^^^^^^ help: remove the call to `map_err`
 
 error: unnecessary map of the identity function
-  --> tests/ui/map_identity.rs:36:22
+  --> tests/ui/map_identity.rs:37:22
    |
 LL |     let _ = x.clone().map(|(x, y)| (x, y));
    |                      ^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map`
 
 error: unnecessary map of the identity function
-  --> tests/ui/map_identity.rs:38:22
+  --> tests/ui/map_identity.rs:39:22
    |
 LL |       let _ = x.clone().map(|(x, y)| {
    |  ______________________^
@@ -58,76 +58,100 @@ LL | |     });
    | |______^ help: remove the call to `map`
 
 error: unnecessary map of the identity function
-  --> tests/ui/map_identity.rs:42:22
+  --> tests/ui/map_identity.rs:43:22
    |
 LL |     let _ = x.clone().map(|(x, y)| return (x, y));
    |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map`
 
 error: unnecessary map of the identity function
-  --> tests/ui/map_identity.rs:46:22
+  --> tests/ui/map_identity.rs:47:22
    |
 LL |     let _ = y.clone().map(|(x, y, (z, (w,)))| (x, y, (z, (w,))));
    |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map`
 
 error: unnecessary map of the identity function
-  --> tests/ui/map_identity.rs:76:30
+  --> tests/ui/map_identity.rs:77:30
    |
 LL |     let _ = x.iter().copied().map(|(x, y)| (x, y));
    |                              ^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map`
 
 error: unnecessary map of the identity function
-  --> tests/ui/map_identity.rs:88:15
+  --> tests/ui/map_identity.rs:86:15
+   |
+LL |     let _ = it.map(|x| x).next();
+   |               ^^^^^^^^^^^
+   |
+help: remove the call to `map`, and make `it` mutable
+   |
+LL ~     let mut it = [1, 2, 3].into_iter();
+LL ~     let _ = it.next();
+   |
+
+error: unnecessary map of the identity function
+  --> tests/ui/map_identity.rs:93:23
+   |
+LL |     let _ = subindex.0.map(|n| n).next();
+   |                       ^^^^^^^^^^^
+   |
+help: remove the call to `map`, and make `subindex` mutable
+   |
+LL ~     let mut subindex = (index.by_ref().take(3), 42);
+LL ~     let _ = subindex.0.next();
+   |
+
+error: unnecessary map of the identity function
+  --> tests/ui/map_identity.rs:100:15
    |
 LL |     let _ = it.map(|x| x).next();
    |               ^^^^^^^^^^^ help: remove the call to `map`
 
 error: unnecessary map of the identity function
-  --> tests/ui/map_identity.rs:93:19
+  --> tests/ui/map_identity.rs:106:19
    |
 LL |     let _ = { it }.map(|x| x).next();
    |                   ^^^^^^^^^^^ help: remove the call to `map`
 
 error: unnecessary map of the identity function
-  --> tests/ui/map_identity.rs:105:30
+  --> tests/ui/map_identity.rs:119:30
    |
 LL |     let _ = x.iter().copied().map(|[x, y]| [x, y]);
    |                              ^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map`
 
 error: unnecessary map of the identity function
-  --> tests/ui/map_identity.rs:131:26
+  --> tests/ui/map_identity.rs:145:26
    |
 LL |     let _ = x.into_iter().map(|Foo { foo, bar }| Foo { foo, bar });
    |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map`
 
 error: unnecessary map of the identity function
-  --> tests/ui/map_identity.rs:135:26
+  --> tests/ui/map_identity.rs:149:26
    |
 LL |     let _ = x.into_iter().map(|Foo { foo, bar }| foo::Foo { foo, bar });
    |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map`
 
 error: unnecessary map of the identity function
-  --> tests/ui/map_identity.rs:143:26
+  --> tests/ui/map_identity.rs:157:26
    |
 LL |     let _ = x.into_iter().map(|Foo { foo, bar }| Foo { foo: foo, bar: bar });
    |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map`
 
 error: unnecessary map of the identity function
-  --> tests/ui/map_identity.rs:147:26
+  --> tests/ui/map_identity.rs:161:26
    |
 LL |     let _ = x.into_iter().map(|Foo { foo, bar }| Foo { bar, foo });
    |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map`
 
 error: unnecessary map of the identity function
-  --> tests/ui/map_identity.rs:157:26
+  --> tests/ui/map_identity.rs:171:26
    |
 LL |     let _ = x.into_iter().map(|Foo2(foo, bar)| Foo2(foo, bar));
    |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map`
 
 error: unnecessary map of the identity function
-  --> tests/ui/map_identity.rs:161:26
+  --> tests/ui/map_identity.rs:175:26
    |
 LL |     let _ = x.into_iter().map(|Foo2(foo, bar)| foo::Foo2(foo, bar));
    |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove the call to `map`
 
-error: aborting due to 20 previous errors
+error: aborting due to 22 previous errors
 
diff --git a/src/tools/clippy/tests/ui/match_as_ref.fixed b/src/tools/clippy/tests/ui/match_as_ref.fixed
index 8c07076af4a..a39f0c9299b 100644
--- a/src/tools/clippy/tests/ui/match_as_ref.fixed
+++ b/src/tools/clippy/tests/ui/match_as_ref.fixed
@@ -41,3 +41,33 @@ fn main() {
         None => None,
     };
 }
+
+mod issue15691 {
+    use std::ops::{Deref, DerefMut};
+
+    struct A(B);
+    struct B;
+
+    impl Deref for A {
+        type Target = B;
+        fn deref(&self) -> &Self::Target {
+            &self.0
+        }
+    }
+
+    impl DerefMut for A {
+        fn deref_mut(&mut self) -> &mut Self::Target {
+            &mut self.0
+        }
+    }
+
+    fn func() {
+        let mut a = Some(A(B));
+        let mut b = Some(B);
+        // Do not lint, we don't have `None => None`
+        let _ = match b {
+            Some(ref mut x) => Some(x),
+            None => a.as_deref_mut(),
+        };
+    }
+}
diff --git a/src/tools/clippy/tests/ui/match_as_ref.rs b/src/tools/clippy/tests/ui/match_as_ref.rs
index 3a5b1227331..04992816790 100644
--- a/src/tools/clippy/tests/ui/match_as_ref.rs
+++ b/src/tools/clippy/tests/ui/match_as_ref.rs
@@ -53,3 +53,33 @@ fn main() {
         None => None,
     };
 }
+
+mod issue15691 {
+    use std::ops::{Deref, DerefMut};
+
+    struct A(B);
+    struct B;
+
+    impl Deref for A {
+        type Target = B;
+        fn deref(&self) -> &Self::Target {
+            &self.0
+        }
+    }
+
+    impl DerefMut for A {
+        fn deref_mut(&mut self) -> &mut Self::Target {
+            &mut self.0
+        }
+    }
+
+    fn func() {
+        let mut a = Some(A(B));
+        let mut b = Some(B);
+        // Do not lint, we don't have `None => None`
+        let _ = match b {
+            Some(ref mut x) => Some(x),
+            None => a.as_deref_mut(),
+        };
+    }
+}
diff --git a/src/tools/clippy/tests/ui/match_bool.fixed b/src/tools/clippy/tests/ui/match_bool.fixed
index 1dfb82db120..876ae935afd 100644
--- a/src/tools/clippy/tests/ui/match_bool.fixed
+++ b/src/tools/clippy/tests/ui/match_bool.fixed
@@ -61,4 +61,17 @@ fn issue14099() {
     } }
 }
 
+fn issue15351() {
+    let mut d = false;
+    match d {
+        false => println!("foo"),
+        ref mut d => *d = false,
+    }
+
+    match d {
+        false => println!("foo"),
+        e => println!("{e}"),
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/match_bool.rs b/src/tools/clippy/tests/ui/match_bool.rs
index 719b4e51eb6..a134ad8346e 100644
--- a/src/tools/clippy/tests/ui/match_bool.rs
+++ b/src/tools/clippy/tests/ui/match_bool.rs
@@ -113,4 +113,17 @@ fn issue14099() {
     }
 }
 
+fn issue15351() {
+    let mut d = false;
+    match d {
+        false => println!("foo"),
+        ref mut d => *d = false,
+    }
+
+    match d {
+        false => println!("foo"),
+        e => println!("{e}"),
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/match_ref_pats.fixed b/src/tools/clippy/tests/ui/match_ref_pats.fixed
index 8add3da0c99..f727546838b 100644
--- a/src/tools/clippy/tests/ui/match_ref_pats.fixed
+++ b/src/tools/clippy/tests/ui/match_ref_pats.fixed
@@ -1,6 +1,12 @@
 #![warn(clippy::match_ref_pats)]
 #![allow(dead_code, unused_variables)]
-#![allow(clippy::enum_variant_names, clippy::equatable_if_let, clippy::uninlined_format_args)]
+#![allow(
+    clippy::enum_variant_names,
+    clippy::equatable_if_let,
+    clippy::uninlined_format_args,
+    clippy::empty_loop,
+    clippy::diverging_sub_expression
+)]
 
 fn ref_pats() {
     {
@@ -120,4 +126,32 @@ mod issue_7740 {
     }
 }
 
+mod issue15378 {
+    fn never_in_match() {
+        match unimplemented!() {
+            &_ => {},
+            &&&42 => {
+                todo!()
+            },
+            _ => {},
+        }
+
+        match panic!() {
+            &_ => {},
+            &&&42 => {
+                todo!()
+            },
+            _ => {},
+        }
+
+        match loop {} {
+            &_ => {},
+            &&&42 => {
+                todo!()
+            },
+            _ => {},
+        }
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/match_ref_pats.rs b/src/tools/clippy/tests/ui/match_ref_pats.rs
index 07889b0dfc2..eca4d584acd 100644
--- a/src/tools/clippy/tests/ui/match_ref_pats.rs
+++ b/src/tools/clippy/tests/ui/match_ref_pats.rs
@@ -1,6 +1,12 @@
 #![warn(clippy::match_ref_pats)]
 #![allow(dead_code, unused_variables)]
-#![allow(clippy::enum_variant_names, clippy::equatable_if_let, clippy::uninlined_format_args)]
+#![allow(
+    clippy::enum_variant_names,
+    clippy::equatable_if_let,
+    clippy::uninlined_format_args,
+    clippy::empty_loop,
+    clippy::diverging_sub_expression
+)]
 
 fn ref_pats() {
     {
@@ -120,4 +126,32 @@ mod issue_7740 {
     }
 }
 
+mod issue15378 {
+    fn never_in_match() {
+        match unimplemented!() {
+            &_ => {},
+            &&&42 => {
+                todo!()
+            },
+            _ => {},
+        }
+
+        match panic!() {
+            &_ => {},
+            &&&42 => {
+                todo!()
+            },
+            _ => {},
+        }
+
+        match loop {} {
+            &_ => {},
+            &&&42 => {
+                todo!()
+            },
+            _ => {},
+        }
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/match_ref_pats.stderr b/src/tools/clippy/tests/ui/match_ref_pats.stderr
index f81b290b32c..ecb08e6972d 100644
--- a/src/tools/clippy/tests/ui/match_ref_pats.stderr
+++ b/src/tools/clippy/tests/ui/match_ref_pats.stderr
@@ -1,5 +1,5 @@
 error: you don't need to add `&` to all patterns
-  --> tests/ui/match_ref_pats.rs:8:9
+  --> tests/ui/match_ref_pats.rs:14:9
    |
 LL | /         match v {
 LL | |
@@ -19,7 +19,7 @@ LL ~             None => println!("none"),
    |
 
 error: you don't need to add `&` to both the expression and the patterns
-  --> tests/ui/match_ref_pats.rs:26:5
+  --> tests/ui/match_ref_pats.rs:32:5
    |
 LL | /     match &w {
 LL | |
@@ -37,7 +37,7 @@ LL ~         None => println!("none"),
    |
 
 error: redundant pattern matching, consider using `is_none()`
-  --> tests/ui/match_ref_pats.rs:39:12
+  --> tests/ui/match_ref_pats.rs:45:12
    |
 LL |     if let &None = a {
    |     -------^^^^^---- help: try: `if a.is_none()`
@@ -46,13 +46,13 @@ LL |     if let &None = a {
    = help: to override `-D warnings` add `#[allow(clippy::redundant_pattern_matching)]`
 
 error: redundant pattern matching, consider using `is_none()`
-  --> tests/ui/match_ref_pats.rs:45:12
+  --> tests/ui/match_ref_pats.rs:51:12
    |
 LL |     if let &None = &b {
    |     -------^^^^^----- help: try: `if b.is_none()`
 
 error: you don't need to add `&` to all patterns
-  --> tests/ui/match_ref_pats.rs:106:9
+  --> tests/ui/match_ref_pats.rs:112:9
    |
 LL | /         match foobar_variant!(0) {
 LL | |
diff --git a/src/tools/clippy/tests/ui/missing_inline.rs b/src/tools/clippy/tests/ui/missing_inline.rs
index 223c7447975..8e937d60951 100644
--- a/src/tools/clippy/tests/ui/missing_inline.rs
+++ b/src/tools/clippy/tests/ui/missing_inline.rs
@@ -97,3 +97,10 @@ pub mod issue15301 {
         println!("Just called a Rust function from Rust!");
     }
 }
+
+pub mod issue15491 {
+    pub trait Foo {
+        #[allow(clippy::missing_inline_in_public_items)]
+        fn foo(&self) {}
+    }
+}
diff --git a/src/tools/clippy/tests/ui/missing_transmute_annotations_unfixable.rs b/src/tools/clippy/tests/ui/missing_transmute_annotations_unfixable.rs
new file mode 100644
index 00000000000..08ba3b791ee
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_transmute_annotations_unfixable.rs
@@ -0,0 +1,29 @@
+//@no-rustfix
+
+fn issue14984() {
+    async fn e() {}
+    async fn x() -> u32 {
+        0
+    }
+    async fn y() -> f32 {
+        0.0
+    };
+    let mut yy = unsafe { std::ptr::read(&y()) };
+    yy = unsafe { std::mem::transmute(std::ptr::read(&x())) };
+    //~^ missing_transmute_annotations
+
+    let mut zz = 0u8;
+    zz = unsafe { std::mem::transmute(std::ptr::read(&x())) };
+    //~^ missing_transmute_annotations
+
+    yy = unsafe { std::mem::transmute(zz) };
+    //~^ missing_transmute_annotations
+
+    fn a() -> impl Sized {
+        0u32
+    }
+
+    let mut b: f32 = 0.0;
+    b = unsafe { std::mem::transmute(a()) };
+    //~^ missing_transmute_annotations
+}
diff --git a/src/tools/clippy/tests/ui/missing_transmute_annotations_unfixable.stderr b/src/tools/clippy/tests/ui/missing_transmute_annotations_unfixable.stderr
new file mode 100644
index 00000000000..83efdce13f7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_transmute_annotations_unfixable.stderr
@@ -0,0 +1,36 @@
+error: transmute used without annotations
+  --> tests/ui/missing_transmute_annotations_unfixable.rs:12:29
+   |
+LL |     yy = unsafe { std::mem::transmute(std::ptr::read(&x())) };
+   |                             ^^^^^^^^^
+   |
+   = help: consider giving the source and destination types a name, and adding missing type annotations
+   = note: `-D clippy::missing-transmute-annotations` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::missing_transmute_annotations)]`
+
+error: transmute used without annotations
+  --> tests/ui/missing_transmute_annotations_unfixable.rs:16:29
+   |
+LL |     zz = unsafe { std::mem::transmute(std::ptr::read(&x())) };
+   |                             ^^^^^^^^^
+   |
+   = help: consider giving the origin type a name, and adding missing type annotations
+
+error: transmute used without annotations
+  --> tests/ui/missing_transmute_annotations_unfixable.rs:19:29
+   |
+LL |     yy = unsafe { std::mem::transmute(zz) };
+   |                             ^^^^^^^^^
+   |
+   = help: consider giving the destination type a name, and adding missing type annotations
+
+error: transmute used without annotations
+  --> tests/ui/missing_transmute_annotations_unfixable.rs:27:28
+   |
+LL |     b = unsafe { std::mem::transmute(a()) };
+   |                            ^^^^^^^^^
+   |
+   = help: consider giving `a()`'s type a name, and adding missing type annotations
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs
index 016fd89a7b7..132673d5164 100644
--- a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs
+++ b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.rs
@@ -1,10 +1,10 @@
 //@needs-asm-support
 //@aux-build:proc_macros.rs
-#![allow(unused)]
-#![allow(deref_nullptr)]
-#![allow(clippy::unnecessary_operation)]
-#![allow(dropping_copy_types)]
-#![allow(clippy::assign_op_pattern)]
+#![expect(
+    dropping_copy_types,
+    clippy::unnecessary_operation,
+    clippy::unnecessary_literal_unwrap
+)]
 #![warn(clippy::multiple_unsafe_ops_per_block)]
 
 extern crate proc_macros;
@@ -105,17 +105,17 @@ fn correct3() {
     }
 }
 
-// tests from the issue (https://github.com/rust-lang/rust-clippy/issues/10064)
-
-unsafe fn read_char_bad(ptr: *const u8) -> char {
-    unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
-    //~^ multiple_unsafe_ops_per_block
-}
+fn issue10064() {
+    unsafe fn read_char_bad(ptr: *const u8) -> char {
+        unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
+        //~^ multiple_unsafe_ops_per_block
+    }
 
-// no lint
-unsafe fn read_char_good(ptr: *const u8) -> char {
-    let int_value = unsafe { *ptr.cast::<u32>() };
-    unsafe { core::char::from_u32_unchecked(int_value) }
+    // no lint
+    unsafe fn read_char_good(ptr: *const u8) -> char {
+        let int_value = unsafe { *ptr.cast::<u32>() };
+        unsafe { core::char::from_u32_unchecked(int_value) }
+    }
 }
 
 // no lint
@@ -126,42 +126,87 @@ fn issue10259() {
     });
 }
 
-fn _fn_ptr(x: unsafe fn()) {
-    unsafe {
-        //~^ multiple_unsafe_ops_per_block
-        x();
-        x();
+fn issue10367() {
+    fn fn_ptr(x: unsafe fn()) {
+        unsafe {
+            //~^ multiple_unsafe_ops_per_block
+            x();
+            x();
+        }
     }
-}
 
-fn _assoc_const() {
-    trait X {
-        const X: unsafe fn();
+    fn assoc_const() {
+        trait X {
+            const X: unsafe fn();
+        }
+        fn _f<T: X>() {
+            unsafe {
+                //~^ multiple_unsafe_ops_per_block
+                T::X();
+                T::X();
+            }
+        }
     }
-    fn _f<T: X>() {
+
+    fn field_fn_ptr(x: unsafe fn()) {
+        struct X(unsafe fn());
+        let x = X(x);
         unsafe {
             //~^ multiple_unsafe_ops_per_block
-            T::X();
-            T::X();
+            x.0();
+            x.0();
         }
     }
 }
 
-fn _field_fn_ptr(x: unsafe fn()) {
-    struct X(unsafe fn());
-    let x = X(x);
+// await expands to an unsafe block with several operations, but this is fine.
+async fn issue11312() {
+    async fn helper() {}
+
+    helper().await;
+}
+
+async fn issue13879() {
+    async fn foo() {}
+
+    // no lint: nothing unsafe beyond the `await` which we ignore
+    unsafe {
+        foo().await;
+    }
+
+    // no lint: only one unsafe call beyond the `await`
+    unsafe {
+        not_very_safe();
+        foo().await;
+    }
+
+    // lint: two unsafe calls beyond the `await`
     unsafe {
         //~^ multiple_unsafe_ops_per_block
-        x.0();
-        x.0();
+        not_very_safe();
+        STATIC += 1;
+        foo().await;
     }
-}
 
-// await expands to an unsafe block with several operations, but this is fine.: #11312
-async fn await_desugaring_silent() {
-    async fn helper() {}
+    async unsafe fn foo_unchecked() {}
 
-    helper().await;
+    // no lint: only one unsafe call in the `await`ed expr
+    unsafe {
+        foo_unchecked().await;
+    }
+
+    // lint: one unsafe call in the `await`ed expr, and one outside
+    unsafe {
+        //~^ multiple_unsafe_ops_per_block
+        not_very_safe();
+        foo_unchecked().await;
+    }
+
+    // lint: two unsafe calls in the `await`ed expr
+    unsafe {
+        //~^ multiple_unsafe_ops_per_block
+        Some(foo_unchecked()).unwrap_unchecked().await;
+    }
 }
 
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr
index 3130cecc252..922a464c6b6 100644
--- a/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr
+++ b/src/tools/clippy/tests/ui/multiple_unsafe_ops_per_block.stderr
@@ -113,84 +113,147 @@ LL |         asm!("nop");
    |         ^^^^^^^^^^^
 
 error: this `unsafe` block contains 2 unsafe operations, expected only one
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:111:5
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:110:9
    |
-LL |     unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |         unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: unsafe function call occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:111:14
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:110:18
    |
-LL |     unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |         unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 note: raw pointer dereference occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:111:39
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:110:43
    |
-LL |     unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
-   |                                       ^^^^^^^^^^^^^^^^^^
+LL |         unsafe { char::from_u32_unchecked(*ptr.cast::<u32>()) }
+   |                                           ^^^^^^^^^^^^^^^^^^
 
 error: this `unsafe` block contains 2 unsafe operations, expected only one
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:130:5
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:131:9
    |
-LL | /     unsafe {
+LL | /         unsafe {
 LL | |
-LL | |         x();
-LL | |         x();
-LL | |     }
-   | |_____^
+LL | |             x();
+LL | |             x();
+LL | |         }
+   | |_________^
    |
 note: unsafe function call occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:132:9
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:133:13
    |
-LL |         x();
-   |         ^^^
+LL |             x();
+   |             ^^^
 note: unsafe function call occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:133:9
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:134:13
    |
-LL |         x();
-   |         ^^^
+LL |             x();
+   |             ^^^
 
 error: this `unsafe` block contains 2 unsafe operations, expected only one
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:142:9
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:143:13
+   |
+LL | /             unsafe {
+LL | |
+LL | |                 T::X();
+LL | |                 T::X();
+LL | |             }
+   | |_____________^
+   |
+note: unsafe function call occurs here
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:145:17
+   |
+LL |                 T::X();
+   |                 ^^^^^^
+note: unsafe function call occurs here
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:146:17
+   |
+LL |                 T::X();
+   |                 ^^^^^^
+
+error: this `unsafe` block contains 2 unsafe operations, expected only one
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:154:9
    |
 LL | /         unsafe {
 LL | |
-LL | |             T::X();
-LL | |             T::X();
+LL | |             x.0();
+LL | |             x.0();
 LL | |         }
    | |_________^
    |
 note: unsafe function call occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:144:13
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:156:13
    |
-LL |             T::X();
-   |             ^^^^^^
+LL |             x.0();
+   |             ^^^^^
 note: unsafe function call occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:145:13
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:157:13
    |
-LL |             T::X();
-   |             ^^^^^^
+LL |             x.0();
+   |             ^^^^^
 
 error: this `unsafe` block contains 2 unsafe operations, expected only one
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:153:5
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:184:5
    |
 LL | /     unsafe {
 LL | |
-LL | |         x.0();
-LL | |         x.0();
+LL | |         not_very_safe();
+LL | |         STATIC += 1;
+LL | |         foo().await;
 LL | |     }
    | |_____^
    |
 note: unsafe function call occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:155:9
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:186:9
+   |
+LL |         not_very_safe();
+   |         ^^^^^^^^^^^^^^^
+note: modification of a mutable static occurs here
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:187:9
+   |
+LL |         STATIC += 1;
+   |         ^^^^^^^^^^^
+
+error: this `unsafe` block contains 2 unsafe operations, expected only one
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:199:5
+   |
+LL | /     unsafe {
+LL | |
+LL | |         not_very_safe();
+LL | |         foo_unchecked().await;
+LL | |     }
+   | |_____^
+   |
+note: unsafe function call occurs here
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:201:9
+   |
+LL |         not_very_safe();
+   |         ^^^^^^^^^^^^^^^
+note: unsafe function call occurs here
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:202:9
+   |
+LL |         foo_unchecked().await;
+   |         ^^^^^^^^^^^^^^^
+
+error: this `unsafe` block contains 2 unsafe operations, expected only one
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:206:5
+   |
+LL | /     unsafe {
+LL | |
+LL | |         Some(foo_unchecked()).unwrap_unchecked().await;
+LL | |     }
+   | |_____^
+   |
+note: unsafe method call occurs here
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:208:9
    |
-LL |         x.0();
-   |         ^^^^^
+LL |         Some(foo_unchecked()).unwrap_unchecked().await;
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 note: unsafe function call occurs here
-  --> tests/ui/multiple_unsafe_ops_per_block.rs:156:9
+  --> tests/ui/multiple_unsafe_ops_per_block.rs:208:14
    |
-LL |         x.0();
-   |         ^^^^^
+LL |         Some(foo_unchecked()).unwrap_unchecked().await;
+   |              ^^^^^^^^^^^^^^^
 
-error: aborting due to 8 previous errors
+error: aborting due to 11 previous errors
 
diff --git a/src/tools/clippy/tests/ui/mut_reference.fixed b/src/tools/clippy/tests/ui/mut_reference.fixed
new file mode 100644
index 00000000000..03d854099e6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/mut_reference.fixed
@@ -0,0 +1,170 @@
+#![allow(clippy::mut_mut)]
+
+fn takes_ref(a: &i32) {}
+fn takes_refmut(a: &mut i32) {}
+fn takes_ref_ref(a: &&i32) {}
+fn takes_refmut_ref(a: &mut &i32) {}
+fn takes_ref_refmut(a: &&mut i32) {}
+fn takes_refmut_refmut(a: &mut &mut i32) {}
+fn takes_raw_const(a: *const i32) {}
+fn takes_raw_mut(a: *mut i32) {}
+
+mod issue11268 {
+    macro_rules! x {
+        (1 $f:expr) => {
+            $f(&mut 1);
+        };
+        (2 $f:expr) => {
+            $f(&mut &1)
+        };
+        (3 $f:expr) => {
+            $f(&mut &mut 1)
+        };
+        (4 $f:expr) => {
+            let mut a = 1;
+            $f(&raw mut a)
+        };
+    }
+
+    fn f() {
+        x!(1 super::takes_ref);
+        x!(1 super::takes_refmut);
+        x!(2 super::takes_refmut_ref);
+        x!(3 super::takes_ref_refmut);
+        x!(3 super::takes_refmut_refmut);
+        x!(4 super::takes_raw_const);
+        x!(4 super::takes_raw_mut);
+    }
+}
+
+struct MyStruct;
+
+impl MyStruct {
+    fn takes_ref(&self, a: &i32) {}
+    fn takes_refmut(&self, a: &mut i32) {}
+    fn takes_ref_ref(&self, a: &&i32) {}
+    fn takes_refmut_ref(&self, a: &mut &i32) {}
+    fn takes_ref_refmut(&self, a: &&mut i32) {}
+    fn takes_refmut_refmut(&self, a: &mut &mut i32) {}
+    fn takes_raw_const(&self, a: *const i32) {}
+    fn takes_raw_mut(&self, a: *mut i32) {}
+}
+
+#[warn(clippy::unnecessary_mut_passed)]
+fn main() {
+    // Functions
+    takes_ref(&42);
+    //~^ unnecessary_mut_passed
+    takes_ref_ref(&&42);
+    //~^ unnecessary_mut_passed
+    takes_ref_refmut(&&mut 42);
+    //~^ unnecessary_mut_passed
+    takes_raw_const(&42);
+    //~^ unnecessary_mut_passed
+
+    let as_ptr: fn(&i32) = takes_ref;
+    as_ptr(&42);
+    //~^ unnecessary_mut_passed
+    let as_ptr: fn(&&i32) = takes_ref_ref;
+    as_ptr(&&42);
+    //~^ unnecessary_mut_passed
+    let as_ptr: fn(&&mut i32) = takes_ref_refmut;
+    as_ptr(&&mut 42);
+    //~^ unnecessary_mut_passed
+    let as_ptr: fn(*const i32) = takes_raw_const;
+    as_ptr(&42);
+    //~^ unnecessary_mut_passed
+
+    // Methods
+    let my_struct = MyStruct;
+    my_struct.takes_ref(&42);
+    //~^ unnecessary_mut_passed
+    my_struct.takes_ref_ref(&&42);
+    //~^ unnecessary_mut_passed
+    my_struct.takes_ref_refmut(&&mut 42);
+    //~^ unnecessary_mut_passed
+    my_struct.takes_raw_const(&42);
+    //~^ unnecessary_mut_passed
+
+    // No error
+
+    // Functions
+    takes_ref(&42);
+    let as_ptr: fn(&i32) = takes_ref;
+    as_ptr(&42);
+
+    takes_refmut(&mut 42);
+    let as_ptr: fn(&mut i32) = takes_refmut;
+    as_ptr(&mut 42);
+
+    takes_ref_ref(&&42);
+    let as_ptr: fn(&&i32) = takes_ref_ref;
+    as_ptr(&&42);
+
+    takes_refmut_ref(&mut &42);
+    let as_ptr: fn(&mut &i32) = takes_refmut_ref;
+    as_ptr(&mut &42);
+
+    takes_ref_refmut(&&mut 42);
+    let as_ptr: fn(&&mut i32) = takes_ref_refmut;
+    as_ptr(&&mut 42);
+
+    takes_refmut_refmut(&mut &mut 42);
+    let as_ptr: fn(&mut &mut i32) = takes_refmut_refmut;
+    as_ptr(&mut &mut 42);
+
+    takes_raw_const(&42);
+    let as_ptr: fn(*const i32) = takes_raw_const;
+    as_ptr(&42);
+
+    takes_raw_mut(&mut 42);
+    let as_ptr: fn(*mut i32) = takes_raw_mut;
+    as_ptr(&mut 42);
+
+    let a = &mut 42;
+    let b = &mut &42;
+    let c = &mut &mut 42;
+    takes_ref(a);
+    takes_ref_ref(b);
+    takes_ref_refmut(c);
+    takes_raw_const(a);
+
+    // Methods
+    my_struct.takes_ref(&42);
+    my_struct.takes_refmut(&mut 42);
+    my_struct.takes_ref_ref(&&42);
+    my_struct.takes_refmut_ref(&mut &42);
+    my_struct.takes_ref_refmut(&&mut 42);
+    my_struct.takes_refmut_refmut(&mut &mut 42);
+    my_struct.takes_raw_const(&42);
+    my_struct.takes_raw_mut(&mut 42);
+    my_struct.takes_ref(a);
+    my_struct.takes_ref_ref(b);
+    my_struct.takes_ref_refmut(c);
+    my_struct.takes_raw_const(a);
+    my_struct.takes_raw_mut(a);
+}
+
+// not supported currently
+fn raw_ptrs(my_struct: MyStruct) {
+    let mut n = 42;
+
+    takes_raw_const(&raw mut n);
+
+    let as_ptr: fn(*const i32) = takes_raw_const;
+    as_ptr(&raw mut n);
+
+    my_struct.takes_raw_const(&raw mut n);
+
+    // No error
+
+    takes_raw_const(&raw const n);
+    takes_raw_mut(&raw mut n);
+
+    let a = &raw mut n;
+    takes_raw_const(a);
+
+    my_struct.takes_raw_const(&raw const n);
+    my_struct.takes_raw_mut(&raw mut n);
+    my_struct.takes_raw_const(a);
+}
diff --git a/src/tools/clippy/tests/ui/mut_reference.rs b/src/tools/clippy/tests/ui/mut_reference.rs
index f664c373cdc..80e3f506927 100644
--- a/src/tools/clippy/tests/ui/mut_reference.rs
+++ b/src/tools/clippy/tests/ui/mut_reference.rs
@@ -1,60 +1,170 @@
-#![allow(unused_variables, dead_code)]
-//@no-rustfix
-fn takes_an_immutable_reference(a: &i32) {}
-fn takes_a_mutable_reference(a: &mut i32) {}
+#![allow(clippy::mut_mut)]
+
+fn takes_ref(a: &i32) {}
+fn takes_refmut(a: &mut i32) {}
+fn takes_ref_ref(a: &&i32) {}
+fn takes_refmut_ref(a: &mut &i32) {}
+fn takes_ref_refmut(a: &&mut i32) {}
+fn takes_refmut_refmut(a: &mut &mut i32) {}
+fn takes_raw_const(a: *const i32) {}
+fn takes_raw_mut(a: *mut i32) {}
 
 mod issue11268 {
     macro_rules! x {
-        ($f:expr) => {
+        (1 $f:expr) => {
             $f(&mut 1);
         };
+        (2 $f:expr) => {
+            $f(&mut &1)
+        };
+        (3 $f:expr) => {
+            $f(&mut &mut 1)
+        };
+        (4 $f:expr) => {
+            let mut a = 1;
+            $f(&raw mut a)
+        };
     }
 
     fn f() {
-        x!(super::takes_an_immutable_reference);
-        x!(super::takes_a_mutable_reference);
+        x!(1 super::takes_ref);
+        x!(1 super::takes_refmut);
+        x!(2 super::takes_refmut_ref);
+        x!(3 super::takes_ref_refmut);
+        x!(3 super::takes_refmut_refmut);
+        x!(4 super::takes_raw_const);
+        x!(4 super::takes_raw_mut);
     }
 }
 
 struct MyStruct;
 
 impl MyStruct {
-    fn takes_an_immutable_reference(&self, a: &i32) {}
-
-    fn takes_a_mutable_reference(&self, a: &mut i32) {}
+    fn takes_ref(&self, a: &i32) {}
+    fn takes_refmut(&self, a: &mut i32) {}
+    fn takes_ref_ref(&self, a: &&i32) {}
+    fn takes_refmut_ref(&self, a: &mut &i32) {}
+    fn takes_ref_refmut(&self, a: &&mut i32) {}
+    fn takes_refmut_refmut(&self, a: &mut &mut i32) {}
+    fn takes_raw_const(&self, a: *const i32) {}
+    fn takes_raw_mut(&self, a: *mut i32) {}
 }
 
 #[warn(clippy::unnecessary_mut_passed)]
 fn main() {
     // Functions
-    takes_an_immutable_reference(&mut 42);
+    takes_ref(&mut 42);
+    //~^ unnecessary_mut_passed
+    takes_ref_ref(&mut &42);
+    //~^ unnecessary_mut_passed
+    takes_ref_refmut(&mut &mut 42);
+    //~^ unnecessary_mut_passed
+    takes_raw_const(&mut 42);
     //~^ unnecessary_mut_passed
 
-    let as_ptr: fn(&i32) = takes_an_immutable_reference;
+    let as_ptr: fn(&i32) = takes_ref;
+    as_ptr(&mut 42);
+    //~^ unnecessary_mut_passed
+    let as_ptr: fn(&&i32) = takes_ref_ref;
+    as_ptr(&mut &42);
+    //~^ unnecessary_mut_passed
+    let as_ptr: fn(&&mut i32) = takes_ref_refmut;
+    as_ptr(&mut &mut 42);
+    //~^ unnecessary_mut_passed
+    let as_ptr: fn(*const i32) = takes_raw_const;
     as_ptr(&mut 42);
     //~^ unnecessary_mut_passed
 
     // Methods
     let my_struct = MyStruct;
-    my_struct.takes_an_immutable_reference(&mut 42);
+    my_struct.takes_ref(&mut 42);
+    //~^ unnecessary_mut_passed
+    my_struct.takes_ref_ref(&mut &42);
+    //~^ unnecessary_mut_passed
+    my_struct.takes_ref_refmut(&mut &mut 42);
+    //~^ unnecessary_mut_passed
+    my_struct.takes_raw_const(&mut 42);
     //~^ unnecessary_mut_passed
 
     // No error
 
     // Functions
-    takes_an_immutable_reference(&42);
-    let as_ptr: fn(&i32) = takes_an_immutable_reference;
+    takes_ref(&42);
+    let as_ptr: fn(&i32) = takes_ref;
     as_ptr(&42);
 
-    takes_a_mutable_reference(&mut 42);
-    let as_ptr: fn(&mut i32) = takes_a_mutable_reference;
+    takes_refmut(&mut 42);
+    let as_ptr: fn(&mut i32) = takes_refmut;
+    as_ptr(&mut 42);
+
+    takes_ref_ref(&&42);
+    let as_ptr: fn(&&i32) = takes_ref_ref;
+    as_ptr(&&42);
+
+    takes_refmut_ref(&mut &42);
+    let as_ptr: fn(&mut &i32) = takes_refmut_ref;
+    as_ptr(&mut &42);
+
+    takes_ref_refmut(&&mut 42);
+    let as_ptr: fn(&&mut i32) = takes_ref_refmut;
+    as_ptr(&&mut 42);
+
+    takes_refmut_refmut(&mut &mut 42);
+    let as_ptr: fn(&mut &mut i32) = takes_refmut_refmut;
+    as_ptr(&mut &mut 42);
+
+    takes_raw_const(&42);
+    let as_ptr: fn(*const i32) = takes_raw_const;
+    as_ptr(&42);
+
+    takes_raw_mut(&mut 42);
+    let as_ptr: fn(*mut i32) = takes_raw_mut;
     as_ptr(&mut 42);
 
     let a = &mut 42;
-    takes_an_immutable_reference(a);
+    let b = &mut &42;
+    let c = &mut &mut 42;
+    takes_ref(a);
+    takes_ref_ref(b);
+    takes_ref_refmut(c);
+    takes_raw_const(a);
 
     // Methods
-    my_struct.takes_an_immutable_reference(&42);
-    my_struct.takes_a_mutable_reference(&mut 42);
-    my_struct.takes_an_immutable_reference(a);
+    my_struct.takes_ref(&42);
+    my_struct.takes_refmut(&mut 42);
+    my_struct.takes_ref_ref(&&42);
+    my_struct.takes_refmut_ref(&mut &42);
+    my_struct.takes_ref_refmut(&&mut 42);
+    my_struct.takes_refmut_refmut(&mut &mut 42);
+    my_struct.takes_raw_const(&42);
+    my_struct.takes_raw_mut(&mut 42);
+    my_struct.takes_ref(a);
+    my_struct.takes_ref_ref(b);
+    my_struct.takes_ref_refmut(c);
+    my_struct.takes_raw_const(a);
+    my_struct.takes_raw_mut(a);
+}
+
+// not supported currently
+fn raw_ptrs(my_struct: MyStruct) {
+    let mut n = 42;
+
+    takes_raw_const(&raw mut n);
+
+    let as_ptr: fn(*const i32) = takes_raw_const;
+    as_ptr(&raw mut n);
+
+    my_struct.takes_raw_const(&raw mut n);
+
+    // No error
+
+    takes_raw_const(&raw const n);
+    takes_raw_mut(&raw mut n);
+
+    let a = &raw mut n;
+    takes_raw_const(a);
+
+    my_struct.takes_raw_const(&raw const n);
+    my_struct.takes_raw_mut(&raw mut n);
+    my_struct.takes_raw_const(a);
 }
diff --git a/src/tools/clippy/tests/ui/mut_reference.stderr b/src/tools/clippy/tests/ui/mut_reference.stderr
index 474221329c2..5ecfaa37416 100644
--- a/src/tools/clippy/tests/ui/mut_reference.stderr
+++ b/src/tools/clippy/tests/ui/mut_reference.stderr
@@ -1,23 +1,77 @@
-error: the function `takes_an_immutable_reference` doesn't need a mutable reference
-  --> tests/ui/mut_reference.rs:30:34
+error: the function `takes_ref` doesn't need a mutable reference
+  --> tests/ui/mut_reference.rs:56:15
    |
-LL |     takes_an_immutable_reference(&mut 42);
-   |                                  ^^^^^^^
+LL |     takes_ref(&mut 42);
+   |               ^^^^^^^ help: remove this `mut`: `&42`
    |
    = note: `-D clippy::unnecessary-mut-passed` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::unnecessary_mut_passed)]`
 
+error: the function `takes_ref_ref` doesn't need a mutable reference
+  --> tests/ui/mut_reference.rs:58:19
+   |
+LL |     takes_ref_ref(&mut &42);
+   |                   ^^^^^^^^ help: remove this `mut`: `&&42`
+
+error: the function `takes_ref_refmut` doesn't need a mutable reference
+  --> tests/ui/mut_reference.rs:60:22
+   |
+LL |     takes_ref_refmut(&mut &mut 42);
+   |                      ^^^^^^^^^^^^ help: remove this `mut`: `&&mut 42`
+
+error: the function `takes_raw_const` doesn't need a mutable reference
+  --> tests/ui/mut_reference.rs:62:21
+   |
+LL |     takes_raw_const(&mut 42);
+   |                     ^^^^^^^ help: remove this `mut`: `&42`
+
+error: the function `as_ptr` doesn't need a mutable reference
+  --> tests/ui/mut_reference.rs:66:12
+   |
+LL |     as_ptr(&mut 42);
+   |            ^^^^^^^ help: remove this `mut`: `&42`
+
+error: the function `as_ptr` doesn't need a mutable reference
+  --> tests/ui/mut_reference.rs:69:12
+   |
+LL |     as_ptr(&mut &42);
+   |            ^^^^^^^^ help: remove this `mut`: `&&42`
+
+error: the function `as_ptr` doesn't need a mutable reference
+  --> tests/ui/mut_reference.rs:72:12
+   |
+LL |     as_ptr(&mut &mut 42);
+   |            ^^^^^^^^^^^^ help: remove this `mut`: `&&mut 42`
+
 error: the function `as_ptr` doesn't need a mutable reference
-  --> tests/ui/mut_reference.rs:34:12
+  --> tests/ui/mut_reference.rs:75:12
    |
 LL |     as_ptr(&mut 42);
-   |            ^^^^^^^
+   |            ^^^^^^^ help: remove this `mut`: `&42`
+
+error: the method `takes_ref` doesn't need a mutable reference
+  --> tests/ui/mut_reference.rs:80:25
+   |
+LL |     my_struct.takes_ref(&mut 42);
+   |                         ^^^^^^^ help: remove this `mut`: `&42`
+
+error: the method `takes_ref_ref` doesn't need a mutable reference
+  --> tests/ui/mut_reference.rs:82:29
+   |
+LL |     my_struct.takes_ref_ref(&mut &42);
+   |                             ^^^^^^^^ help: remove this `mut`: `&&42`
+
+error: the method `takes_ref_refmut` doesn't need a mutable reference
+  --> tests/ui/mut_reference.rs:84:32
+   |
+LL |     my_struct.takes_ref_refmut(&mut &mut 42);
+   |                                ^^^^^^^^^^^^ help: remove this `mut`: `&&mut 42`
 
-error: the method `takes_an_immutable_reference` doesn't need a mutable reference
-  --> tests/ui/mut_reference.rs:39:44
+error: the method `takes_raw_const` doesn't need a mutable reference
+  --> tests/ui/mut_reference.rs:86:31
    |
-LL |     my_struct.takes_an_immutable_reference(&mut 42);
-   |                                            ^^^^^^^
+LL |     my_struct.takes_raw_const(&mut 42);
+   |                               ^^^^^^^ help: remove this `mut`: `&42`
 
-error: aborting due to 3 previous errors
+error: aborting due to 12 previous errors
 
diff --git a/src/tools/clippy/tests/ui/needless_bool/fixable.stderr b/src/tools/clippy/tests/ui/needless_bool/fixable.stderr
index 3f117ee5a50..9404d07ba0e 100644
--- a/src/tools/clippy/tests/ui/needless_bool/fixable.stderr
+++ b/src/tools/clippy/tests/ui/needless_bool/fixable.stderr
@@ -135,7 +135,7 @@ error: equality checks against true are unnecessary
   --> tests/ui/needless_bool/fixable.rs:157:8
    |
 LL |     if x == true {};
-   |        ^^^^^^^^^ help: try simplifying it as shown: `x`
+   |        ^^^^^^^^^ help: try: `x`
    |
    = note: `-D clippy::bool-comparison` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::bool_comparison)]`
@@ -144,19 +144,19 @@ error: equality checks against false can be replaced by a negation
   --> tests/ui/needless_bool/fixable.rs:162:8
    |
 LL |     if x == false {};
-   |        ^^^^^^^^^^ help: try simplifying it as shown: `!x`
+   |        ^^^^^^^^^^ help: try: `!x`
 
 error: equality checks against true are unnecessary
   --> tests/ui/needless_bool/fixable.rs:173:8
    |
 LL |     if x == true {};
-   |        ^^^^^^^^^ help: try simplifying it as shown: `x`
+   |        ^^^^^^^^^ help: try: `x`
 
 error: equality checks against false can be replaced by a negation
   --> tests/ui/needless_bool/fixable.rs:175:8
    |
 LL |     if x == false {};
-   |        ^^^^^^^^^^ help: try simplifying it as shown: `!x`
+   |        ^^^^^^^^^^ help: try: `!x`
 
 error: this if-then-else expression returns a bool literal
   --> tests/ui/needless_bool/fixable.rs:185:12
diff --git a/src/tools/clippy/tests/ui/needless_for_each_unfixable.rs b/src/tools/clippy/tests/ui/needless_for_each_unfixable.rs
index 159cbe49828..24ce5786b91 100644
--- a/src/tools/clippy/tests/ui/needless_for_each_unfixable.rs
+++ b/src/tools/clippy/tests/ui/needless_for_each_unfixable.rs
@@ -1,6 +1,6 @@
 //@no-rustfix: overlapping suggestions
 #![warn(clippy::needless_for_each)]
-#![allow(clippy::needless_return, clippy::uninlined_format_args)]
+#![allow(clippy::needless_return)]
 
 fn main() {
     let v: Vec<i32> = Vec::new();
@@ -11,7 +11,22 @@ fn main() {
         if *v == 10 {
             return;
         } else {
-            println!("{}", v);
+            println!("{v}");
         }
     });
 }
+
+fn issue9912() {
+    let mut i = 0;
+    // Changing this to a `for` loop would break type inference
+    [].iter().for_each(move |_: &i32| {
+        //~^ needless_for_each
+        i += 1;
+    });
+
+    // Changing this would actually be okay, but we still suggest `MaybeIncorrect`ly
+    [1i32].iter().for_each(move |_: &i32| {
+        //~^ needless_for_each
+        i += 1;
+    });
+}
diff --git a/src/tools/clippy/tests/ui/needless_for_each_unfixable.stderr b/src/tools/clippy/tests/ui/needless_for_each_unfixable.stderr
index 3a3a240c5a3..6bc56db68a0 100644
--- a/src/tools/clippy/tests/ui/needless_for_each_unfixable.stderr
+++ b/src/tools/clippy/tests/ui/needless_for_each_unfixable.stderr
@@ -19,7 +19,7 @@ LL +
 LL +         if *v == 10 {
 LL +             return;
 LL +         } else {
-LL +             println!("{}", v);
+LL +             println!("{v}");
 LL +         }
 LL +     }
    |
@@ -29,5 +29,39 @@ LL -             return;
 LL +             continue;
    |
 
-error: aborting due to 1 previous error
+error: needless use of `for_each`
+  --> tests/ui/needless_for_each_unfixable.rs:22:5
+   |
+LL | /     [].iter().for_each(move |_: &i32| {
+LL | |
+LL | |         i += 1;
+LL | |     });
+   | |_______^
+   |
+help: try
+   |
+LL ~     for _ in [].iter() {
+LL +
+LL +         i += 1;
+LL +     }
+   |
+
+error: needless use of `for_each`
+  --> tests/ui/needless_for_each_unfixable.rs:28:5
+   |
+LL | /     [1i32].iter().for_each(move |_: &i32| {
+LL | |
+LL | |         i += 1;
+LL | |     });
+   | |_______^
+   |
+help: try
+   |
+LL ~     for _ in [1i32].iter() {
+LL +
+LL +         i += 1;
+LL +     }
+   |
+
+error: aborting due to 3 previous errors
 
diff --git a/src/tools/clippy/tests/ui/needless_range_loop.rs b/src/tools/clippy/tests/ui/needless_range_loop.rs
index 70cf9fa7369..ea4591d8b71 100644
--- a/src/tools/clippy/tests/ui/needless_range_loop.rs
+++ b/src/tools/clippy/tests/ui/needless_range_loop.rs
@@ -210,3 +210,35 @@ fn needless_loop() {
         black_box([1, 2, 3, 4, 5, 6, 7, 8][i]);
     }
 }
+
+fn issue_15068() {
+    let a = vec![vec![0u8; MAX_LEN]; MAX_LEN];
+    let b = vec![0u8; MAX_LEN];
+
+    for i in 0..MAX_LEN {
+        // no error
+        let _ = a[0][i];
+        let _ = b[i];
+    }
+
+    for i in 0..MAX_LEN {
+        // no error
+        let _ = a[i][0];
+        let _ = b[i];
+    }
+
+    for i in 0..MAX_LEN {
+        // no error
+        let _ = a[i][b[i] as usize];
+    }
+
+    for i in 0..MAX_LEN {
+        //~^ needless_range_loop
+        let _ = a[i][i];
+    }
+
+    for i in 0..MAX_LEN {
+        //~^ needless_range_loop
+        let _ = a[0][i];
+    }
+}
diff --git a/src/tools/clippy/tests/ui/needless_range_loop.stderr b/src/tools/clippy/tests/ui/needless_range_loop.stderr
index 23c085f9d3b..33a519d8a80 100644
--- a/src/tools/clippy/tests/ui/needless_range_loop.stderr
+++ b/src/tools/clippy/tests/ui/needless_range_loop.stderr
@@ -168,5 +168,29 @@ LL -     for i in 0..vec.len() {
 LL +     for (i, <item>) in vec.iter_mut().enumerate() {
    |
 
-error: aborting due to 14 previous errors
+error: the loop variable `i` is used to index `a`
+  --> tests/ui/needless_range_loop.rs:235:14
+   |
+LL |     for i in 0..MAX_LEN {
+   |              ^^^^^^^^^^
+   |
+help: consider using an iterator and enumerate()
+   |
+LL -     for i in 0..MAX_LEN {
+LL +     for (i, <item>) in a.iter().enumerate().take(MAX_LEN) {
+   |
+
+error: the loop variable `i` is only used to index `a`
+  --> tests/ui/needless_range_loop.rs:240:14
+   |
+LL |     for i in 0..MAX_LEN {
+   |              ^^^^^^^^^^
+   |
+help: consider using an iterator
+   |
+LL -     for i in 0..MAX_LEN {
+LL +     for <item> in a.iter().take(MAX_LEN) {
+   |
+
+error: aborting due to 16 previous errors
 
diff --git a/src/tools/clippy/tests/ui/needless_return.fixed b/src/tools/clippy/tests/ui/needless_return.fixed
index d571b97f519..f5f8bb21e81 100644
--- a/src/tools/clippy/tests/ui/needless_return.fixed
+++ b/src/tools/clippy/tests/ui/needless_return.fixed
@@ -517,3 +517,10 @@ mod else_ifs {
         }
     }
 }
+
+fn issue14474() -> u64 {
+    return 456;
+
+    #[cfg(false)]
+    123
+}
diff --git a/src/tools/clippy/tests/ui/needless_return.rs b/src/tools/clippy/tests/ui/needless_return.rs
index 2e4348ea338..495516c1c2e 100644
--- a/src/tools/clippy/tests/ui/needless_return.rs
+++ b/src/tools/clippy/tests/ui/needless_return.rs
@@ -526,3 +526,10 @@ mod else_ifs {
         }
     }
 }
+
+fn issue14474() -> u64 {
+    return 456;
+
+    #[cfg(false)]
+    123
+}
diff --git a/src/tools/clippy/tests/ui/never_loop.rs b/src/tools/clippy/tests/ui/never_loop.rs
index 48d4b8ad151..01db64a446c 100644
--- a/src/tools/clippy/tests/ui/never_loop.rs
+++ b/src/tools/clippy/tests/ui/never_loop.rs
@@ -498,3 +498,27 @@ fn issue15059() {
         ()
     }
 }
+
+fn issue15350() {
+    'bar: for _ in 0..100 {
+        //~^ never_loop
+        loop {
+            //~^ never_loop
+            println!("This will still run");
+            break 'bar;
+        }
+    }
+
+    'foo: for _ in 0..100 {
+        //~^ never_loop
+        loop {
+            //~^ never_loop
+            println!("This will still run");
+            loop {
+                //~^ never_loop
+                println!("This will still run");
+                break 'foo;
+            }
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/ui/never_loop.stderr b/src/tools/clippy/tests/ui/never_loop.stderr
index 54b463266a3..4fda06cff4a 100644
--- a/src/tools/clippy/tests/ui/never_loop.stderr
+++ b/src/tools/clippy/tests/ui/never_loop.stderr
@@ -193,6 +193,19 @@ LL | |         return;
 LL | |     }
    | |_____^
    |
+help: this code is unreachable. Consider moving the reachable parts out
+  --> tests/ui/never_loop.rs:436:9
+   |
+LL | /         loop {
+LL | |
+LL | |             break 'outer;
+LL | |         }
+   | |_________^
+help: this code is unreachable. Consider moving the reachable parts out
+  --> tests/ui/never_loop.rs:440:9
+   |
+LL |         return;
+   |         ^^^^^^^
 help: if you need the first element of the iterator, try writing
    |
 LL -     'outer: for v in 0..10 {
@@ -297,5 +310,87 @@ LL ~
 LL ~         
    |
 
-error: aborting due to 24 previous errors
+error: this loop never actually loops
+  --> tests/ui/never_loop.rs:503:5
+   |
+LL | /     'bar: for _ in 0..100 {
+LL | |
+LL | |         loop {
+...  |
+LL | |     }
+   | |_____^
+   |
+help: this code is unreachable. Consider moving the reachable parts out
+  --> tests/ui/never_loop.rs:505:9
+   |
+LL | /         loop {
+LL | |
+LL | |             println!("This will still run");
+LL | |             break 'bar;
+LL | |         }
+   | |_________^
+help: if you need the first element of the iterator, try writing
+   |
+LL -     'bar: for _ in 0..100 {
+LL +     if let Some(_) = (0..100).next() {
+   |
+
+error: this loop never actually loops
+  --> tests/ui/never_loop.rs:505:9
+   |
+LL | /         loop {
+LL | |
+LL | |             println!("This will still run");
+LL | |             break 'bar;
+LL | |         }
+   | |_________^
+
+error: this loop never actually loops
+  --> tests/ui/never_loop.rs:512:5
+   |
+LL | /     'foo: for _ in 0..100 {
+LL | |
+LL | |         loop {
+...  |
+LL | |     }
+   | |_____^
+   |
+help: this code is unreachable. Consider moving the reachable parts out
+  --> tests/ui/never_loop.rs:514:9
+   |
+LL | /         loop {
+LL | |
+LL | |             println!("This will still run");
+LL | |             loop {
+...  |
+LL | |         }
+   | |_________^
+help: if you need the first element of the iterator, try writing
+   |
+LL -     'foo: for _ in 0..100 {
+LL +     if let Some(_) = (0..100).next() {
+   |
+
+error: this loop never actually loops
+  --> tests/ui/never_loop.rs:514:9
+   |
+LL | /         loop {
+LL | |
+LL | |             println!("This will still run");
+LL | |             loop {
+...  |
+LL | |         }
+   | |_________^
+
+error: this loop never actually loops
+  --> tests/ui/never_loop.rs:517:13
+   |
+LL | /             loop {
+LL | |
+LL | |                 println!("This will still run");
+LL | |                 break 'foo;
+LL | |             }
+   | |_____________^
+
+error: aborting due to 29 previous errors
 
diff --git a/src/tools/clippy/tests/ui/nonminimal_bool.rs b/src/tools/clippy/tests/ui/nonminimal_bool.rs
index cacce9a7d1c..f03f74dfafe 100644
--- a/src/tools/clippy/tests/ui/nonminimal_bool.rs
+++ b/src/tools/clippy/tests/ui/nonminimal_bool.rs
@@ -182,14 +182,12 @@ fn issue_5794() {
     if !b == true {}
     //~^ nonminimal_bool
     //~| bool_comparison
-    //~| bool_comparison
     if !b != true {}
     //~^ nonminimal_bool
     //~| bool_comparison
     if true == !b {}
     //~^ nonminimal_bool
     //~| bool_comparison
-    //~| bool_comparison
     if true != !b {}
     //~^ nonminimal_bool
     //~| bool_comparison
diff --git a/src/tools/clippy/tests/ui/nonminimal_bool.stderr b/src/tools/clippy/tests/ui/nonminimal_bool.stderr
index c20412974b2..6a20b9216da 100644
--- a/src/tools/clippy/tests/ui/nonminimal_bool.stderr
+++ b/src/tools/clippy/tests/ui/nonminimal_bool.stderr
@@ -154,98 +154,86 @@ error: this boolean expression can be simplified
 LL |     if !b == true {}
    |        ^^^^^^^^^^ help: try: `b != true`
 
-error: this comparison might be written more concisely
+error: equality checks against true are unnecessary
   --> tests/ui/nonminimal_bool.rs:182:8
    |
 LL |     if !b == true {}
-   |        ^^^^^^^^^^ help: try simplifying it as shown: `b != true`
+   |        ^^^^^^^^^^ help: try: `!b`
    |
    = note: `-D clippy::bool-comparison` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::bool_comparison)]`
 
-error: equality checks against true are unnecessary
-  --> tests/ui/nonminimal_bool.rs:182:8
-   |
-LL |     if !b == true {}
-   |        ^^^^^^^^^^ help: try simplifying it as shown: `!b`
-
 error: this boolean expression can be simplified
-  --> tests/ui/nonminimal_bool.rs:186:8
+  --> tests/ui/nonminimal_bool.rs:185:8
    |
 LL |     if !b != true {}
    |        ^^^^^^^^^^ help: try: `b == true`
 
 error: inequality checks against true can be replaced by a negation
-  --> tests/ui/nonminimal_bool.rs:186:8
+  --> tests/ui/nonminimal_bool.rs:185:8
    |
 LL |     if !b != true {}
-   |        ^^^^^^^^^^ help: try simplifying it as shown: `b`
+   |        ^^^^^^^^^^ help: try: `b`
 
 error: this boolean expression can be simplified
-  --> tests/ui/nonminimal_bool.rs:189:8
+  --> tests/ui/nonminimal_bool.rs:188:8
    |
 LL |     if true == !b {}
    |        ^^^^^^^^^^ help: try: `true != b`
 
-error: this comparison might be written more concisely
-  --> tests/ui/nonminimal_bool.rs:189:8
-   |
-LL |     if true == !b {}
-   |        ^^^^^^^^^^ help: try simplifying it as shown: `true != b`
-
 error: equality checks against true are unnecessary
-  --> tests/ui/nonminimal_bool.rs:189:8
+  --> tests/ui/nonminimal_bool.rs:188:8
    |
 LL |     if true == !b {}
-   |        ^^^^^^^^^^ help: try simplifying it as shown: `!b`
+   |        ^^^^^^^^^^ help: try: `!b`
 
 error: this boolean expression can be simplified
-  --> tests/ui/nonminimal_bool.rs:193:8
+  --> tests/ui/nonminimal_bool.rs:191:8
    |
 LL |     if true != !b {}
    |        ^^^^^^^^^^ help: try: `true == b`
 
 error: inequality checks against true can be replaced by a negation
-  --> tests/ui/nonminimal_bool.rs:193:8
+  --> tests/ui/nonminimal_bool.rs:191:8
    |
 LL |     if true != !b {}
-   |        ^^^^^^^^^^ help: try simplifying it as shown: `b`
+   |        ^^^^^^^^^^ help: try: `b`
 
 error: this boolean expression can be simplified
-  --> tests/ui/nonminimal_bool.rs:196:8
+  --> tests/ui/nonminimal_bool.rs:194:8
    |
 LL |     if !b == !c {}
    |        ^^^^^^^^ help: try: `b == c`
 
 error: this boolean expression can be simplified
-  --> tests/ui/nonminimal_bool.rs:198:8
+  --> tests/ui/nonminimal_bool.rs:196:8
    |
 LL |     if !b != !c {}
    |        ^^^^^^^^ help: try: `b != c`
 
 error: this boolean expression can be simplified
-  --> tests/ui/nonminimal_bool.rs:214:8
+  --> tests/ui/nonminimal_bool.rs:212:8
    |
 LL |     if !(a < 2.0 && !b) {
    |        ^^^^^^^^^^^^^^^^ help: try: `a >= 2.0 || b`
 
 error: this boolean expression can be simplified
-  --> tests/ui/nonminimal_bool.rs:233:12
+  --> tests/ui/nonminimal_bool.rs:231:12
    |
 LL |         if !(matches!(ty, TyKind::Ref(_, _, _)) && !is_mutable(&expr)) {
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `!matches!(ty, TyKind::Ref(_, _, _)) || is_mutable(&expr)`
 
 error: this boolean expression can be simplified
-  --> tests/ui/nonminimal_bool.rs:253:8
+  --> tests/ui/nonminimal_bool.rs:251:8
    |
 LL |     if !S != true {}
    |        ^^^^^^^^^^ help: try: `S == true`
 
 error: inequality checks against true can be replaced by a negation
-  --> tests/ui/nonminimal_bool.rs:253:8
+  --> tests/ui/nonminimal_bool.rs:251:8
    |
 LL |     if !S != true {}
-   |        ^^^^^^^^^^ help: try simplifying it as shown: `!!S`
+   |        ^^^^^^^^^^ help: try: `!!S`
 
-error: aborting due to 33 previous errors
+error: aborting due to 31 previous errors
 
diff --git a/src/tools/clippy/tests/ui/option_if_let_else.fixed b/src/tools/clippy/tests/ui/option_if_let_else.fixed
index 0f86de5646c..6ce067f5c24 100644
--- a/src/tools/clippy/tests/ui/option_if_let_else.fixed
+++ b/src/tools/clippy/tests/ui/option_if_let_else.fixed
@@ -125,6 +125,16 @@ fn complex_subpat() -> DummyEnum {
     DummyEnum::Two
 }
 
+// #10335
+pub fn test_result_err_ignored_1(r: Result<&[u8], &[u8]>) -> Vec<u8> {
+    r.map_or_else(|_| Vec::new(), |s| s.to_owned())
+}
+
+// #10335
+pub fn test_result_err_ignored_2(r: Result<&[u8], &[u8]>) -> Vec<u8> {
+    r.map_or_else(|_| Vec::new(), |s| s.to_owned())
+}
+
 fn main() {
     let optional = Some(5);
     let _ = optional.map_or(5, |x| x + 2);
diff --git a/src/tools/clippy/tests/ui/option_if_let_else.rs b/src/tools/clippy/tests/ui/option_if_let_else.rs
index 7aabd778f87..096d3aabf28 100644
--- a/src/tools/clippy/tests/ui/option_if_let_else.rs
+++ b/src/tools/clippy/tests/ui/option_if_let_else.rs
@@ -152,6 +152,22 @@ fn complex_subpat() -> DummyEnum {
     DummyEnum::Two
 }
 
+// #10335
+pub fn test_result_err_ignored_1(r: Result<&[u8], &[u8]>) -> Vec<u8> {
+    match r {
+        //~^ option_if_let_else
+        Ok(s) => s.to_owned(),
+        Err(_) => Vec::new(),
+    }
+}
+
+// #10335
+pub fn test_result_err_ignored_2(r: Result<&[u8], &[u8]>) -> Vec<u8> {
+    if let Ok(s) = r { s.to_owned() }
+    //~^ option_if_let_else
+    else { Vec::new() }
+}
+
 fn main() {
     let optional = Some(5);
     let _ = if let Some(x) = optional { x + 2 } else { 5 };
diff --git a/src/tools/clippy/tests/ui/option_if_let_else.stderr b/src/tools/clippy/tests/ui/option_if_let_else.stderr
index 2e2fe6f2049..21a80ae038d 100644
--- a/src/tools/clippy/tests/ui/option_if_let_else.stderr
+++ b/src/tools/clippy/tests/ui/option_if_let_else.stderr
@@ -188,14 +188,32 @@ LL +         true
 LL +     })
    |
 
+error: use Option::map_or_else instead of an if let/else
+  --> tests/ui/option_if_let_else.rs:157:5
+   |
+LL | /     match r {
+LL | |
+LL | |         Ok(s) => s.to_owned(),
+LL | |         Err(_) => Vec::new(),
+LL | |     }
+   | |_____^ help: try: `r.map_or_else(|_| Vec::new(), |s| s.to_owned())`
+
+error: use Option::map_or_else instead of an if let/else
+  --> tests/ui/option_if_let_else.rs:166:5
+   |
+LL | /     if let Ok(s) = r { s.to_owned() }
+LL | |
+LL | |     else { Vec::new() }
+   | |_______________________^ help: try: `r.map_or_else(|_| Vec::new(), |s| s.to_owned())`
+
 error: use Option::map_or instead of an if let/else
-  --> tests/ui/option_if_let_else.rs:157:13
+  --> tests/ui/option_if_let_else.rs:173:13
    |
 LL |     let _ = if let Some(x) = optional { x + 2 } else { 5 };
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `optional.map_or(5, |x| x + 2)`
 
 error: use Option::map_or instead of an if let/else
-  --> tests/ui/option_if_let_else.rs:168:13
+  --> tests/ui/option_if_let_else.rs:184:13
    |
 LL |       let _ = if let Some(x) = Some(0) {
    |  _____________^
@@ -217,13 +235,13 @@ LL ~         });
    |
 
 error: use Option::map_or instead of an if let/else
-  --> tests/ui/option_if_let_else.rs:197:13
+  --> tests/ui/option_if_let_else.rs:213:13
    |
 LL |     let _ = if let Some(x) = Some(0) { s.len() + x } else { s.len() };
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Some(0).map_or(s.len(), |x| s.len() + x)`
 
 error: use Option::map_or instead of an if let/else
-  --> tests/ui/option_if_let_else.rs:202:13
+  --> tests/ui/option_if_let_else.rs:218:13
    |
 LL |       let _ = if let Some(x) = Some(0) {
    |  _____________^
@@ -245,7 +263,7 @@ LL ~     });
    |
 
 error: use Option::map_or instead of an if let/else
-  --> tests/ui/option_if_let_else.rs:242:13
+  --> tests/ui/option_if_let_else.rs:258:13
    |
 LL |       let _ = match s {
    |  _____________^
@@ -256,7 +274,7 @@ LL | |     };
    | |_____^ help: try: `s.map_or(1, |string| string.len())`
 
 error: use Option::map_or instead of an if let/else
-  --> tests/ui/option_if_let_else.rs:247:13
+  --> tests/ui/option_if_let_else.rs:263:13
    |
 LL |       let _ = match Some(10) {
    |  _____________^
@@ -267,7 +285,7 @@ LL | |     };
    | |_____^ help: try: `Some(10).map_or(5, |a| a + 1)`
 
 error: use Option::map_or instead of an if let/else
-  --> tests/ui/option_if_let_else.rs:254:13
+  --> tests/ui/option_if_let_else.rs:270:13
    |
 LL |       let _ = match res {
    |  _____________^
@@ -278,7 +296,7 @@ LL | |     };
    | |_____^ help: try: `res.map_or(1, |a| a + 1)`
 
 error: use Option::map_or instead of an if let/else
-  --> tests/ui/option_if_let_else.rs:259:13
+  --> tests/ui/option_if_let_else.rs:275:13
    |
 LL |       let _ = match res {
    |  _____________^
@@ -289,13 +307,13 @@ LL | |     };
    | |_____^ help: try: `res.map_or(1, |a| a + 1)`
 
 error: use Option::map_or instead of an if let/else
-  --> tests/ui/option_if_let_else.rs:264:13
+  --> tests/ui/option_if_let_else.rs:280:13
    |
 LL |     let _ = if let Ok(a) = res { a + 1 } else { 5 };
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `res.map_or(5, |a| a + 1)`
 
 error: use Option::map_or instead of an if let/else
-  --> tests/ui/option_if_let_else.rs:282:17
+  --> tests/ui/option_if_let_else.rs:298:17
    |
 LL |           let _ = match initial {
    |  _________________^
@@ -306,7 +324,7 @@ LL | |         };
    | |_________^ help: try: `initial.as_ref().map_or(42, |value| do_something(value))`
 
 error: use Option::map_or instead of an if let/else
-  --> tests/ui/option_if_let_else.rs:290:17
+  --> tests/ui/option_if_let_else.rs:306:17
    |
 LL |           let _ = match initial {
    |  _________________^
@@ -317,7 +335,7 @@ LL | |         };
    | |_________^ help: try: `initial.as_mut().map_or(42, |value| do_something2(value))`
 
 error: use Option::map_or_else instead of an if let/else
-  --> tests/ui/option_if_let_else.rs:314:24
+  --> tests/ui/option_if_let_else.rs:330:24
    |
 LL |       let mut _hashmap = if let Some(hm) = &opt {
    |  ________________________^
@@ -329,19 +347,19 @@ LL | |     };
    | |_____^ help: try: `opt.as_ref().map_or_else(HashMap::new, |hm| hm.clone())`
 
 error: use Option::map_or_else instead of an if let/else
-  --> tests/ui/option_if_let_else.rs:321:19
+  --> tests/ui/option_if_let_else.rs:337:19
    |
 LL |     let mut _hm = if let Some(hm) = &opt { hm.clone() } else { new_map!() };
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.as_ref().map_or_else(|| new_map!(), |hm| hm.clone())`
 
 error: use Option::map_or instead of an if let/else
-  --> tests/ui/option_if_let_else.rs:372:22
+  --> tests/ui/option_if_let_else.rs:388:22
    |
 LL |     let _ = unsafe { if let Some(o) = *opt_raw_ptr { o } else { 1 } };
    |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(*opt_raw_ptr).map_or(1, |o| o)`
 
 error: use Option::map_or_else instead of an if let/else
-  --> tests/ui/option_if_let_else.rs:378:13
+  --> tests/ui/option_if_let_else.rs:394:13
    |
 LL |       let _ = match res {
    |  _____________^
@@ -351,5 +369,5 @@ LL | |         Err(_) => String::new(),
 LL | |     };
    | |_____^ help: try: `res.map_or_else(|_| String::new(), |s| s.clone())`
 
-error: aborting due to 27 previous errors
+error: aborting due to 29 previous errors
 
diff --git a/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.fixed b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.fixed
index 55c1b8f110c..340be7c7e93 100644
--- a/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.fixed
+++ b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.fixed
@@ -102,4 +102,13 @@ fn option_map_unit_fn() {
     //~^ option_map_unit_fn
 }
 
+fn issue15568() {
+    unsafe fn f(_: u32) {}
+    let x = Some(3);
+    if let Some(x) = x { unsafe { f(x) } }
+    //~^ option_map_unit_fn
+    if let Some(x) = x { unsafe { f(x) } }
+    //~^ option_map_unit_fn
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.rs b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.rs
index 5ed47e4c60b..d902c87379b 100644
--- a/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.rs
+++ b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.rs
@@ -102,4 +102,13 @@ fn option_map_unit_fn() {
     //~^ option_map_unit_fn
 }
 
+fn issue15568() {
+    unsafe fn f(_: u32) {}
+    let x = Some(3);
+    x.map(|x| unsafe { f(x) });
+    //~^ option_map_unit_fn
+    x.map(|x| unsafe { { f(x) } });
+    //~^ option_map_unit_fn
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.stderr b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.stderr
index 3f7abae34ee..2405aa9a7cc 100644
--- a/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.stderr
+++ b/src/tools/clippy/tests/ui/option_map_unit_fn_fixable.stderr
@@ -153,5 +153,21 @@ LL |     option().map(|value| println!("{:?}", value));
    |     |
    |     help: try: `if let Some(value) = option() { println!("{:?}", value) }`
 
-error: aborting due to 19 previous errors
+error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`
+  --> tests/ui/option_map_unit_fn_fixable.rs:108:5
+   |
+LL |     x.map(|x| unsafe { f(x) });
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^-
+   |     |
+   |     help: try: `if let Some(x) = x { unsafe { f(x) } }`
+
+error: called `map(f)` on an `Option` value where `f` is a closure that returns the unit type `()`
+  --> tests/ui/option_map_unit_fn_fixable.rs:110:5
+   |
+LL |     x.map(|x| unsafe { { f(x) } });
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-
+   |     |
+   |     help: try: `if let Some(x) = x { unsafe { f(x) } }`
+
+error: aborting due to 21 previous errors
 
diff --git a/src/tools/clippy/tests/ui/or_fun_call.fixed b/src/tools/clippy/tests/ui/or_fun_call.fixed
index 0a8525a12f5..7a0be97017e 100644
--- a/src/tools/clippy/tests/ui/or_fun_call.fixed
+++ b/src/tools/clippy/tests/ui/or_fun_call.fixed
@@ -77,6 +77,22 @@ fn or_fun_call() {
     with_default_type.unwrap_or_default();
     //~^ unwrap_or_default
 
+    let with_default_literal = Some(1);
+    with_default_literal.unwrap_or(0);
+    // Do not lint because `.unwrap_or_default()` wouldn't be simpler
+
+    let with_default_literal = Some(1.0);
+    with_default_literal.unwrap_or(0.0);
+    // Do not lint because `.unwrap_or_default()` wouldn't be simpler
+
+    let with_default_literal = Some("foo");
+    with_default_literal.unwrap_or("");
+    // Do not lint because `.unwrap_or_default()` wouldn't be simpler
+
+    let with_default_vec_macro = Some(vec![1, 2, 3]);
+    with_default_vec_macro.unwrap_or(vec![]);
+    // Do not lint because `.unwrap_or_default()` wouldn't be simpler
+
     let self_default = None::<FakeDefault>;
     self_default.unwrap_or_else(<FakeDefault>::default);
     //~^ or_fun_call
diff --git a/src/tools/clippy/tests/ui/or_fun_call.rs b/src/tools/clippy/tests/ui/or_fun_call.rs
index b4f9b950a7f..724af606de9 100644
--- a/src/tools/clippy/tests/ui/or_fun_call.rs
+++ b/src/tools/clippy/tests/ui/or_fun_call.rs
@@ -77,6 +77,22 @@ fn or_fun_call() {
     with_default_type.unwrap_or(u64::default());
     //~^ unwrap_or_default
 
+    let with_default_literal = Some(1);
+    with_default_literal.unwrap_or(0);
+    // Do not lint because `.unwrap_or_default()` wouldn't be simpler
+
+    let with_default_literal = Some(1.0);
+    with_default_literal.unwrap_or(0.0);
+    // Do not lint because `.unwrap_or_default()` wouldn't be simpler
+
+    let with_default_literal = Some("foo");
+    with_default_literal.unwrap_or("");
+    // Do not lint because `.unwrap_or_default()` wouldn't be simpler
+
+    let with_default_vec_macro = Some(vec![1, 2, 3]);
+    with_default_vec_macro.unwrap_or(vec![]);
+    // Do not lint because `.unwrap_or_default()` wouldn't be simpler
+
     let self_default = None::<FakeDefault>;
     self_default.unwrap_or(<FakeDefault>::default());
     //~^ or_fun_call
@@ -86,7 +102,7 @@ fn or_fun_call() {
     //~^ unwrap_or_default
 
     let with_vec = Some(vec![1]);
-    with_vec.unwrap_or(vec![]);
+    with_vec.unwrap_or(Vec::new());
     //~^ unwrap_or_default
 
     let without_default = Some(Foo);
@@ -98,7 +114,7 @@ fn or_fun_call() {
     //~^ unwrap_or_default
 
     let mut map_vec = HashMap::<u64, Vec<i32>>::new();
-    map_vec.entry(42).or_insert(vec![]);
+    map_vec.entry(42).or_insert(Vec::new());
     //~^ unwrap_or_default
 
     let mut btree = BTreeMap::<u64, String>::new();
@@ -106,7 +122,7 @@ fn or_fun_call() {
     //~^ unwrap_or_default
 
     let mut btree_vec = BTreeMap::<u64, Vec<i32>>::new();
-    btree_vec.entry(42).or_insert(vec![]);
+    btree_vec.entry(42).or_insert(Vec::new());
     //~^ unwrap_or_default
 
     let stringy = Some(String::new());
diff --git a/src/tools/clippy/tests/ui/or_fun_call.stderr b/src/tools/clippy/tests/ui/or_fun_call.stderr
index 3e4df772668..40b25f91154 100644
--- a/src/tools/clippy/tests/ui/or_fun_call.stderr
+++ b/src/tools/clippy/tests/ui/or_fun_call.stderr
@@ -47,175 +47,175 @@ LL |     with_default_type.unwrap_or(u64::default());
    |                       ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
 
 error: function call inside of `unwrap_or`
-  --> tests/ui/or_fun_call.rs:81:18
+  --> tests/ui/or_fun_call.rs:97:18
    |
 LL |     self_default.unwrap_or(<FakeDefault>::default());
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(<FakeDefault>::default)`
 
 error: use of `unwrap_or` to construct default value
-  --> tests/ui/or_fun_call.rs:85:18
+  --> tests/ui/or_fun_call.rs:101:18
    |
 LL |     real_default.unwrap_or(<FakeDefault as Default>::default());
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
 
 error: use of `unwrap_or` to construct default value
-  --> tests/ui/or_fun_call.rs:89:14
+  --> tests/ui/or_fun_call.rs:105:14
    |
-LL |     with_vec.unwrap_or(vec![]);
-   |              ^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
+LL |     with_vec.unwrap_or(Vec::new());
+   |              ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
 
 error: function call inside of `unwrap_or`
-  --> tests/ui/or_fun_call.rs:93:21
+  --> tests/ui/or_fun_call.rs:109:21
    |
 LL |     without_default.unwrap_or(Foo::new());
    |                     ^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(Foo::new)`
 
 error: use of `or_insert` to construct default value
-  --> tests/ui/or_fun_call.rs:97:19
+  --> tests/ui/or_fun_call.rs:113:19
    |
 LL |     map.entry(42).or_insert(String::new());
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()`
 
 error: use of `or_insert` to construct default value
-  --> tests/ui/or_fun_call.rs:101:23
+  --> tests/ui/or_fun_call.rs:117:23
    |
-LL |     map_vec.entry(42).or_insert(vec![]);
-   |                       ^^^^^^^^^^^^^^^^^ help: try: `or_default()`
+LL |     map_vec.entry(42).or_insert(Vec::new());
+   |                       ^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()`
 
 error: use of `or_insert` to construct default value
-  --> tests/ui/or_fun_call.rs:105:21
+  --> tests/ui/or_fun_call.rs:121:21
    |
 LL |     btree.entry(42).or_insert(String::new());
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()`
 
 error: use of `or_insert` to construct default value
-  --> tests/ui/or_fun_call.rs:109:25
+  --> tests/ui/or_fun_call.rs:125:25
    |
-LL |     btree_vec.entry(42).or_insert(vec![]);
-   |                         ^^^^^^^^^^^^^^^^^ help: try: `or_default()`
+LL |     btree_vec.entry(42).or_insert(Vec::new());
+   |                         ^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()`
 
 error: use of `unwrap_or` to construct default value
-  --> tests/ui/or_fun_call.rs:113:21
+  --> tests/ui/or_fun_call.rs:129:21
    |
 LL |     let _ = stringy.unwrap_or(String::new());
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
 
 error: function call inside of `ok_or`
-  --> tests/ui/or_fun_call.rs:118:17
+  --> tests/ui/or_fun_call.rs:134:17
    |
 LL |     let _ = opt.ok_or(format!("{} world.", hello));
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ok_or_else(|| format!("{} world.", hello))`
 
 error: function call inside of `unwrap_or`
-  --> tests/ui/or_fun_call.rs:123:21
+  --> tests/ui/or_fun_call.rs:139:21
    |
 LL |     let _ = Some(1).unwrap_or(map[&1]);
    |                     ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])`
 
 error: function call inside of `unwrap_or`
-  --> tests/ui/or_fun_call.rs:126:21
+  --> tests/ui/or_fun_call.rs:142:21
    |
 LL |     let _ = Some(1).unwrap_or(map[&1]);
    |                     ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| map[&1])`
 
 error: function call inside of `or`
-  --> tests/ui/or_fun_call.rs:151:35
+  --> tests/ui/or_fun_call.rs:167:35
    |
 LL |     let _ = Some("a".to_string()).or(Some("b".to_string()));
    |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_else(|| Some("b".to_string()))`
 
 error: function call inside of `unwrap_or`
-  --> tests/ui/or_fun_call.rs:194:18
+  --> tests/ui/or_fun_call.rs:210:18
    |
 LL |             None.unwrap_or(ptr_to_ref(s));
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| ptr_to_ref(s))`
 
 error: function call inside of `unwrap_or`
-  --> tests/ui/or_fun_call.rs:202:14
+  --> tests/ui/or_fun_call.rs:218:14
    |
 LL |         None.unwrap_or(unsafe { ptr_to_ref(s) });
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })`
 
 error: function call inside of `unwrap_or`
-  --> tests/ui/or_fun_call.rs:205:14
+  --> tests/ui/or_fun_call.rs:221:14
    |
 LL |         None.unwrap_or( unsafe { ptr_to_ref(s) }    );
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| unsafe { ptr_to_ref(s) })`
 
 error: function call inside of `map_or`
-  --> tests/ui/or_fun_call.rs:281:25
+  --> tests/ui/or_fun_call.rs:297:25
    |
 LL |         let _ = Some(4).map_or(g(), |v| v);
    |                         ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(g, |v| v)`
 
 error: function call inside of `map_or`
-  --> tests/ui/or_fun_call.rs:283:25
+  --> tests/ui/or_fun_call.rs:299:25
    |
 LL |         let _ = Some(4).map_or(g(), f);
    |                         ^^^^^^^^^^^^^^ help: try: `map_or_else(g, f)`
 
 error: function call inside of `map_or`
-  --> tests/ui/or_fun_call.rs:286:25
+  --> tests/ui/or_fun_call.rs:302:25
    |
 LL |         let _ = Some(4).map_or("asd".to_string().len() as i32, f);
    |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|| "asd".to_string().len() as i32, f)`
 
 error: use of `unwrap_or_else` to construct default value
-  --> tests/ui/or_fun_call.rs:317:18
+  --> tests/ui/or_fun_call.rs:333:18
    |
 LL |         with_new.unwrap_or_else(Vec::new);
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
 
 error: use of `unwrap_or_else` to construct default value
-  --> tests/ui/or_fun_call.rs:321:28
+  --> tests/ui/or_fun_call.rs:337:28
    |
 LL |         with_default_trait.unwrap_or_else(Default::default);
    |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
 
 error: use of `unwrap_or_else` to construct default value
-  --> tests/ui/or_fun_call.rs:325:27
+  --> tests/ui/or_fun_call.rs:341:27
    |
 LL |         with_default_type.unwrap_or_else(u64::default);
    |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
 
 error: use of `unwrap_or_else` to construct default value
-  --> tests/ui/or_fun_call.rs:329:22
+  --> tests/ui/or_fun_call.rs:345:22
    |
 LL |         real_default.unwrap_or_else(<FakeDefault as Default>::default);
    |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
 
 error: use of `or_insert_with` to construct default value
-  --> tests/ui/or_fun_call.rs:333:23
+  --> tests/ui/or_fun_call.rs:349:23
    |
 LL |         map.entry(42).or_insert_with(String::new);
    |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()`
 
 error: use of `or_insert_with` to construct default value
-  --> tests/ui/or_fun_call.rs:337:25
+  --> tests/ui/or_fun_call.rs:353:25
    |
 LL |         btree.entry(42).or_insert_with(String::new);
    |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `or_default()`
 
 error: use of `unwrap_or_else` to construct default value
-  --> tests/ui/or_fun_call.rs:341:25
+  --> tests/ui/or_fun_call.rs:357:25
    |
 LL |         let _ = stringy.unwrap_or_else(String::new);
    |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
 
 error: function call inside of `unwrap_or`
-  --> tests/ui/or_fun_call.rs:383:17
+  --> tests/ui/or_fun_call.rs:399:17
    |
 LL |     let _ = opt.unwrap_or({ f() }); // suggest `.unwrap_or_else(f)`
    |                 ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(f)`
 
 error: function call inside of `unwrap_or`
-  --> tests/ui/or_fun_call.rs:388:17
+  --> tests/ui/or_fun_call.rs:404:17
    |
 LL |     let _ = opt.unwrap_or(f() + 1); // suggest `.unwrap_or_else(|| f() + 1)`
    |                 ^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| f() + 1)`
 
 error: function call inside of `unwrap_or`
-  --> tests/ui/or_fun_call.rs:393:17
+  --> tests/ui/or_fun_call.rs:409:17
    |
 LL |       let _ = opt.unwrap_or({
    |  _________________^
@@ -235,55 +235,55 @@ LL ~     });
    |
 
 error: function call inside of `map_or`
-  --> tests/ui/or_fun_call.rs:399:17
+  --> tests/ui/or_fun_call.rs:415:17
    |
 LL |     let _ = opt.map_or(f() + 1, |v| v); // suggest `.map_or_else(|| f() + 1, |v| v)`
    |                 ^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|| f() + 1, |v| v)`
 
 error: use of `unwrap_or` to construct default value
-  --> tests/ui/or_fun_call.rs:404:17
+  --> tests/ui/or_fun_call.rs:420:17
    |
 LL |     let _ = opt.unwrap_or({ i32::default() });
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_default()`
 
 error: function call inside of `unwrap_or`
-  --> tests/ui/or_fun_call.rs:411:21
+  --> tests/ui/or_fun_call.rs:427:21
    |
 LL |     let _ = opt_foo.unwrap_or(Foo { val: String::default() });
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or_else(|| Foo { val: String::default() })`
 
 error: function call inside of `map_or`
-  --> tests/ui/or_fun_call.rs:426:19
+  --> tests/ui/or_fun_call.rs:442:19
    |
 LL |         let _ = x.map_or(g(), |v| v);
    |                   ^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|_| g(), |v| v)`
 
 error: function call inside of `map_or`
-  --> tests/ui/or_fun_call.rs:428:19
+  --> tests/ui/or_fun_call.rs:444:19
    |
 LL |         let _ = x.map_or(g(), f);
    |                   ^^^^^^^^^^^^^^ help: try: `map_or_else(|_| g(), f)`
 
 error: function call inside of `map_or`
-  --> tests/ui/or_fun_call.rs:431:19
+  --> tests/ui/or_fun_call.rs:447:19
    |
 LL |         let _ = x.map_or("asd".to_string().len() as i32, f);
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `map_or_else(|_| "asd".to_string().len() as i32, f)`
 
 error: function call inside of `get_or_insert`
-  --> tests/ui/or_fun_call.rs:442:15
+  --> tests/ui/or_fun_call.rs:458:15
    |
 LL |     let _ = x.get_or_insert(g());
    |               ^^^^^^^^^^^^^^^^^^ help: try: `get_or_insert_with(g)`
 
 error: function call inside of `and`
-  --> tests/ui/or_fun_call.rs:452:15
+  --> tests/ui/or_fun_call.rs:468:15
    |
 LL |     let _ = x.and(g());
    |               ^^^^^^^^ help: try: `and_then(|_| g())`
 
 error: function call inside of `and`
-  --> tests/ui/or_fun_call.rs:462:15
+  --> tests/ui/or_fun_call.rs:478:15
    |
 LL |     let _ = x.and(g());
    |               ^^^^^^^^ help: try: `and_then(|_| g())`
diff --git a/src/tools/clippy/tests/ui/or_then_unwrap.fixed b/src/tools/clippy/tests/ui/or_then_unwrap.fixed
index ba9beef57af..9660b82fe7d 100644
--- a/src/tools/clippy/tests/ui/or_then_unwrap.fixed
+++ b/src/tools/clippy/tests/ui/or_then_unwrap.fixed
@@ -28,6 +28,12 @@ fn main() {
     //
     //~^^ or_then_unwrap
 
+    // Call with macro should preserve the macro call rather than expand it
+    let option: Option<Vec<&str>> = None;
+    let _ = option.unwrap_or(vec!["fallback"]); // should trigger lint
+    //
+    //~^^ or_then_unwrap
+
     // as part of a method chain
     let option: Option<&str> = None;
     let _ = option.map(|v| v).unwrap_or("fallback").to_string().chars(); // should trigger lint
diff --git a/src/tools/clippy/tests/ui/or_then_unwrap.rs b/src/tools/clippy/tests/ui/or_then_unwrap.rs
index fac90249a24..c3873352116 100644
--- a/src/tools/clippy/tests/ui/or_then_unwrap.rs
+++ b/src/tools/clippy/tests/ui/or_then_unwrap.rs
@@ -28,6 +28,12 @@ fn main() {
     //
     //~^^ or_then_unwrap
 
+    // Call with macro should preserve the macro call rather than expand it
+    let option: Option<Vec<&str>> = None;
+    let _ = option.or(Some(vec!["fallback"])).unwrap(); // should trigger lint
+    //
+    //~^^ or_then_unwrap
+
     // as part of a method chain
     let option: Option<&str> = None;
     let _ = option.map(|v| v).or(Some("fallback")).unwrap().to_string().chars(); // should trigger lint
diff --git a/src/tools/clippy/tests/ui/or_then_unwrap.stderr b/src/tools/clippy/tests/ui/or_then_unwrap.stderr
index 1160498c605..3e66b15edbd 100644
--- a/src/tools/clippy/tests/ui/or_then_unwrap.stderr
+++ b/src/tools/clippy/tests/ui/or_then_unwrap.stderr
@@ -14,10 +14,16 @@ LL |     let _ = result.or::<&str>(Ok("fallback")).unwrap(); // should trigger l
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or("fallback")`
 
 error: found `.or(Some(…)).unwrap()`
-  --> tests/ui/or_then_unwrap.rs:33:31
+  --> tests/ui/or_then_unwrap.rs:33:20
+   |
+LL |     let _ = option.or(Some(vec!["fallback"])).unwrap(); // should trigger lint
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or(vec!["fallback"])`
+
+error: found `.or(Some(…)).unwrap()`
+  --> tests/ui/or_then_unwrap.rs:39:31
    |
 LL |     let _ = option.map(|v| v).or(Some("fallback")).unwrap().to_string().chars(); // should trigger lint
    |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `unwrap_or("fallback")`
 
-error: aborting due to 3 previous errors
+error: aborting due to 4 previous errors
 
diff --git a/src/tools/clippy/tests/ui/panicking_macros.rs b/src/tools/clippy/tests/ui/panicking_macros.rs
index 65854d7eb4b..b044be7d54a 100644
--- a/src/tools/clippy/tests/ui/panicking_macros.rs
+++ b/src/tools/clippy/tests/ui/panicking_macros.rs
@@ -31,6 +31,20 @@ fn panic() {
     let b = a + 2;
 }
 
+const fn panic_const() {
+    let a = 2;
+    panic!();
+    //~^ panic
+
+    panic!("message");
+    //~^ panic
+
+    panic!("{} {}", "panic with", "multiple arguments");
+    //~^ panic
+
+    let b = a + 2;
+}
+
 fn todo() {
     let a = 2;
     todo!();
@@ -114,6 +128,7 @@ fn debug_assert_msg() {
 
 fn main() {
     panic();
+    panic_const();
     todo();
     unimplemented();
     unreachable();
diff --git a/src/tools/clippy/tests/ui/panicking_macros.stderr b/src/tools/clippy/tests/ui/panicking_macros.stderr
index 03e459e4ec6..7b767ae0f65 100644
--- a/src/tools/clippy/tests/ui/panicking_macros.stderr
+++ b/src/tools/clippy/tests/ui/panicking_macros.stderr
@@ -19,9 +19,27 @@ error: `panic` should not be present in production code
 LL |     panic!("{} {}", "panic with", "multiple arguments");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: `todo` should not be present in production code
+error: `panic` should not be present in production code
   --> tests/ui/panicking_macros.rs:36:5
    |
+LL |     panic!();
+   |     ^^^^^^^^
+
+error: `panic` should not be present in production code
+  --> tests/ui/panicking_macros.rs:39:5
+   |
+LL |     panic!("message");
+   |     ^^^^^^^^^^^^^^^^^
+
+error: `panic` should not be present in production code
+  --> tests/ui/panicking_macros.rs:42:5
+   |
+LL |     panic!("{} {}", "panic with", "multiple arguments");
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: `todo` should not be present in production code
+  --> tests/ui/panicking_macros.rs:50:5
+   |
 LL |     todo!();
    |     ^^^^^^^
    |
@@ -29,19 +47,19 @@ LL |     todo!();
    = help: to override `-D warnings` add `#[allow(clippy::todo)]`
 
 error: `todo` should not be present in production code
-  --> tests/ui/panicking_macros.rs:39:5
+  --> tests/ui/panicking_macros.rs:53:5
    |
 LL |     todo!("message");
    |     ^^^^^^^^^^^^^^^^
 
 error: `todo` should not be present in production code
-  --> tests/ui/panicking_macros.rs:42:5
+  --> tests/ui/panicking_macros.rs:56:5
    |
 LL |     todo!("{} {}", "panic with", "multiple arguments");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: `unimplemented` should not be present in production code
-  --> tests/ui/panicking_macros.rs:50:5
+  --> tests/ui/panicking_macros.rs:64:5
    |
 LL |     unimplemented!();
    |     ^^^^^^^^^^^^^^^^
@@ -50,19 +68,19 @@ LL |     unimplemented!();
    = help: to override `-D warnings` add `#[allow(clippy::unimplemented)]`
 
 error: `unimplemented` should not be present in production code
-  --> tests/ui/panicking_macros.rs:53:5
+  --> tests/ui/panicking_macros.rs:67:5
    |
 LL |     unimplemented!("message");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: `unimplemented` should not be present in production code
-  --> tests/ui/panicking_macros.rs:56:5
+  --> tests/ui/panicking_macros.rs:70:5
    |
 LL |     unimplemented!("{} {}", "panic with", "multiple arguments");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: usage of the `unreachable!` macro
-  --> tests/ui/panicking_macros.rs:64:5
+  --> tests/ui/panicking_macros.rs:78:5
    |
 LL |     unreachable!();
    |     ^^^^^^^^^^^^^^
@@ -71,40 +89,40 @@ LL |     unreachable!();
    = help: to override `-D warnings` add `#[allow(clippy::unreachable)]`
 
 error: usage of the `unreachable!` macro
-  --> tests/ui/panicking_macros.rs:67:5
+  --> tests/ui/panicking_macros.rs:81:5
    |
 LL |     unreachable!("message");
    |     ^^^^^^^^^^^^^^^^^^^^^^^
 
 error: usage of the `unreachable!` macro
-  --> tests/ui/panicking_macros.rs:70:5
+  --> tests/ui/panicking_macros.rs:84:5
    |
 LL |     unreachable!("{} {}", "panic with", "multiple arguments");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: `panic` should not be present in production code
-  --> tests/ui/panicking_macros.rs:78:5
+  --> tests/ui/panicking_macros.rs:92:5
    |
 LL |     panic!();
    |     ^^^^^^^^
 
 error: `todo` should not be present in production code
-  --> tests/ui/panicking_macros.rs:81:5
+  --> tests/ui/panicking_macros.rs:95:5
    |
 LL |     todo!();
    |     ^^^^^^^
 
 error: `unimplemented` should not be present in production code
-  --> tests/ui/panicking_macros.rs:84:5
+  --> tests/ui/panicking_macros.rs:98:5
    |
 LL |     unimplemented!();
    |     ^^^^^^^^^^^^^^^^
 
 error: usage of the `unreachable!` macro
-  --> tests/ui/panicking_macros.rs:87:5
+  --> tests/ui/panicking_macros.rs:101:5
    |
 LL |     unreachable!();
    |     ^^^^^^^^^^^^^^
 
-error: aborting due to 16 previous errors
+error: aborting due to 19 previous errors
 
diff --git a/src/tools/clippy/tests/ui/print_literal.fixed b/src/tools/clippy/tests/ui/print_literal.fixed
index ebfe19c700e..26139f9b671 100644
--- a/src/tools/clippy/tests/ui/print_literal.fixed
+++ b/src/tools/clippy/tests/ui/print_literal.fixed
@@ -105,3 +105,16 @@ fn issue_14930() {
     println!("Hello x is {0:2$.1$}", 0.01, 2, 3);
     //~^ print_literal
 }
+
+fn issue_15576() {
+    println!("Hello x is {1:.*}", 5, 0.01);
+    //~^ print_literal
+
+    println!("Hello x is {:.p$}", 0.01, p = 5);
+    //~^ print_literal
+
+    println!(
+        "Hello name: x is {1:.*} (which {1} with {0} places)", 5, 0.01
+    );
+    //~^^ print_literal
+}
diff --git a/src/tools/clippy/tests/ui/print_literal.rs b/src/tools/clippy/tests/ui/print_literal.rs
index 8f3d9be0698..7c4cf028e84 100644
--- a/src/tools/clippy/tests/ui/print_literal.rs
+++ b/src/tools/clippy/tests/ui/print_literal.rs
@@ -106,3 +106,17 @@ fn issue_14930() {
     println!("Hello {0} is {1:3$.2$}", "x", 0.01, 2, 3);
     //~^ print_literal
 }
+
+fn issue_15576() {
+    println!("Hello {} is {2:.*}", "x", 5, 0.01);
+    //~^ print_literal
+
+    println!("Hello {} is {:.p$}", "x", 0.01, p = 5);
+    //~^ print_literal
+
+    println!(
+        "Hello {}: {2} is {3:.*} (which {3} with {1} places)",
+        "name", 5, "x", 0.01
+    );
+    //~^^ print_literal
+}
diff --git a/src/tools/clippy/tests/ui/print_literal.stderr b/src/tools/clippy/tests/ui/print_literal.stderr
index 1c378017d15..c136f52800f 100644
--- a/src/tools/clippy/tests/ui/print_literal.stderr
+++ b/src/tools/clippy/tests/ui/print_literal.stderr
@@ -277,5 +277,41 @@ LL -     println!("Hello {0} is {1:3$.2$}", "x", 0.01, 2, 3);
 LL +     println!("Hello x is {0:2$.1$}", 0.01, 2, 3);
    |
 
-error: aborting due to 22 previous errors
+error: literal with an empty format string
+  --> tests/ui/print_literal.rs:111:36
+   |
+LL |     println!("Hello {} is {2:.*}", "x", 5, 0.01);
+   |                                    ^^^
+   |
+help: try
+   |
+LL -     println!("Hello {} is {2:.*}", "x", 5, 0.01);
+LL +     println!("Hello x is {1:.*}", 5, 0.01);
+   |
+
+error: literal with an empty format string
+  --> tests/ui/print_literal.rs:114:36
+   |
+LL |     println!("Hello {} is {:.p$}", "x", 0.01, p = 5);
+   |                                    ^^^
+   |
+help: try
+   |
+LL -     println!("Hello {} is {:.p$}", "x", 0.01, p = 5);
+LL +     println!("Hello x is {:.p$}", 0.01, p = 5);
+   |
+
+error: literal with an empty format string
+  --> tests/ui/print_literal.rs:119:9
+   |
+LL |         "name", 5, "x", 0.01
+   |         ^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL -         "Hello {}: {2} is {3:.*} (which {3} with {1} places)",
+LL +         "Hello name: x is {1:.*} (which {1} with {0} places)", 5, 0.01
+   |
+
+error: aborting due to 25 previous errors
 
diff --git a/src/tools/clippy/tests/ui/ptr_arg.stderr b/src/tools/clippy/tests/ui/ptr_arg.stderr
index 87235057349..f32e83d8b81 100644
--- a/src/tools/clippy/tests/ui/ptr_arg.stderr
+++ b/src/tools/clippy/tests/ui/ptr_arg.stderr
@@ -268,10 +268,10 @@ LL |     fn barbar(_x: &mut Vec<u32>, y: &mut String) {
    |                                     ^^^^^^^^^^^ help: change this to: `&mut str`
 
 error: eliding a lifetime that's named elsewhere is confusing
-  --> tests/ui/ptr_arg.rs:314:36
+  --> tests/ui/ptr_arg.rs:314:56
    |
 LL |     fn cow_good_ret_ty<'a>(input: &'a Cow<'a, str>) -> &str {
-   |                                    ^^     ^^           ---- the same lifetime is elided here
+   |                                    --     --           ^^^^ the same lifetime is elided here
    |                                    |      |
    |                                    |      the lifetime is named here
    |                                    the lifetime is named here
diff --git a/src/tools/clippy/tests/ui/ptr_as_ptr.fixed b/src/tools/clippy/tests/ui/ptr_as_ptr.fixed
index 71fea6144e7..4457878b6fa 100644
--- a/src/tools/clippy/tests/ui/ptr_as_ptr.fixed
+++ b/src/tools/clippy/tests/ui/ptr_as_ptr.fixed
@@ -2,8 +2,8 @@
 
 #![warn(clippy::ptr_as_ptr)]
 
-#[macro_use]
 extern crate proc_macros;
+use proc_macros::{external, inline_macros, with_span};
 
 mod issue_11278_a {
     #[derive(Debug)]
@@ -53,11 +53,13 @@ fn main() {
     //~^ ptr_as_ptr
 
     // Make sure the lint is triggered inside a macro
-    let _ = inline!($ptr.cast::<i32>());
-    //~^ ptr_as_ptr
+    // FIXME: `is_from_proc_macro` incorrectly stops the lint from firing here
+    let _ = inline!($ptr as *const i32);
 
     // Do not lint inside macros from external crates
     let _ = external!($ptr as *const i32);
+
+    let _ = with_span!(expr $ptr as *const i32);
 }
 
 #[clippy::msrv = "1.37"]
@@ -227,3 +229,9 @@ fn issue15283() {
         //~^ ptr_as_ptr
     }
 }
+
+fn issue15232() {
+    let mut b = Box::new(0i32);
+    let _ptr = std::ptr::addr_of_mut!(*b).cast::<()>();
+    //~^ ptr_as_ptr
+}
diff --git a/src/tools/clippy/tests/ui/ptr_as_ptr.rs b/src/tools/clippy/tests/ui/ptr_as_ptr.rs
index 4d507592a1e..9124fc912d2 100644
--- a/src/tools/clippy/tests/ui/ptr_as_ptr.rs
+++ b/src/tools/clippy/tests/ui/ptr_as_ptr.rs
@@ -2,8 +2,8 @@
 
 #![warn(clippy::ptr_as_ptr)]
 
-#[macro_use]
 extern crate proc_macros;
+use proc_macros::{external, inline_macros, with_span};
 
 mod issue_11278_a {
     #[derive(Debug)]
@@ -53,11 +53,13 @@ fn main() {
     //~^ ptr_as_ptr
 
     // Make sure the lint is triggered inside a macro
+    // FIXME: `is_from_proc_macro` incorrectly stops the lint from firing here
     let _ = inline!($ptr as *const i32);
-    //~^ ptr_as_ptr
 
     // Do not lint inside macros from external crates
     let _ = external!($ptr as *const i32);
+
+    let _ = with_span!(expr $ptr as *const i32);
 }
 
 #[clippy::msrv = "1.37"]
@@ -227,3 +229,9 @@ fn issue15283() {
         //~^ ptr_as_ptr
     }
 }
+
+fn issue15232() {
+    let mut b = Box::new(0i32);
+    let _ptr = std::ptr::addr_of_mut!(*b) as *mut ();
+    //~^ ptr_as_ptr
+}
diff --git a/src/tools/clippy/tests/ui/ptr_as_ptr.stderr b/src/tools/clippy/tests/ui/ptr_as_ptr.stderr
index adad159bb0f..af21c1e35f5 100644
--- a/src/tools/clippy/tests/ui/ptr_as_ptr.stderr
+++ b/src/tools/clippy/tests/ui/ptr_as_ptr.stderr
@@ -38,174 +38,172 @@ LL |     let _: *mut i32 = mut_ptr as _;
    |                       ^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast()`
 
 error: `as` casting between raw pointers without changing their constness
-  --> tests/ui/ptr_as_ptr.rs:56:21
-   |
-LL |     let _ = inline!($ptr as *const i32);
-   |                     ^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `$ptr.cast::<i32>()`
-   |
-   = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: `as` casting between raw pointers without changing their constness
-  --> tests/ui/ptr_as_ptr.rs:78:13
+  --> tests/ui/ptr_as_ptr.rs:80:13
    |
 LL |     let _ = ptr as *const i32;
    |             ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::<i32>()`
 
 error: `as` casting between raw pointers without changing their constness
-  --> tests/ui/ptr_as_ptr.rs:80:13
+  --> tests/ui/ptr_as_ptr.rs:82:13
    |
 LL |     let _ = mut_ptr as *mut i32;
    |             ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::<i32>()`
 
 error: `as` casting between raw pointers without changing their constness
-  --> tests/ui/ptr_as_ptr.rs:88:9
+  --> tests/ui/ptr_as_ptr.rs:90:9
    |
 LL |         ptr::null_mut() as *mut u32
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut::<u32>()`
 
 error: `as` casting between raw pointers without changing their constness
-  --> tests/ui/ptr_as_ptr.rs:93:9
+  --> tests/ui/ptr_as_ptr.rs:95:9
    |
 LL |         std::ptr::null_mut() as *mut u32
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null_mut::<u32>()`
 
 error: `as` casting between raw pointers without changing their constness
-  --> tests/ui/ptr_as_ptr.rs:99:9
+  --> tests/ui/ptr_as_ptr.rs:101:9
    |
 LL |         ptr::null_mut() as *mut u32
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut::<u32>()`
 
 error: `as` casting between raw pointers without changing their constness
-  --> tests/ui/ptr_as_ptr.rs:104:9
+  --> tests/ui/ptr_as_ptr.rs:106:9
    |
 LL |         core::ptr::null_mut() as *mut u32
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `core::ptr::null_mut::<u32>()`
 
 error: `as` casting between raw pointers without changing their constness
-  --> tests/ui/ptr_as_ptr.rs:110:9
+  --> tests/ui/ptr_as_ptr.rs:112:9
    |
 LL |         ptr::null() as *const u32
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null::<u32>()`
 
 error: `as` casting between raw pointers without changing their constness
-  --> tests/ui/ptr_as_ptr.rs:115:9
+  --> tests/ui/ptr_as_ptr.rs:117:9
    |
 LL |         std::ptr::null() as *const u32
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null::<u32>()`
 
 error: `as` casting between raw pointers without changing their constness
-  --> tests/ui/ptr_as_ptr.rs:121:9
+  --> tests/ui/ptr_as_ptr.rs:123:9
    |
 LL |         ptr::null() as *const u32
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null::<u32>()`
 
 error: `as` casting between raw pointers without changing their constness
-  --> tests/ui/ptr_as_ptr.rs:126:9
+  --> tests/ui/ptr_as_ptr.rs:128:9
    |
 LL |         core::ptr::null() as *const u32
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `core::ptr::null::<u32>()`
 
 error: `as` casting between raw pointers without changing their constness
-  --> tests/ui/ptr_as_ptr.rs:134:9
+  --> tests/ui/ptr_as_ptr.rs:136:9
    |
 LL |         ptr::null_mut() as *mut _
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut()`
 
 error: `as` casting between raw pointers without changing their constness
-  --> tests/ui/ptr_as_ptr.rs:139:9
+  --> tests/ui/ptr_as_ptr.rs:141:9
    |
 LL |         std::ptr::null_mut() as *mut _
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null_mut()`
 
 error: `as` casting between raw pointers without changing their constness
-  --> tests/ui/ptr_as_ptr.rs:145:9
+  --> tests/ui/ptr_as_ptr.rs:147:9
    |
 LL |         ptr::null_mut() as *mut _
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut()`
 
 error: `as` casting between raw pointers without changing their constness
-  --> tests/ui/ptr_as_ptr.rs:150:9
+  --> tests/ui/ptr_as_ptr.rs:152:9
    |
 LL |         core::ptr::null_mut() as *mut _
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `core::ptr::null_mut()`
 
 error: `as` casting between raw pointers without changing their constness
-  --> tests/ui/ptr_as_ptr.rs:156:9
+  --> tests/ui/ptr_as_ptr.rs:158:9
    |
 LL |         ptr::null() as *const _
    |         ^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null()`
 
 error: `as` casting between raw pointers without changing their constness
-  --> tests/ui/ptr_as_ptr.rs:161:9
+  --> tests/ui/ptr_as_ptr.rs:163:9
    |
 LL |         std::ptr::null() as *const _
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null()`
 
 error: `as` casting between raw pointers without changing their constness
-  --> tests/ui/ptr_as_ptr.rs:167:9
+  --> tests/ui/ptr_as_ptr.rs:169:9
    |
 LL |         ptr::null() as *const _
    |         ^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null()`
 
 error: `as` casting between raw pointers without changing their constness
-  --> tests/ui/ptr_as_ptr.rs:172:9
+  --> tests/ui/ptr_as_ptr.rs:174:9
    |
 LL |         core::ptr::null() as *const _
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `core::ptr::null()`
 
 error: `as` casting between raw pointers without changing their constness
-  --> tests/ui/ptr_as_ptr.rs:180:9
+  --> tests/ui/ptr_as_ptr.rs:182:9
    |
 LL |         ptr::null_mut() as _
    |         ^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut()`
 
 error: `as` casting between raw pointers without changing their constness
-  --> tests/ui/ptr_as_ptr.rs:185:9
+  --> tests/ui/ptr_as_ptr.rs:187:9
    |
 LL |         std::ptr::null_mut() as _
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null_mut()`
 
 error: `as` casting between raw pointers without changing their constness
-  --> tests/ui/ptr_as_ptr.rs:191:9
+  --> tests/ui/ptr_as_ptr.rs:193:9
    |
 LL |         ptr::null_mut() as _
    |         ^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut()`
 
 error: `as` casting between raw pointers without changing their constness
-  --> tests/ui/ptr_as_ptr.rs:196:9
+  --> tests/ui/ptr_as_ptr.rs:198:9
    |
 LL |         core::ptr::null_mut() as _
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `core::ptr::null_mut()`
 
 error: `as` casting between raw pointers without changing their constness
-  --> tests/ui/ptr_as_ptr.rs:202:9
+  --> tests/ui/ptr_as_ptr.rs:204:9
    |
 LL |         ptr::null() as _
    |         ^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null()`
 
 error: `as` casting between raw pointers without changing their constness
-  --> tests/ui/ptr_as_ptr.rs:207:9
+  --> tests/ui/ptr_as_ptr.rs:209:9
    |
 LL |         std::ptr::null() as _
    |         ^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null()`
 
 error: `as` casting between raw pointers without changing their constness
-  --> tests/ui/ptr_as_ptr.rs:213:9
+  --> tests/ui/ptr_as_ptr.rs:215:9
    |
 LL |         ptr::null() as _
    |         ^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null()`
 
 error: `as` casting between raw pointers without changing their constness
-  --> tests/ui/ptr_as_ptr.rs:218:9
+  --> tests/ui/ptr_as_ptr.rs:220:9
    |
 LL |         core::ptr::null() as _
    |         ^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `core::ptr::null()`
 
 error: `as` casting between raw pointers without changing their constness
-  --> tests/ui/ptr_as_ptr.rs:226:43
+  --> tests/ui/ptr_as_ptr.rs:228:43
    |
 LL |         let _: fn() = std::mem::transmute(std::ptr::null::<()>() as *const u8);
    |                                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null::<u8>()`
 
+error: `as` casting between raw pointers without changing their constness
+  --> tests/ui/ptr_as_ptr.rs:235:16
+   |
+LL |     let _ptr = std::ptr::addr_of_mut!(*b) as *mut ();
+   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `std::ptr::addr_of_mut!(*b).cast::<()>()`
+
 error: aborting due to 34 previous errors
 
diff --git a/src/tools/clippy/tests/ui/ptr_cast_constness.fixed b/src/tools/clippy/tests/ui/ptr_cast_constness.fixed
index 79bfae1f7eb..cf57de53d9f 100644
--- a/src/tools/clippy/tests/ui/ptr_cast_constness.fixed
+++ b/src/tools/clippy/tests/ui/ptr_cast_constness.fixed
@@ -106,3 +106,9 @@ fn issue14621() {
     let _ = std::ptr::addr_of_mut!(local).cast_const();
     //~^ ptr_cast_constness
 }
+
+fn issue11317() {
+    let r = &0_u32;
+    let _ptr: *mut u32 = (r as *const u32).cast_mut();
+    //~^ ptr_cast_constness
+}
diff --git a/src/tools/clippy/tests/ui/ptr_cast_constness.rs b/src/tools/clippy/tests/ui/ptr_cast_constness.rs
index f6590dabd5b..ea53a0fa8c5 100644
--- a/src/tools/clippy/tests/ui/ptr_cast_constness.rs
+++ b/src/tools/clippy/tests/ui/ptr_cast_constness.rs
@@ -106,3 +106,9 @@ fn issue14621() {
     let _ = std::ptr::addr_of_mut!(local) as *const _;
     //~^ ptr_cast_constness
 }
+
+fn issue11317() {
+    let r = &0_u32;
+    let _ptr: *mut u32 = r as *const _ as *mut _;
+    //~^ ptr_cast_constness
+}
diff --git a/src/tools/clippy/tests/ui/ptr_cast_constness.stderr b/src/tools/clippy/tests/ui/ptr_cast_constness.stderr
index 0b1644168ff..4adb5cc5ad7 100644
--- a/src/tools/clippy/tests/ui/ptr_cast_constness.stderr
+++ b/src/tools/clippy/tests/ui/ptr_cast_constness.stderr
@@ -89,5 +89,11 @@ error: `as` casting between raw pointers while changing only its constness
 LL |     let _ = std::ptr::addr_of_mut!(local) as *const _;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_const`, a safer alternative: `std::ptr::addr_of_mut!(local).cast_const()`
 
-error: aborting due to 14 previous errors
+error: `as` casting between raw pointers while changing only its constness
+  --> tests/ui/ptr_cast_constness.rs:112:26
+   |
+LL |     let _ptr: *mut u32 = r as *const _ as *mut _;
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast_mut`, a safer alternative: `(r as *const u32).cast_mut()`
+
+error: aborting due to 15 previous errors
 
diff --git a/src/tools/clippy/tests/ui/ptr_offset_with_cast.fixed b/src/tools/clippy/tests/ui/ptr_offset_with_cast.fixed
index 4fe9dcf46c3..42d1abeaa05 100644
--- a/src/tools/clippy/tests/ui/ptr_offset_with_cast.fixed
+++ b/src/tools/clippy/tests/ui/ptr_offset_with_cast.fixed
@@ -1,4 +1,4 @@
-#![allow(clippy::unnecessary_cast, clippy::useless_vec)]
+#![expect(clippy::unnecessary_cast, clippy::useless_vec, clippy::needless_borrow)]
 
 fn main() {
     let vec = vec![b'a', b'b', b'c'];
@@ -18,5 +18,25 @@ fn main() {
         //~^ ptr_offset_with_cast
         let _ = ptr.wrapping_offset(offset_isize as isize);
         let _ = ptr.wrapping_offset(offset_u8 as isize);
+
+        let _ = S.offset(offset_usize as isize);
+        let _ = S.wrapping_offset(offset_usize as isize);
+
+        let _ = (&ptr).add(offset_usize);
+        //~^ ptr_offset_with_cast
+        let _ = (&ptr).wrapping_add(offset_usize);
+        //~^ ptr_offset_with_cast
+    }
+}
+
+#[derive(Clone, Copy)]
+struct S;
+
+impl S {
+    fn offset(self, _: isize) -> Self {
+        self
+    }
+    fn wrapping_offset(self, _: isize) -> Self {
+        self
     }
 }
diff --git a/src/tools/clippy/tests/ui/ptr_offset_with_cast.rs b/src/tools/clippy/tests/ui/ptr_offset_with_cast.rs
index a1fb892733d..6d06a6af1fa 100644
--- a/src/tools/clippy/tests/ui/ptr_offset_with_cast.rs
+++ b/src/tools/clippy/tests/ui/ptr_offset_with_cast.rs
@@ -1,4 +1,4 @@
-#![allow(clippy::unnecessary_cast, clippy::useless_vec)]
+#![expect(clippy::unnecessary_cast, clippy::useless_vec, clippy::needless_borrow)]
 
 fn main() {
     let vec = vec![b'a', b'b', b'c'];
@@ -18,5 +18,25 @@ fn main() {
         //~^ ptr_offset_with_cast
         let _ = ptr.wrapping_offset(offset_isize as isize);
         let _ = ptr.wrapping_offset(offset_u8 as isize);
+
+        let _ = S.offset(offset_usize as isize);
+        let _ = S.wrapping_offset(offset_usize as isize);
+
+        let _ = (&ptr).offset(offset_usize as isize);
+        //~^ ptr_offset_with_cast
+        let _ = (&ptr).wrapping_offset(offset_usize as isize);
+        //~^ ptr_offset_with_cast
+    }
+}
+
+#[derive(Clone, Copy)]
+struct S;
+
+impl S {
+    fn offset(self, _: isize) -> Self {
+        self
+    }
+    fn wrapping_offset(self, _: isize) -> Self {
+        self
     }
 }
diff --git a/src/tools/clippy/tests/ui/ptr_offset_with_cast.stderr b/src/tools/clippy/tests/ui/ptr_offset_with_cast.stderr
index dcd5e027d18..022b3286c93 100644
--- a/src/tools/clippy/tests/ui/ptr_offset_with_cast.stderr
+++ b/src/tools/clippy/tests/ui/ptr_offset_with_cast.stderr
@@ -2,16 +2,51 @@ error: use of `offset` with a `usize` casted to an `isize`
   --> tests/ui/ptr_offset_with_cast.rs:12:17
    |
 LL |         let _ = ptr.offset(offset_usize as isize);
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr.add(offset_usize)`
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::ptr-offset-with-cast` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::ptr_offset_with_cast)]`
+help: use `add` instead
+   |
+LL -         let _ = ptr.offset(offset_usize as isize);
+LL +         let _ = ptr.add(offset_usize);
+   |
 
 error: use of `wrapping_offset` with a `usize` casted to an `isize`
   --> tests/ui/ptr_offset_with_cast.rs:17:17
    |
 LL |         let _ = ptr.wrapping_offset(offset_usize as isize);
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr.wrapping_add(offset_usize)`
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `wrapping_add` instead
+   |
+LL -         let _ = ptr.wrapping_offset(offset_usize as isize);
+LL +         let _ = ptr.wrapping_add(offset_usize);
+   |
+
+error: use of `offset` with a `usize` casted to an `isize`
+  --> tests/ui/ptr_offset_with_cast.rs:25:17
+   |
+LL |         let _ = (&ptr).offset(offset_usize as isize);
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `add` instead
+   |
+LL -         let _ = (&ptr).offset(offset_usize as isize);
+LL +         let _ = (&ptr).add(offset_usize);
+   |
+
+error: use of `wrapping_offset` with a `usize` casted to an `isize`
+  --> tests/ui/ptr_offset_with_cast.rs:27:17
+   |
+LL |         let _ = (&ptr).wrapping_offset(offset_usize as isize);
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `wrapping_add` instead
+   |
+LL -         let _ = (&ptr).wrapping_offset(offset_usize as isize);
+LL +         let _ = (&ptr).wrapping_add(offset_usize);
+   |
 
-error: aborting due to 2 previous errors
+error: aborting due to 4 previous errors
 
diff --git a/src/tools/clippy/tests/ui/question_mark.fixed b/src/tools/clippy/tests/ui/question_mark.fixed
index 8d6f5fbadca..ac81b324c20 100644
--- a/src/tools/clippy/tests/ui/question_mark.fixed
+++ b/src/tools/clippy/tests/ui/question_mark.fixed
@@ -1,6 +1,4 @@
 #![feature(try_blocks)]
-#![allow(unreachable_code)]
-#![allow(dead_code)]
 #![allow(clippy::unnecessary_wraps)]
 
 use std::sync::MutexGuard;
@@ -465,3 +463,15 @@ fn issue_13642(x: Option<i32>) -> Option<()> {
 
     None
 }
+
+fn issue_15679() -> Result<i32, String> {
+    let some_result: Result<i32, &'static str> = todo!();
+
+    some_result?;
+
+    some_result?;
+
+    some_result?;
+
+    Ok(0)
+}
diff --git a/src/tools/clippy/tests/ui/question_mark.rs b/src/tools/clippy/tests/ui/question_mark.rs
index f13eee29c11..b5866dac6b8 100644
--- a/src/tools/clippy/tests/ui/question_mark.rs
+++ b/src/tools/clippy/tests/ui/question_mark.rs
@@ -1,6 +1,4 @@
 #![feature(try_blocks)]
-#![allow(unreachable_code)]
-#![allow(dead_code)]
 #![allow(clippy::unnecessary_wraps)]
 
 use std::sync::MutexGuard;
@@ -561,3 +559,27 @@ fn issue_13642(x: Option<i32>) -> Option<()> {
 
     None
 }
+
+fn issue_15679() -> Result<i32, String> {
+    let some_result: Result<i32, &'static str> = todo!();
+
+    match some_result {
+        //~^ question_mark
+        Ok(val) => val,
+        Err(err) => return Err(err.into()),
+    };
+
+    match some_result {
+        //~^ question_mark
+        Ok(val) => val,
+        Err(err) => return Err(Into::into(err)),
+    };
+
+    match some_result {
+        //~^ question_mark
+        Ok(val) => val,
+        Err(err) => return Err(<&str as Into<String>>::into(err)),
+    };
+
+    Ok(0)
+}
diff --git a/src/tools/clippy/tests/ui/question_mark.stderr b/src/tools/clippy/tests/ui/question_mark.stderr
index d8ce4420aee..1ecd936292e 100644
--- a/src/tools/clippy/tests/ui/question_mark.stderr
+++ b/src/tools/clippy/tests/ui/question_mark.stderr
@@ -1,5 +1,5 @@
 error: this block may be rewritten with the `?` operator
-  --> tests/ui/question_mark.rs:9:5
+  --> tests/ui/question_mark.rs:7:5
    |
 LL | /     if a.is_none() {
 LL | |
@@ -11,7 +11,7 @@ LL | |     }
    = help: to override `-D warnings` add `#[allow(clippy::question_mark)]`
 
 error: this block may be rewritten with the `?` operator
-  --> tests/ui/question_mark.rs:55:9
+  --> tests/ui/question_mark.rs:53:9
    |
 LL | /         if (self.opt).is_none() {
 LL | |
@@ -20,7 +20,7 @@ LL | |         }
    | |_________^ help: replace it with: `(self.opt)?;`
 
 error: this block may be rewritten with the `?` operator
-  --> tests/ui/question_mark.rs:60:9
+  --> tests/ui/question_mark.rs:58:9
    |
 LL | /         if self.opt.is_none() {
 LL | |
@@ -29,7 +29,7 @@ LL | |         }
    | |_________^ help: replace it with: `self.opt?;`
 
 error: this block may be rewritten with the `?` operator
-  --> tests/ui/question_mark.rs:65:17
+  --> tests/ui/question_mark.rs:63:17
    |
 LL |           let _ = if self.opt.is_none() {
    |  _________________^
@@ -41,7 +41,7 @@ LL | |         };
    | |_________^ help: replace it with: `Some(self.opt?)`
 
 error: this block may be rewritten with the `?` operator
-  --> tests/ui/question_mark.rs:72:17
+  --> tests/ui/question_mark.rs:70:17
    |
 LL |           let _ = if let Some(x) = self.opt {
    |  _________________^
@@ -53,7 +53,7 @@ LL | |         };
    | |_________^ help: replace it with: `self.opt?`
 
 error: this block may be rewritten with the `?` operator
-  --> tests/ui/question_mark.rs:90:9
+  --> tests/ui/question_mark.rs:88:9
    |
 LL | /         if self.opt.is_none() {
 LL | |
@@ -62,7 +62,7 @@ LL | |         }
    | |_________^ help: replace it with: `self.opt.as_ref()?;`
 
 error: this block may be rewritten with the `?` operator
-  --> tests/ui/question_mark.rs:99:9
+  --> tests/ui/question_mark.rs:97:9
    |
 LL | /         if self.opt.is_none() {
 LL | |
@@ -71,7 +71,7 @@ LL | |         }
    | |_________^ help: replace it with: `self.opt.as_ref()?;`
 
 error: this block may be rewritten with the `?` operator
-  --> tests/ui/question_mark.rs:108:9
+  --> tests/ui/question_mark.rs:106:9
    |
 LL | /         if self.opt.is_none() {
 LL | |
@@ -80,7 +80,7 @@ LL | |         }
    | |_________^ help: replace it with: `self.opt.as_ref()?;`
 
 error: this block may be rewritten with the `?` operator
-  --> tests/ui/question_mark.rs:116:26
+  --> tests/ui/question_mark.rs:114:26
    |
 LL |           let v: &Vec<_> = if let Some(ref v) = self.opt {
    |  __________________________^
@@ -92,7 +92,7 @@ LL | |         };
    | |_________^ help: replace it with: `self.opt.as_ref()?`
 
 error: this block may be rewritten with the `?` operator
-  --> tests/ui/question_mark.rs:127:17
+  --> tests/ui/question_mark.rs:125:17
    |
 LL |           let v = if let Some(v) = self.opt {
    |  _________________^
@@ -104,7 +104,7 @@ LL | |         };
    | |_________^ help: replace it with: `self.opt?`
 
 error: this block may be rewritten with the `?` operator
-  --> tests/ui/question_mark.rs:149:5
+  --> tests/ui/question_mark.rs:147:5
    |
 LL | /     if f().is_none() {
 LL | |
@@ -113,7 +113,7 @@ LL | |     }
    | |_____^ help: replace it with: `f()?;`
 
 error: this `match` expression can be replaced with `?`
-  --> tests/ui/question_mark.rs:154:16
+  --> tests/ui/question_mark.rs:152:16
    |
 LL |       let _val = match f() {
    |  ________________^
@@ -124,7 +124,7 @@ LL | |     };
    | |_____^ help: try instead: `f()?`
 
 error: this `match` expression can be replaced with `?`
-  --> tests/ui/question_mark.rs:165:5
+  --> tests/ui/question_mark.rs:163:5
    |
 LL | /     match f() {
 LL | |
@@ -134,7 +134,7 @@ LL | |     };
    | |_____^ help: try instead: `f()?`
 
 error: this `match` expression can be replaced with `?`
-  --> tests/ui/question_mark.rs:171:5
+  --> tests/ui/question_mark.rs:169:5
    |
 LL | /     match opt_none!() {
 LL | |
@@ -144,13 +144,13 @@ LL | |     };
    | |_____^ help: try instead: `opt_none!()?`
 
 error: this block may be rewritten with the `?` operator
-  --> tests/ui/question_mark.rs:198:13
+  --> tests/ui/question_mark.rs:196: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:201:5
+  --> tests/ui/question_mark.rs:199:5
    |
 LL | /     if x.is_err() {
 LL | |
@@ -159,7 +159,7 @@ LL | |     }
    | |_____^ help: replace it with: `x?;`
 
 error: this `match` expression can be replaced with `?`
-  --> tests/ui/question_mark.rs:206:16
+  --> tests/ui/question_mark.rs:204:16
    |
 LL |       let _val = match func_returning_result() {
    |  ________________^
@@ -170,7 +170,7 @@ LL | |     };
    | |_____^ help: try instead: `func_returning_result()?`
 
 error: this `match` expression can be replaced with `?`
-  --> tests/ui/question_mark.rs:212:5
+  --> tests/ui/question_mark.rs:210:5
    |
 LL | /     match func_returning_result() {
 LL | |
@@ -180,7 +180,7 @@ LL | |     };
    | |_____^ help: try instead: `func_returning_result()?`
 
 error: this block may be rewritten with the `?` operator
-  --> tests/ui/question_mark.rs:304:5
+  --> tests/ui/question_mark.rs:302:5
    |
 LL | /     if let Err(err) = func_returning_result() {
 LL | |
@@ -189,7 +189,7 @@ LL | |     }
    | |_____^ help: replace it with: `func_returning_result()?;`
 
 error: this block may be rewritten with the `?` operator
-  --> tests/ui/question_mark.rs:312:5
+  --> tests/ui/question_mark.rs:310:5
    |
 LL | /     if let Err(err) = func_returning_result() {
 LL | |
@@ -198,7 +198,7 @@ LL | |     }
    | |_____^ help: replace it with: `func_returning_result()?;`
 
 error: this block may be rewritten with the `?` operator
-  --> tests/ui/question_mark.rs:395:13
+  --> tests/ui/question_mark.rs:393:13
    |
 LL | /             if a.is_none() {
 LL | |
@@ -208,7 +208,7 @@ LL | |             }
    | |_____________^ help: replace it with: `a?;`
 
 error: this `let...else` may be rewritten with the `?` operator
-  --> tests/ui/question_mark.rs:456:5
+  --> tests/ui/question_mark.rs:454:5
    |
 LL | /     let Some(v) = bar.foo.owned.clone() else {
 LL | |         return None;
@@ -216,7 +216,7 @@ LL | |     };
    | |______^ help: replace it with: `let v = bar.foo.owned.clone()?;`
 
 error: this `let...else` may be rewritten with the `?` operator
-  --> tests/ui/question_mark.rs:471:5
+  --> tests/ui/question_mark.rs:469:5
    |
 LL | /     let Some(ref x) = foo.opt_x else {
 LL | |         return None;
@@ -224,7 +224,7 @@ LL | |     };
    | |______^ help: replace it with: `let x = foo.opt_x.as_ref()?;`
 
 error: this `let...else` may be rewritten with the `?` operator
-  --> tests/ui/question_mark.rs:481:5
+  --> tests/ui/question_mark.rs:479:5
    |
 LL | /     let Some(ref mut x) = foo.opt_x else {
 LL | |         return None;
@@ -232,7 +232,7 @@ LL | |     };
    | |______^ help: replace it with: `let x = foo.opt_x.as_mut()?;`
 
 error: this `let...else` may be rewritten with the `?` operator
-  --> tests/ui/question_mark.rs:492:5
+  --> tests/ui/question_mark.rs:490:5
    |
 LL | /     let Some(ref x @ ref y) = foo.opt_x else {
 LL | |         return None;
@@ -240,7 +240,7 @@ LL | |     };
    | |______^ help: replace it with: `let x @ y = foo.opt_x.as_ref()?;`
 
 error: this `let...else` may be rewritten with the `?` operator
-  --> tests/ui/question_mark.rs:496:5
+  --> tests/ui/question_mark.rs:494:5
    |
 LL | /     let Some(ref x @ WrapperStructWithString(_)) = bar else {
 LL | |         return None;
@@ -248,7 +248,7 @@ LL | |     };
    | |______^ help: replace it with: `let x @ &WrapperStructWithString(_) = bar.as_ref()?;`
 
 error: this `let...else` may be rewritten with the `?` operator
-  --> tests/ui/question_mark.rs:500:5
+  --> tests/ui/question_mark.rs:498:5
    |
 LL | /     let Some(ref mut x @ WrapperStructWithString(_)) = bar else {
 LL | |         return None;
@@ -256,7 +256,7 @@ LL | |     };
    | |______^ help: replace it with: `let x @ &mut WrapperStructWithString(_) = bar.as_mut()?;`
 
 error: this block may be rewritten with the `?` operator
-  --> tests/ui/question_mark.rs:522:5
+  --> tests/ui/question_mark.rs:520:5
    |
 LL | /     if arg.is_none() {
 LL | |
@@ -265,7 +265,7 @@ LL | |     }
    | |_____^ help: replace it with: `arg?;`
 
 error: this `match` expression can be replaced with `?`
-  --> tests/ui/question_mark.rs:526:15
+  --> tests/ui/question_mark.rs:524:15
    |
 LL |       let val = match arg {
    |  _______________^
@@ -276,12 +276,42 @@ LL | |     };
    | |_____^ help: try instead: `arg?`
 
 error: this `let...else` may be rewritten with the `?` operator
-  --> tests/ui/question_mark.rs:536:5
+  --> tests/ui/question_mark.rs:534:5
    |
 LL | /     let Some(a) = *a else {
 LL | |         return None;
 LL | |     };
    | |______^ help: replace it with: `let a = (*a)?;`
 
-error: aborting due to 30 previous errors
+error: this `match` expression can be replaced with `?`
+  --> tests/ui/question_mark.rs:566:5
+   |
+LL | /     match some_result {
+LL | |
+LL | |         Ok(val) => val,
+LL | |         Err(err) => return Err(err.into()),
+LL | |     };
+   | |_____^ help: try instead: `some_result?`
+
+error: this `match` expression can be replaced with `?`
+  --> tests/ui/question_mark.rs:572:5
+   |
+LL | /     match some_result {
+LL | |
+LL | |         Ok(val) => val,
+LL | |         Err(err) => return Err(Into::into(err)),
+LL | |     };
+   | |_____^ help: try instead: `some_result?`
+
+error: this `match` expression can be replaced with `?`
+  --> tests/ui/question_mark.rs:578:5
+   |
+LL | /     match some_result {
+LL | |
+LL | |         Ok(val) => val,
+LL | |         Err(err) => return Err(<&str as Into<String>>::into(err)),
+LL | |     };
+   | |_____^ help: try instead: `some_result?`
+
+error: aborting due to 33 previous errors
 
diff --git a/src/tools/clippy/tests/ui/read_zero_byte_vec.rs b/src/tools/clippy/tests/ui/read_zero_byte_vec.rs
index 938d61b6860..720276cb554 100644
--- a/src/tools/clippy/tests/ui/read_zero_byte_vec.rs
+++ b/src/tools/clippy/tests/ui/read_zero_byte_vec.rs
@@ -120,3 +120,30 @@ fn allow_works<F: std::io::Read>(mut f: F) {
 }
 
 fn main() {}
+
+fn issue15575() -> usize {
+    use std::io::Read;
+    use std::net::TcpListener;
+
+    let listener = TcpListener::bind("127.0.0.1:9010").unwrap();
+    let mut stream_and_addr = listener.accept().unwrap();
+    let mut buf = Vec::with_capacity(32);
+    let num_bytes_received = stream_and_addr.0.read(&mut buf).unwrap();
+    //~^ read_zero_byte_vec
+
+    let cap = 1000;
+    let mut buf = Vec::with_capacity(cap);
+    let num_bytes_received = stream_and_addr.0.read(&mut buf).unwrap();
+    //~^ read_zero_byte_vec
+
+    let cap = 1000;
+    let mut buf = Vec::with_capacity(cap);
+    let num_bytes_received = { stream_and_addr.0.read(&mut buf) }.unwrap();
+    //~^ read_zero_byte_vec
+
+    use std::fs::File;
+    let mut f = File::open("foo.txt").unwrap();
+    let mut data = Vec::with_capacity(100);
+    f.read(&mut data).unwrap()
+    //~^ read_zero_byte_vec
+}
diff --git a/src/tools/clippy/tests/ui/read_zero_byte_vec.stderr b/src/tools/clippy/tests/ui/read_zero_byte_vec.stderr
index 8f255bc87ab..8dd74592e4c 100644
--- a/src/tools/clippy/tests/ui/read_zero_byte_vec.stderr
+++ b/src/tools/clippy/tests/ui/read_zero_byte_vec.stderr
@@ -2,16 +2,27 @@ error: reading zero byte data to `Vec`
   --> tests/ui/read_zero_byte_vec.rs:22:5
    |
 LL |     f.read_exact(&mut data).unwrap();
-   |     ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `data.resize(20, 0); f.read_exact(&mut data)`
+   |     ^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::read-zero-byte-vec` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::read_zero_byte_vec)]`
+help: try
+   |
+LL ~     data.resize(20, 0);
+LL ~     f.read_exact(&mut data).unwrap();
+   |
 
 error: reading zero byte data to `Vec`
   --> tests/ui/read_zero_byte_vec.rs:28:5
    |
 LL |     f.read_exact(&mut data2)?;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `data2.resize(cap, 0); f.read_exact(&mut data2)`
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL ~     data2.resize(cap, 0);
+LL ~     f.read_exact(&mut data2)?;
+   |
 
 error: reading zero byte data to `Vec`
   --> tests/ui/read_zero_byte_vec.rs:33:5
@@ -67,5 +78,53 @@ error: reading zero byte data to `Vec`
 LL |     r.read_exact(&mut data2).await.unwrap();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 11 previous errors
+error: reading zero byte data to `Vec`
+  --> tests/ui/read_zero_byte_vec.rs:131:30
+   |
+LL |     let num_bytes_received = stream_and_addr.0.read(&mut buf).unwrap();
+   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL ~     buf.resize(32, 0);
+LL ~     let num_bytes_received = stream_and_addr.0.read(&mut buf).unwrap();
+   |
+
+error: reading zero byte data to `Vec`
+  --> tests/ui/read_zero_byte_vec.rs:136:30
+   |
+LL |     let num_bytes_received = stream_and_addr.0.read(&mut buf).unwrap();
+   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL ~     buf.resize(cap, 0);
+LL ~     let num_bytes_received = stream_and_addr.0.read(&mut buf).unwrap();
+   |
+
+error: reading zero byte data to `Vec`
+  --> tests/ui/read_zero_byte_vec.rs:141:32
+   |
+LL |     let num_bytes_received = { stream_and_addr.0.read(&mut buf) }.unwrap();
+   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL ~     buf.resize(cap, 0);
+LL ~     let num_bytes_received = { stream_and_addr.0.read(&mut buf) }.unwrap();
+   |
+
+error: reading zero byte data to `Vec`
+  --> tests/ui/read_zero_byte_vec.rs:147:5
+   |
+LL |     f.read(&mut data).unwrap()
+   |     ^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL ~     data.resize(100, 0);
+LL ~     f.read(&mut data).unwrap()
+   |
+
+error: aborting due to 15 previous errors
 
diff --git a/src/tools/clippy/tests/ui/ref_option/ref_option.all.fixed b/src/tools/clippy/tests/ui/ref_option/ref_option.all.fixed
deleted file mode 100644
index 4159e916f19..00000000000
--- a/src/tools/clippy/tests/ui/ref_option/ref_option.all.fixed
+++ /dev/null
@@ -1,79 +0,0 @@
-//@revisions: private all
-//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/private
-//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/all
-
-#![allow(unused, clippy::needless_lifetimes, clippy::borrowed_box)]
-#![warn(clippy::ref_option)]
-
-fn opt_u8(a: Option<&u8>) {}
-//~^ ref_option
-fn opt_gen<T>(a: Option<&T>) {}
-//~^ ref_option
-fn opt_string(a: std::option::Option<&String>) {}
-//~^ ref_option
-fn ret_string<'a>(p: &'a str) -> Option<&'a u8> {
-    //~^ ref_option
-    panic!()
-}
-fn ret_string_static() -> Option<&'static u8> {
-    //~^ ref_option
-    panic!()
-}
-fn mult_string(a: Option<&String>, b: Option<&Vec<u8>>) {}
-//~^ ref_option
-fn ret_box<'a>() -> Option<&'a Box<u8>> {
-    //~^ ref_option
-    panic!()
-}
-
-pub fn pub_opt_string(a: Option<&String>) {}
-//~[all]^ ref_option
-pub fn pub_mult_string(a: Option<&String>, b: Option<&Vec<u8>>) {}
-//~[all]^ ref_option
-
-pub trait PubTrait {
-    fn pub_trait_opt(&self, a: Option<&Vec<u8>>);
-    //~[all]^ ref_option
-    fn pub_trait_ret(&self) -> Option<&Vec<u8>>;
-    //~[all]^ ref_option
-}
-
-trait PrivateTrait {
-    fn trait_opt(&self, a: Option<&String>);
-    //~^ ref_option
-    fn trait_ret(&self) -> Option<&String>;
-    //~^ ref_option
-}
-
-pub struct PubStruct;
-
-impl PubStruct {
-    pub fn pub_opt_params(&self, a: Option<&()>) {}
-    //~[all]^ ref_option
-    pub fn pub_opt_ret(&self) -> Option<&String> {
-        //~[all]^ ref_option
-        panic!()
-    }
-
-    fn private_opt_params(&self, a: Option<&()>) {}
-    //~^ ref_option
-    fn private_opt_ret(&self) -> Option<&String> {
-        //~^ ref_option
-        panic!()
-    }
-}
-
-// valid, don't change
-fn mut_u8(a: &mut Option<u8>) {}
-pub fn pub_mut_u8(a: &mut Option<String>) {}
-
-// might be good to catch in the future
-fn mut_u8_ref(a: &mut &Option<u8>) {}
-pub fn pub_mut_u8_ref(a: &mut &Option<String>) {}
-fn lambdas() {
-    // Not handled for now, not sure if we should
-    let x = |a: &Option<String>| {};
-    let x = |a: &Option<String>| -> &Option<String> { panic!() };
-}
-
-fn main() {}
diff --git a/src/tools/clippy/tests/ui/ref_option/ref_option.private.fixed b/src/tools/clippy/tests/ui/ref_option/ref_option.private.fixed
deleted file mode 100644
index 3b158befb92..00000000000
--- a/src/tools/clippy/tests/ui/ref_option/ref_option.private.fixed
+++ /dev/null
@@ -1,79 +0,0 @@
-//@revisions: private all
-//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/private
-//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/all
-
-#![allow(unused, clippy::needless_lifetimes, clippy::borrowed_box)]
-#![warn(clippy::ref_option)]
-
-fn opt_u8(a: Option<&u8>) {}
-//~^ ref_option
-fn opt_gen<T>(a: Option<&T>) {}
-//~^ ref_option
-fn opt_string(a: std::option::Option<&String>) {}
-//~^ ref_option
-fn ret_string<'a>(p: &'a str) -> Option<&'a u8> {
-    //~^ ref_option
-    panic!()
-}
-fn ret_string_static() -> Option<&'static u8> {
-    //~^ ref_option
-    panic!()
-}
-fn mult_string(a: Option<&String>, b: Option<&Vec<u8>>) {}
-//~^ ref_option
-fn ret_box<'a>() -> Option<&'a Box<u8>> {
-    //~^ ref_option
-    panic!()
-}
-
-pub fn pub_opt_string(a: &Option<String>) {}
-//~[all]^ ref_option
-pub fn pub_mult_string(a: &Option<String>, b: &Option<Vec<u8>>) {}
-//~[all]^ ref_option
-
-pub trait PubTrait {
-    fn pub_trait_opt(&self, a: &Option<Vec<u8>>);
-    //~[all]^ ref_option
-    fn pub_trait_ret(&self) -> &Option<Vec<u8>>;
-    //~[all]^ ref_option
-}
-
-trait PrivateTrait {
-    fn trait_opt(&self, a: Option<&String>);
-    //~^ ref_option
-    fn trait_ret(&self) -> Option<&String>;
-    //~^ ref_option
-}
-
-pub struct PubStruct;
-
-impl PubStruct {
-    pub fn pub_opt_params(&self, a: &Option<()>) {}
-    //~[all]^ ref_option
-    pub fn pub_opt_ret(&self) -> &Option<String> {
-        //~[all]^ ref_option
-        panic!()
-    }
-
-    fn private_opt_params(&self, a: Option<&()>) {}
-    //~^ ref_option
-    fn private_opt_ret(&self) -> Option<&String> {
-        //~^ ref_option
-        panic!()
-    }
-}
-
-// valid, don't change
-fn mut_u8(a: &mut Option<u8>) {}
-pub fn pub_mut_u8(a: &mut Option<String>) {}
-
-// might be good to catch in the future
-fn mut_u8_ref(a: &mut &Option<u8>) {}
-pub fn pub_mut_u8_ref(a: &mut &Option<String>) {}
-fn lambdas() {
-    // Not handled for now, not sure if we should
-    let x = |a: &Option<String>| {};
-    let x = |a: &Option<String>| -> &Option<String> { panic!() };
-}
-
-fn main() {}
diff --git a/src/tools/clippy/tests/ui/ref_option/ref_option.rs b/src/tools/clippy/tests/ui/ref_option/ref_option.rs
deleted file mode 100644
index 35cd94174f8..00000000000
--- a/src/tools/clippy/tests/ui/ref_option/ref_option.rs
+++ /dev/null
@@ -1,79 +0,0 @@
-//@revisions: private all
-//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/private
-//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/all
-
-#![allow(unused, clippy::needless_lifetimes, clippy::borrowed_box)]
-#![warn(clippy::ref_option)]
-
-fn opt_u8(a: &Option<u8>) {}
-//~^ ref_option
-fn opt_gen<T>(a: &Option<T>) {}
-//~^ ref_option
-fn opt_string(a: &std::option::Option<String>) {}
-//~^ ref_option
-fn ret_string<'a>(p: &'a str) -> &'a Option<u8> {
-    //~^ ref_option
-    panic!()
-}
-fn ret_string_static() -> &'static Option<u8> {
-    //~^ ref_option
-    panic!()
-}
-fn mult_string(a: &Option<String>, b: &Option<Vec<u8>>) {}
-//~^ ref_option
-fn ret_box<'a>() -> &'a Option<Box<u8>> {
-    //~^ ref_option
-    panic!()
-}
-
-pub fn pub_opt_string(a: &Option<String>) {}
-//~[all]^ ref_option
-pub fn pub_mult_string(a: &Option<String>, b: &Option<Vec<u8>>) {}
-//~[all]^ ref_option
-
-pub trait PubTrait {
-    fn pub_trait_opt(&self, a: &Option<Vec<u8>>);
-    //~[all]^ ref_option
-    fn pub_trait_ret(&self) -> &Option<Vec<u8>>;
-    //~[all]^ ref_option
-}
-
-trait PrivateTrait {
-    fn trait_opt(&self, a: &Option<String>);
-    //~^ ref_option
-    fn trait_ret(&self) -> &Option<String>;
-    //~^ ref_option
-}
-
-pub struct PubStruct;
-
-impl PubStruct {
-    pub fn pub_opt_params(&self, a: &Option<()>) {}
-    //~[all]^ ref_option
-    pub fn pub_opt_ret(&self) -> &Option<String> {
-        //~[all]^ ref_option
-        panic!()
-    }
-
-    fn private_opt_params(&self, a: &Option<()>) {}
-    //~^ ref_option
-    fn private_opt_ret(&self) -> &Option<String> {
-        //~^ ref_option
-        panic!()
-    }
-}
-
-// valid, don't change
-fn mut_u8(a: &mut Option<u8>) {}
-pub fn pub_mut_u8(a: &mut Option<String>) {}
-
-// might be good to catch in the future
-fn mut_u8_ref(a: &mut &Option<u8>) {}
-pub fn pub_mut_u8_ref(a: &mut &Option<String>) {}
-fn lambdas() {
-    // Not handled for now, not sure if we should
-    let x = |a: &Option<String>| {};
-    let x = |a: &Option<String>| -> &Option<String> { panic!() };
-}
-
-fn main() {}
diff --git a/src/tools/clippy/tests/ui/ref_option/ref_option_traits.rs b/src/tools/clippy/tests/ui/ref_option/ref_option_traits.rs
deleted file mode 100644
index 4c773e84f8d..00000000000
--- a/src/tools/clippy/tests/ui/ref_option/ref_option_traits.rs
+++ /dev/null
@@ -1,40 +0,0 @@
-//@no-rustfix: fixes are only done to traits, not the impls
-//@revisions: private all
-//@[private] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/private
-//@[all] rustc-env:CLIPPY_CONF_DIR=tests/ui/ref_option/all
-
-#![warn(clippy::ref_option)]
-
-pub trait PubTrait {
-    fn pub_trait_opt(&self, a: &Option<Vec<u8>>);
-    //~[all]^ ref_option
-    fn pub_trait_ret(&self) -> &Option<Vec<u8>>;
-    //~[all]^ ref_option
-}
-
-trait PrivateTrait {
-    fn trait_opt(&self, a: &Option<String>);
-    //~^ ref_option
-    fn trait_ret(&self) -> &Option<String>;
-    //~^ ref_option
-}
-
-pub struct PubStruct;
-
-impl PubTrait for PubStruct {
-    fn pub_trait_opt(&self, a: &Option<Vec<u8>>) {}
-    fn pub_trait_ret(&self) -> &Option<Vec<u8>> {
-        panic!()
-    }
-}
-
-struct PrivateStruct;
-
-impl PrivateTrait for PrivateStruct {
-    fn trait_opt(&self, a: &Option<String>) {}
-    fn trait_ret(&self) -> &Option<String> {
-        panic!()
-    }
-}
-
-fn main() {}
diff --git a/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.fixed b/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.fixed
new file mode 100644
index 00000000000..ac200b3b19b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.fixed
@@ -0,0 +1,61 @@
+#![warn(clippy::rest_pat_in_fully_bound_structs)]
+#![allow(clippy::struct_field_names)]
+
+struct A {
+    a: i32,
+    b: i64,
+    c: &'static str,
+}
+
+macro_rules! foo {
+    ($param:expr) => {
+        match $param {
+            A { a: 0, b: 0, c: "", .. } => {},
+            _ => {},
+        }
+    };
+}
+
+fn main() {
+    let a_struct = A { a: 5, b: 42, c: "A" };
+
+    match a_struct {
+        A { a: 5, b: 42, c: "",  } => {}, // Lint
+        //~^ rest_pat_in_fully_bound_structs
+        A { a: 0, b: 0, c: "",  } => {}, // Lint
+        //~^ rest_pat_in_fully_bound_structs
+        _ => {},
+    }
+
+    match a_struct {
+        A { a: 5, b: 42, .. } => {},
+        A { a: 0, b: 0, c: "",  } => {}, // Lint
+        //~^ rest_pat_in_fully_bound_structs
+        _ => {},
+    }
+
+    // No lint
+    match a_struct {
+        A { a: 5, .. } => {},
+        A { a: 0, b: 0, .. } => {},
+        _ => {},
+    }
+
+    // No lint
+    foo!(a_struct);
+
+    #[non_exhaustive]
+    struct B {
+        a: u32,
+        b: u32,
+        c: u64,
+    }
+
+    let b_struct = B { a: 5, b: 42, c: 342 };
+
+    match b_struct {
+        B { a: 5, b: 42, .. } => {},
+        B { a: 0, b: 0, c: 128, .. } => {}, // No Lint
+        _ => {},
+    }
+}
diff --git a/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.stderr b/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.stderr
index d048933ddb7..8a2da302b9e 100644
--- a/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.stderr
+++ b/src/tools/clippy/tests/ui/rest_pat_in_fully_bound_structs.stderr
@@ -4,9 +4,13 @@ error: unnecessary use of `..` pattern in struct binding. All fields were alread
 LL |         A { a: 5, b: 42, c: "", .. } => {}, // Lint
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: consider removing `..` from this binding
    = note: `-D clippy::rest-pat-in-fully-bound-structs` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::rest_pat_in_fully_bound_structs)]`
+help: consider removing `..` from this binding
+   |
+LL -         A { a: 5, b: 42, c: "", .. } => {}, // Lint
+LL +         A { a: 5, b: 42, c: "",  } => {}, // Lint
+   |
 
 error: unnecessary use of `..` pattern in struct binding. All fields were already bound
   --> tests/ui/rest_pat_in_fully_bound_structs.rs:25:9
@@ -14,7 +18,11 @@ error: unnecessary use of `..` pattern in struct binding. All fields were alread
 LL |         A { a: 0, b: 0, c: "", .. } => {}, // Lint
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: consider removing `..` from this binding
+help: consider removing `..` from this binding
+   |
+LL -         A { a: 0, b: 0, c: "", .. } => {}, // Lint
+LL +         A { a: 0, b: 0, c: "",  } => {}, // Lint
+   |
 
 error: unnecessary use of `..` pattern in struct binding. All fields were already bound
   --> tests/ui/rest_pat_in_fully_bound_structs.rs:32:9
@@ -22,7 +30,11 @@ error: unnecessary use of `..` pattern in struct binding. All fields were alread
 LL |         A { a: 0, b: 0, c: "", .. } => {}, // Lint
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: consider removing `..` from this binding
+help: consider removing `..` from this binding
+   |
+LL -         A { a: 0, b: 0, c: "", .. } => {}, // Lint
+LL +         A { a: 0, b: 0, c: "",  } => {}, // Lint
+   |
 
 error: aborting due to 3 previous errors
 
diff --git a/src/tools/clippy/tests/ui/semicolon_inside_block.fixed b/src/tools/clippy/tests/ui/semicolon_inside_block.fixed
index 7eb53e733ad..468f0a5b1e4 100644
--- a/src/tools/clippy/tests/ui/semicolon_inside_block.fixed
+++ b/src/tools/clippy/tests/ui/semicolon_inside_block.fixed
@@ -3,7 +3,8 @@
     clippy::unused_unit,
     clippy::unnecessary_operation,
     clippy::no_effect,
-    clippy::single_element_loop
+    clippy::single_element_loop,
+    clippy::double_parens
 )]
 #![warn(clippy::semicolon_inside_block)]
 
@@ -86,3 +87,22 @@ fn main() {
 
     unit_fn_block()
 }
+
+#[rustfmt::skip]
+fn issue15380() {
+    ( {0;0});
+
+    ({
+        0;
+        0
+    });
+
+    (({ 0 }))      ;
+
+    ( ( { 0 } ) ) ;
+}
+
+pub fn issue15388() {
+    #[rustfmt::skip]
+    {0; 0};
+}
diff --git a/src/tools/clippy/tests/ui/semicolon_inside_block.rs b/src/tools/clippy/tests/ui/semicolon_inside_block.rs
index 9fa5b117194..101374af264 100644
--- a/src/tools/clippy/tests/ui/semicolon_inside_block.rs
+++ b/src/tools/clippy/tests/ui/semicolon_inside_block.rs
@@ -3,7 +3,8 @@
     clippy::unused_unit,
     clippy::unnecessary_operation,
     clippy::no_effect,
-    clippy::single_element_loop
+    clippy::single_element_loop,
+    clippy::double_parens
 )]
 #![warn(clippy::semicolon_inside_block)]
 
@@ -86,3 +87,22 @@ fn main() {
 
     unit_fn_block()
 }
+
+#[rustfmt::skip]
+fn issue15380() {
+    ( {0;0});
+
+    ({
+        0;
+        0
+    });
+
+    (({ 0 }))      ;
+
+    ( ( { 0 } ) ) ;
+}
+
+pub fn issue15388() {
+    #[rustfmt::skip]
+    {0; 0};
+}
diff --git a/src/tools/clippy/tests/ui/semicolon_inside_block.stderr b/src/tools/clippy/tests/ui/semicolon_inside_block.stderr
index 23433f4e7ef..2046dd1c36b 100644
--- a/src/tools/clippy/tests/ui/semicolon_inside_block.stderr
+++ b/src/tools/clippy/tests/ui/semicolon_inside_block.stderr
@@ -1,5 +1,5 @@
 error: consider moving the `;` inside the block for consistent formatting
-  --> tests/ui/semicolon_inside_block.rs:38:5
+  --> tests/ui/semicolon_inside_block.rs:39:5
    |
 LL |     { unit_fn_block() };
    |     ^^^^^^^^^^^^^^^^^^^^
@@ -13,7 +13,7 @@ LL +     { unit_fn_block(); }
    |
 
 error: consider moving the `;` inside the block for consistent formatting
-  --> tests/ui/semicolon_inside_block.rs:40:5
+  --> tests/ui/semicolon_inside_block.rs:41:5
    |
 LL |     unsafe { unit_fn_block() };
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -25,7 +25,7 @@ LL +     unsafe { unit_fn_block(); }
    |
 
 error: consider moving the `;` inside the block for consistent formatting
-  --> tests/ui/semicolon_inside_block.rs:49:5
+  --> tests/ui/semicolon_inside_block.rs:50:5
    |
 LL | /     {
 LL | |
@@ -41,7 +41,7 @@ LL ~     }
    |
 
 error: consider moving the `;` inside the block for consistent formatting
-  --> tests/ui/semicolon_inside_block.rs:63:5
+  --> tests/ui/semicolon_inside_block.rs:64:5
    |
 LL |     { m!(()) };
    |     ^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/semicolon_inside_block_stmt_expr_attrs.fixed b/src/tools/clippy/tests/ui/semicolon_inside_block_stmt_expr_attrs.fixed
new file mode 100644
index 00000000000..5b93a91da00
--- /dev/null
+++ b/src/tools/clippy/tests/ui/semicolon_inside_block_stmt_expr_attrs.fixed
@@ -0,0 +1,11 @@
+// Test when the feature `stmt_expr_attributes` is enabled
+
+#![feature(stmt_expr_attributes)]
+#![allow(clippy::no_effect)]
+#![warn(clippy::semicolon_inside_block)]
+
+pub fn issue15388() {
+    #[rustfmt::skip]
+    {0; 0;}
+    //~^ semicolon_inside_block
+}
diff --git a/src/tools/clippy/tests/ui/semicolon_inside_block_stmt_expr_attrs.rs b/src/tools/clippy/tests/ui/semicolon_inside_block_stmt_expr_attrs.rs
new file mode 100644
index 00000000000..aa2c0cb5029
--- /dev/null
+++ b/src/tools/clippy/tests/ui/semicolon_inside_block_stmt_expr_attrs.rs
@@ -0,0 +1,11 @@
+// Test when the feature `stmt_expr_attributes` is enabled
+
+#![feature(stmt_expr_attributes)]
+#![allow(clippy::no_effect)]
+#![warn(clippy::semicolon_inside_block)]
+
+pub fn issue15388() {
+    #[rustfmt::skip]
+    {0; 0};
+    //~^ semicolon_inside_block
+}
diff --git a/src/tools/clippy/tests/ui/semicolon_inside_block_stmt_expr_attrs.stderr b/src/tools/clippy/tests/ui/semicolon_inside_block_stmt_expr_attrs.stderr
new file mode 100644
index 00000000000..5bb91915a5f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/semicolon_inside_block_stmt_expr_attrs.stderr
@@ -0,0 +1,16 @@
+error: consider moving the `;` inside the block for consistent formatting
+  --> tests/ui/semicolon_inside_block_stmt_expr_attrs.rs:9:5
+   |
+LL |     {0; 0};
+   |     ^^^^^^^
+   |
+   = note: `-D clippy::semicolon-inside-block` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::semicolon_inside_block)]`
+help: put the `;` here
+   |
+LL -     {0; 0};
+LL +     {0; 0;}
+   |
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/clippy/tests/ui/similar_names.rs b/src/tools/clippy/tests/ui/similar_names.rs
index 69b6ab6220b..55a141209f0 100644
--- a/src/tools/clippy/tests/ui/similar_names.rs
+++ b/src/tools/clippy/tests/ui/similar_names.rs
@@ -89,6 +89,10 @@ fn main() {
 
     let iter: i32;
     let item: i32;
+
+    // 3 letter names are allowed to be similar
+    let kta: i32;
+    let ktv: i32;
 }
 
 fn foo() {
diff --git a/src/tools/clippy/tests/ui/similar_names.stderr b/src/tools/clippy/tests/ui/similar_names.stderr
index 8d722fb8b56..c226f73d4db 100644
--- a/src/tools/clippy/tests/ui/similar_names.stderr
+++ b/src/tools/clippy/tests/ui/similar_names.stderr
@@ -49,13 +49,13 @@ LL |     let parser: i32;
    |         ^^^^^^
 
 error: binding's name is too similar to existing binding
-  --> tests/ui/similar_names.rs:98:16
+  --> tests/ui/similar_names.rs:102:16
    |
 LL |         bpple: sprang,
    |                ^^^^^^
    |
 note: existing binding defined here
-  --> tests/ui/similar_names.rs:97:16
+  --> tests/ui/similar_names.rs:101:16
    |
 LL |         apple: spring,
    |                ^^^^^^
diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.fixed b/src/tools/clippy/tests/ui/std_instead_of_core.fixed
index 603ab0accb0..c27cec55824 100644
--- a/src/tools/clippy/tests/ui/std_instead_of_core.fixed
+++ b/src/tools/clippy/tests/ui/std_instead_of_core.fixed
@@ -89,3 +89,10 @@ fn msrv_1_76(_: std::net::IpAddr) {}
 #[clippy::msrv = "1.77"]
 fn msrv_1_77(_: core::net::IpAddr) {}
 //~^ std_instead_of_core
+
+#[warn(clippy::alloc_instead_of_core)]
+fn issue15579() {
+    use std::alloc;
+
+    let layout = alloc::Layout::new::<u8>();
+}
diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.rs b/src/tools/clippy/tests/ui/std_instead_of_core.rs
index b6d4abad9f8..7d53f7fc307 100644
--- a/src/tools/clippy/tests/ui/std_instead_of_core.rs
+++ b/src/tools/clippy/tests/ui/std_instead_of_core.rs
@@ -89,3 +89,10 @@ fn msrv_1_76(_: std::net::IpAddr) {}
 #[clippy::msrv = "1.77"]
 fn msrv_1_77(_: std::net::IpAddr) {}
 //~^ std_instead_of_core
+
+#[warn(clippy::alloc_instead_of_core)]
+fn issue15579() {
+    use std::alloc;
+
+    let layout = alloc::Layout::new::<u8>();
+}
diff --git a/src/tools/clippy/tests/ui/track-diagnostics-clippy.stderr b/src/tools/clippy/tests/ui/track-diagnostics-clippy.stderr
index d5533877b45..3ceb501463b 100644
--- a/src/tools/clippy/tests/ui/track-diagnostics-clippy.stderr
+++ b/src/tools/clippy/tests/ui/track-diagnostics-clippy.stderr
@@ -16,7 +16,7 @@ LL |         let d = 42;
 LL |         d
    |         ^
    |
-   = note: -Ztrack-diagnostics: created at clippy_lints/src/returns.rs:LL:CC
+   = note: -Ztrack-diagnostics: created at clippy_lints/src/returns/let_and_return.rs:LL:CC
    = note: `-D clippy::let-and-return` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::let_and_return)]`
 help: return the expression directly
diff --git a/src/tools/clippy/tests/ui/transmute.rs b/src/tools/clippy/tests/ui/transmute.rs
index e968e7a5924..e7099104f94 100644
--- a/src/tools/clippy/tests/ui/transmute.rs
+++ b/src/tools/clippy/tests/ui/transmute.rs
@@ -4,6 +4,7 @@
     dead_code,
     clippy::borrow_as_ptr,
     unnecessary_transmutes,
+    integer_to_ptr_transmutes,
     clippy::needless_lifetimes,
     clippy::missing_transmute_annotations
 )]
@@ -60,12 +61,10 @@ fn useless() {
         //~^ useless_transmute
 
         let _: *const usize = std::mem::transmute(5_isize);
-        //~^ useless_transmute
 
         let _ = std::ptr::dangling::<usize>();
 
         let _: *const usize = std::mem::transmute(1 + 1usize);
-        //~^ useless_transmute
 
         let _ = (1 + 1_usize) as *const usize;
     }
diff --git a/src/tools/clippy/tests/ui/transmute.stderr b/src/tools/clippy/tests/ui/transmute.stderr
index 79528ec06f1..9478db09481 100644
--- a/src/tools/clippy/tests/ui/transmute.stderr
+++ b/src/tools/clippy/tests/ui/transmute.stderr
@@ -1,5 +1,5 @@
 error: transmute from a reference to a pointer
-  --> tests/ui/transmute.rs:33:27
+  --> tests/ui/transmute.rs:34:27
    |
 LL |         let _: *const T = core::mem::transmute(t);
    |                           ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T`
@@ -8,61 +8,49 @@ LL |         let _: *const T = core::mem::transmute(t);
    = help: to override `-D warnings` add `#[allow(clippy::useless_transmute)]`
 
 error: transmute from a reference to a pointer
-  --> tests/ui/transmute.rs:36:25
+  --> tests/ui/transmute.rs:37:25
    |
 LL |         let _: *mut T = core::mem::transmute(t);
    |                         ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *mut T`
 
 error: transmute from a reference to a pointer
-  --> tests/ui/transmute.rs:39:27
+  --> tests/ui/transmute.rs:40:27
    |
 LL |         let _: *const U = core::mem::transmute(t);
    |                           ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `t as *const T as *const U`
 
 error: transmute from a type (`std::vec::Vec<i32>`) to itself
-  --> tests/ui/transmute.rs:47:27
+  --> tests/ui/transmute.rs:48:27
    |
 LL |         let _: Vec<i32> = core::mem::transmute(my_vec());
    |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: transmute from a type (`std::vec::Vec<i32>`) to itself
-  --> tests/ui/transmute.rs:50:27
+  --> tests/ui/transmute.rs:51:27
    |
 LL |         let _: Vec<i32> = core::mem::transmute(my_vec());
    |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: transmute from a type (`std::vec::Vec<i32>`) to itself
-  --> tests/ui/transmute.rs:53:27
+  --> tests/ui/transmute.rs:54:27
    |
 LL |         let _: Vec<i32> = std::mem::transmute(my_vec());
    |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: transmute from a type (`std::vec::Vec<i32>`) to itself
-  --> tests/ui/transmute.rs:56:27
+  --> tests/ui/transmute.rs:57:27
    |
 LL |         let _: Vec<i32> = std::mem::transmute(my_vec());
    |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: transmute from a type (`std::vec::Vec<i32>`) to itself
-  --> tests/ui/transmute.rs:59:27
+  --> tests/ui/transmute.rs:60:27
    |
 LL |         let _: Vec<i32> = my_transmute(my_vec());
    |                           ^^^^^^^^^^^^^^^^^^^^^^
 
-error: transmute from an integer to a pointer
-  --> tests/ui/transmute.rs:62:31
-   |
-LL |         let _: *const usize = std::mem::transmute(5_isize);
-   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `5_isize as *const usize`
-
-error: transmute from an integer to a pointer
-  --> tests/ui/transmute.rs:67:31
-   |
-LL |         let _: *const usize = std::mem::transmute(1 + 1usize);
-   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(1 + 1usize) as *const usize`
-
 error: transmute from a type (`*const Usize`) to the type that it points to (`Usize`)
-  --> tests/ui/transmute.rs:99:24
+  --> tests/ui/transmute.rs:98:24
    |
 LL |         let _: Usize = core::mem::transmute(int_const_ptr);
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -71,25 +59,25 @@ LL |         let _: Usize = core::mem::transmute(int_const_ptr);
    = help: to override `-D warnings` add `#[allow(clippy::crosspointer_transmute)]`
 
 error: transmute from a type (`*mut Usize`) to the type that it points to (`Usize`)
-  --> tests/ui/transmute.rs:102:24
+  --> tests/ui/transmute.rs:101:24
    |
 LL |         let _: Usize = core::mem::transmute(int_mut_ptr);
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: transmute from a type (`Usize`) to a pointer to that type (`*const Usize`)
-  --> tests/ui/transmute.rs:105:31
+  --> tests/ui/transmute.rs:104:31
    |
 LL |         let _: *const Usize = core::mem::transmute(my_int());
    |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: transmute from a type (`Usize`) to a pointer to that type (`*mut Usize`)
-  --> tests/ui/transmute.rs:108:29
+  --> tests/ui/transmute.rs:107:29
    |
 LL |         let _: *mut Usize = core::mem::transmute(my_int());
    |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: transmute from a `u8` to a `bool`
-  --> tests/ui/transmute.rs:115:28
+  --> tests/ui/transmute.rs:114:28
    |
 LL |     let _: bool = unsafe { std::mem::transmute(0_u8) };
    |                            ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `0_u8 != 0`
@@ -98,7 +86,7 @@ LL |     let _: bool = unsafe { std::mem::transmute(0_u8) };
    = help: to override `-D warnings` add `#[allow(clippy::transmute_int_to_bool)]`
 
 error: transmute from a `&[u8]` to a `&str`
-  --> tests/ui/transmute.rs:122:28
+  --> tests/ui/transmute.rs:121:28
    |
 LL |     let _: &str = unsafe { std::mem::transmute(B) };
    |                            ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8(B).unwrap()`
@@ -107,16 +95,16 @@ LL |     let _: &str = unsafe { std::mem::transmute(B) };
    = help: to override `-D warnings` add `#[allow(clippy::transmute_bytes_to_str)]`
 
 error: transmute from a `&mut [u8]` to a `&mut str`
-  --> tests/ui/transmute.rs:125:32
+  --> tests/ui/transmute.rs:124:32
    |
 LL |     let _: &mut str = unsafe { std::mem::transmute(mb) };
    |                                ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_mut(mb).unwrap()`
 
 error: transmute from a `&[u8]` to a `&str`
-  --> tests/ui/transmute.rs:128:30
+  --> tests/ui/transmute.rs:127:30
    |
 LL |     const _: &str = unsafe { std::mem::transmute(B) };
    |                              ^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `std::str::from_utf8_unchecked(B)`
 
-error: aborting due to 18 previous errors
+error: aborting due to 16 previous errors
 
diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.fixed b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.fixed
index 61e3ac2fe88..c130575df96 100644
--- a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.fixed
+++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.fixed
@@ -5,7 +5,7 @@
     clippy::missing_transmute_annotations
 )]
 
-unsafe fn _ptr_to_ref<T, U>(p: *const T, m: *mut T, o: *const U, om: *mut U) {
+fn ptr_to_ref<T, U>(p: *const T, m: *mut T, o: *const U, om: *mut U) {
     unsafe {
         let _: &T = &*p;
         //~^ transmute_ptr_to_ref
@@ -37,7 +37,7 @@ unsafe fn _ptr_to_ref<T, U>(p: *const T, m: *mut T, o: *const U, om: *mut U) {
     }
 }
 
-fn _issue1231() {
+fn issue1231() {
     struct Foo<'a, T> {
         bar: &'a T,
     }
@@ -55,7 +55,7 @@ fn _issue1231() {
     //~^ transmute_ptr_to_ref
 }
 
-unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 {
+fn issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 {
     unsafe {
         match 0 {
             0 => &*x.cast::<&u32>(),
@@ -71,7 +71,7 @@ unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'
 }
 
 #[clippy::msrv = "1.38"]
-unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
+fn meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
     unsafe {
         let a = 0u32;
         let a = &a as *const u32;
@@ -89,7 +89,7 @@ unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
 }
 
 #[clippy::msrv = "1.37"]
-unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
+fn under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
     unsafe {
         let a = 0u32;
         let a = &a as *const u32;
@@ -106,4 +106,33 @@ unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
     }
 }
 
+// handle DSTs
+fn issue13357(ptr: *const [i32], s_ptr: *const &str, a_s_ptr: *const [&str]) {
+    unsafe {
+        // different types, without erased regions
+        let _ = &*(ptr as *const [u32]);
+        //~^ transmute_ptr_to_ref
+        let _: &[u32] = &*(ptr as *const [u32]);
+        //~^ transmute_ptr_to_ref
+
+        // different types, with erased regions
+        let _ = &*(a_s_ptr as *const [&[u8]]);
+        //~^ transmute_ptr_to_ref
+        let _: &[&[u8]] = &*(a_s_ptr as *const [&[u8]]);
+        //~^ transmute_ptr_to_ref
+
+        // same type, without erased regions
+        let _ = &*(ptr as *const [i32]);
+        //~^ transmute_ptr_to_ref
+        let _: &[i32] = &*ptr;
+        //~^ transmute_ptr_to_ref
+
+        // same type, with erased regions
+        let _ = &*(a_s_ptr as *const [&str]);
+        //~^ transmute_ptr_to_ref
+        let _: &[&str] = &*(a_s_ptr as *const [&str]);
+        //~^ transmute_ptr_to_ref
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs
index 48e2f527b55..f79d54234a2 100644
--- a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs
+++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs
@@ -5,7 +5,7 @@
     clippy::missing_transmute_annotations
 )]
 
-unsafe fn _ptr_to_ref<T, U>(p: *const T, m: *mut T, o: *const U, om: *mut U) {
+fn ptr_to_ref<T, U>(p: *const T, m: *mut T, o: *const U, om: *mut U) {
     unsafe {
         let _: &T = std::mem::transmute(p);
         //~^ transmute_ptr_to_ref
@@ -37,7 +37,7 @@ unsafe fn _ptr_to_ref<T, U>(p: *const T, m: *mut T, o: *const U, om: *mut U) {
     }
 }
 
-fn _issue1231() {
+fn issue1231() {
     struct Foo<'a, T> {
         bar: &'a T,
     }
@@ -55,7 +55,7 @@ fn _issue1231() {
     //~^ transmute_ptr_to_ref
 }
 
-unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 {
+fn issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 {
     unsafe {
         match 0 {
             0 => std::mem::transmute(x),
@@ -71,7 +71,7 @@ unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'
 }
 
 #[clippy::msrv = "1.38"]
-unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
+fn meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
     unsafe {
         let a = 0u32;
         let a = &a as *const u32;
@@ -89,7 +89,7 @@ unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
 }
 
 #[clippy::msrv = "1.37"]
-unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
+fn under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
     unsafe {
         let a = 0u32;
         let a = &a as *const u32;
@@ -106,4 +106,33 @@ unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
     }
 }
 
+// handle DSTs
+fn issue13357(ptr: *const [i32], s_ptr: *const &str, a_s_ptr: *const [&str]) {
+    unsafe {
+        // different types, without erased regions
+        let _ = core::mem::transmute::<_, &[u32]>(ptr);
+        //~^ transmute_ptr_to_ref
+        let _: &[u32] = core::mem::transmute(ptr);
+        //~^ transmute_ptr_to_ref
+
+        // different types, with erased regions
+        let _ = core::mem::transmute::<_, &[&[u8]]>(a_s_ptr);
+        //~^ transmute_ptr_to_ref
+        let _: &[&[u8]] = core::mem::transmute(a_s_ptr);
+        //~^ transmute_ptr_to_ref
+
+        // same type, without erased regions
+        let _ = core::mem::transmute::<_, &[i32]>(ptr);
+        //~^ transmute_ptr_to_ref
+        let _: &[i32] = core::mem::transmute(ptr);
+        //~^ transmute_ptr_to_ref
+
+        // same type, with erased regions
+        let _ = core::mem::transmute::<_, &[&str]>(a_s_ptr);
+        //~^ transmute_ptr_to_ref
+        let _: &[&str] = core::mem::transmute(a_s_ptr);
+        //~^ transmute_ptr_to_ref
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.stderr b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.stderr
index 7685c345c86..3f404d295fe 100644
--- a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.stderr
+++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.stderr
@@ -43,13 +43,13 @@ error: transmute from a pointer type (`*mut U`) to a reference type (`&T`)
 LL |         let _: &T = std::mem::transmute(om);
    |                     ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(om as *const T)`
 
-error: transmute from a pointer type (`*const i32`) to a reference type (`&_issue1231::Foo<'_, u8>`)
+error: transmute from a pointer type (`*const i32`) to a reference type (`&issue1231::Foo<'_, u8>`)
   --> tests/ui/transmute_ptr_to_ref.rs:46:32
    |
 LL |     let _: &Foo<u8> = unsafe { std::mem::transmute::<_, &Foo<_>>(raw) };
    |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*raw.cast::<Foo<_>>()`
 
-error: transmute from a pointer type (`*const i32`) to a reference type (`&_issue1231::Foo<'_, &u8>`)
+error: transmute from a pointer type (`*const i32`) to a reference type (`&issue1231::Foo<'_, &u8>`)
   --> tests/ui/transmute_ptr_to_ref.rs:49:33
    |
 LL |     let _: &Foo<&u8> = unsafe { std::mem::transmute::<_, &Foo<&_>>(raw) };
@@ -133,5 +133,53 @@ error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32
 LL |             _ => std::mem::transmute::<_, &&'b u32>(x),
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &'b u32)`
 
-error: aborting due to 22 previous errors
+error: transmute from a pointer type (`*const [i32]`) to a reference type (`&[u32]`)
+  --> tests/ui/transmute_ptr_to_ref.rs:113:17
+   |
+LL |         let _ = core::mem::transmute::<_, &[u32]>(ptr);
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(ptr as *const [u32])`
+
+error: transmute from a pointer type (`*const [i32]`) to a reference type (`&[u32]`)
+  --> tests/ui/transmute_ptr_to_ref.rs:115:25
+   |
+LL |         let _: &[u32] = core::mem::transmute(ptr);
+   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(ptr as *const [u32])`
+
+error: transmute from a pointer type (`*const [&str]`) to a reference type (`&[&[u8]]`)
+  --> tests/ui/transmute_ptr_to_ref.rs:119:17
+   |
+LL |         let _ = core::mem::transmute::<_, &[&[u8]]>(a_s_ptr);
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a_s_ptr as *const [&[u8]])`
+
+error: transmute from a pointer type (`*const [&str]`) to a reference type (`&[&[u8]]`)
+  --> tests/ui/transmute_ptr_to_ref.rs:121:27
+   |
+LL |         let _: &[&[u8]] = core::mem::transmute(a_s_ptr);
+   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a_s_ptr as *const [&[u8]])`
+
+error: transmute from a pointer type (`*const [i32]`) to a reference type (`&[i32]`)
+  --> tests/ui/transmute_ptr_to_ref.rs:125:17
+   |
+LL |         let _ = core::mem::transmute::<_, &[i32]>(ptr);
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(ptr as *const [i32])`
+
+error: transmute from a pointer type (`*const [i32]`) to a reference type (`&[i32]`)
+  --> tests/ui/transmute_ptr_to_ref.rs:127:25
+   |
+LL |         let _: &[i32] = core::mem::transmute(ptr);
+   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*ptr`
+
+error: transmute from a pointer type (`*const [&str]`) to a reference type (`&[&str]`)
+  --> tests/ui/transmute_ptr_to_ref.rs:131:17
+   |
+LL |         let _ = core::mem::transmute::<_, &[&str]>(a_s_ptr);
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a_s_ptr as *const [&str])`
+
+error: transmute from a pointer type (`*const [&str]`) to a reference type (`&[&str]`)
+  --> tests/ui/transmute_ptr_to_ref.rs:133:26
+   |
+LL |         let _: &[&str] = core::mem::transmute(a_s_ptr);
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a_s_ptr as *const [&str])`
+
+error: aborting due to 30 previous errors
 
diff --git a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed
index e7ad2a1cbbc..02f67f79e2b 100644
--- a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed
+++ b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed
@@ -13,9 +13,6 @@ fn main() {
     // We should see an error message for each transmute, and no error messages for
     // the casts, since the casts are the recommended fixes.
 
-    // e is an integer and U is *U_0, while U_0: Sized; addr-ptr-cast
-    let _ptr_i32_transmute = unsafe { usize::MAX as *const i32 };
-    //~^ useless_transmute
     let ptr_i32 = usize::MAX as *const i32;
 
     // e has type *T, U is *U_0, and either U_0: Sized ...
diff --git a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs
index 42a81777a82..c5e156405eb 100644
--- a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs
+++ b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs
@@ -13,9 +13,6 @@ fn main() {
     // We should see an error message for each transmute, and no error messages for
     // the casts, since the casts are the recommended fixes.
 
-    // e is an integer and U is *U_0, while U_0: Sized; addr-ptr-cast
-    let _ptr_i32_transmute = unsafe { transmute::<usize, *const i32>(usize::MAX) };
-    //~^ useless_transmute
     let ptr_i32 = usize::MAX as *const i32;
 
     // e has type *T, U is *U_0, and either U_0: Sized ...
diff --git a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr
index 7746f087cc7..f39a64d57eb 100644
--- a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr
+++ b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.stderr
@@ -1,14 +1,5 @@
-error: transmute from an integer to a pointer
-  --> tests/ui/transmutes_expressible_as_ptr_casts.rs:17:39
-   |
-LL |     let _ptr_i32_transmute = unsafe { transmute::<usize, *const i32>(usize::MAX) };
-   |                                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `usize::MAX as *const i32`
-   |
-   = note: `-D clippy::useless-transmute` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::useless_transmute)]`
-
 error: transmute from a pointer to a pointer
-  --> tests/ui/transmutes_expressible_as_ptr_casts.rs:22:38
+  --> tests/ui/transmutes_expressible_as_ptr_casts.rs:19:38
    |
 LL |     let _ptr_i8_transmute = unsafe { transmute::<*const i32, *const i8>(ptr_i32) };
    |                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -22,7 +13,7 @@ LL +     let _ptr_i8_transmute = unsafe { ptr_i32.cast::<i8>() };
    |
 
 error: transmute from a pointer to a pointer
-  --> tests/ui/transmutes_expressible_as_ptr_casts.rs:29:46
+  --> tests/ui/transmutes_expressible_as_ptr_casts.rs:26:46
    |
 LL |     let _ptr_to_unsized_transmute = unsafe { transmute::<*const [i32], *const [u32]>(slice_ptr) };
    |                                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -34,7 +25,7 @@ LL +     let _ptr_to_unsized_transmute = unsafe { slice_ptr as *const [u32] };
    |
 
 error: transmute from `*const i32` to `usize` which could be expressed as a pointer cast instead
-  --> tests/ui/transmutes_expressible_as_ptr_casts.rs:36:50
+  --> tests/ui/transmutes_expressible_as_ptr_casts.rs:33:50
    |
 LL |     let _usize_from_int_ptr_transmute = unsafe { transmute::<*const i32, usize>(ptr_i32) };
    |                                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `ptr_i32 as usize`
@@ -43,40 +34,43 @@ LL |     let _usize_from_int_ptr_transmute = unsafe { transmute::<*const i32, us
    = help: to override `-D warnings` add `#[allow(clippy::transmutes_expressible_as_ptr_casts)]`
 
 error: transmute from a reference to a pointer
-  --> tests/ui/transmutes_expressible_as_ptr_casts.rs:43:41
+  --> tests/ui/transmutes_expressible_as_ptr_casts.rs:40:41
    |
 LL |     let _array_ptr_transmute = unsafe { transmute::<&[i32; 4], *const [i32; 4]>(array_ref) };
    |                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `array_ref as *const [i32; 4]`
+   |
+   = note: `-D clippy::useless-transmute` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::useless_transmute)]`
 
 error: transmute from `fn(usize) -> u8` to `*const usize` which could be expressed as a pointer cast instead
-  --> tests/ui/transmutes_expressible_as_ptr_casts.rs:52:41
+  --> tests/ui/transmutes_expressible_as_ptr_casts.rs:49:41
    |
 LL |     let _usize_ptr_transmute = unsafe { transmute::<fn(usize) -> u8, *const usize>(foo) };
    |                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as *const usize`
 
 error: transmute from `fn(usize) -> u8` to `usize` which could be expressed as a pointer cast instead
-  --> tests/ui/transmutes_expressible_as_ptr_casts.rs:57:49
+  --> tests/ui/transmutes_expressible_as_ptr_casts.rs:54:49
    |
 LL |     let _usize_from_fn_ptr_transmute = unsafe { transmute::<fn(usize) -> u8, usize>(foo) };
    |                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `foo as usize`
 
 error: transmute from `*const u32` to `usize` which could be expressed as a pointer cast instead
-  --> tests/ui/transmutes_expressible_as_ptr_casts.rs:61:36
+  --> tests/ui/transmutes_expressible_as_ptr_casts.rs:58:36
    |
 LL |     let _usize_from_ref = unsafe { transmute::<*const u32, usize>(&1u32) };
    |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&1u32 as *const u32 as usize`
 
 error: transmute from a reference to a pointer
-  --> tests/ui/transmutes_expressible_as_ptr_casts.rs:73:14
+  --> tests/ui/transmutes_expressible_as_ptr_casts.rs:70:14
    |
 LL |     unsafe { transmute::<&[i32; 1], *const u8>(in_param) }
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `in_param as *const [i32; 1] as *const u8`
 
 error: transmute from `fn()` to `*const u8` which could be expressed as a pointer cast instead
-  --> tests/ui/transmutes_expressible_as_ptr_casts.rs:92:28
+  --> tests/ui/transmutes_expressible_as_ptr_casts.rs:89:28
    |
 LL |     let _x: u8 = unsafe { *std::mem::transmute::<fn(), *const u8>(f) };
    |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(f as *const u8)`
 
-error: aborting due to 10 previous errors
+error: aborting due to 9 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unit_cmp.rs b/src/tools/clippy/tests/ui/unit_cmp.rs
index 93f5b87c3d2..0a0fe3a1990 100644
--- a/src/tools/clippy/tests/ui/unit_cmp.rs
+++ b/src/tools/clippy/tests/ui/unit_cmp.rs
@@ -68,3 +68,20 @@ fn main() {
         }
     );
 }
+
+fn issue15559() {
+    fn foo() {}
+    assert_eq!(
+        //~^ unit_cmp
+        {
+            1;
+        },
+        foo()
+    );
+    assert_eq!(foo(), foo());
+    //~^ unit_cmp
+
+    // don't lint on explicitly written unit expr
+    assert_eq!(foo(), ());
+    assert_ne!((), ContainsUnit(()).0);
+}
diff --git a/src/tools/clippy/tests/ui/unit_cmp.stderr b/src/tools/clippy/tests/ui/unit_cmp.stderr
index 8f26749fd86..21aa9dce151 100644
--- a/src/tools/clippy/tests/ui/unit_cmp.stderr
+++ b/src/tools/clippy/tests/ui/unit_cmp.stderr
@@ -71,5 +71,23 @@ LL | |             true;
 LL | |     );
    | |_____^
 
-error: aborting due to 6 previous errors
+error: `assert_eq` of unit values detected. This will always succeed
+  --> tests/ui/unit_cmp.rs:74:5
+   |
+LL | /     assert_eq!(
+LL | |
+LL | |         {
+LL | |             1;
+LL | |         },
+LL | |         foo()
+LL | |     );
+   | |_____^
+
+error: `assert_eq` of unit values detected. This will always succeed
+  --> tests/ui/unit_cmp.rs:81:5
+   |
+LL |     assert_eq!(foo(), foo());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 8 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unnecessary_clone.stderr b/src/tools/clippy/tests/ui/unnecessary_clone.stderr
index a4fdf09cd5d..17518e123d1 100644
--- a/src/tools/clippy/tests/ui/unnecessary_clone.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_clone.stderr
@@ -2,7 +2,7 @@ error: using `.clone()` on a ref-counted pointer
   --> tests/ui/unnecessary_clone.rs:23:5
    |
 LL |     rc.clone();
-   |     ^^^^^^^^^^ help: try: `Rc::<bool>::clone(&rc)`
+   |     ^^^^^^^^^^ help: try: `std::rc::Rc::<bool>::clone(&rc)`
    |
    = note: `-D clippy::clone-on-ref-ptr` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::clone_on_ref_ptr)]`
@@ -11,25 +11,25 @@ error: using `.clone()` on a ref-counted pointer
   --> tests/ui/unnecessary_clone.rs:28:5
    |
 LL |     arc.clone();
-   |     ^^^^^^^^^^^ help: try: `Arc::<bool>::clone(&arc)`
+   |     ^^^^^^^^^^^ help: try: `std::sync::Arc::<bool>::clone(&arc)`
 
 error: using `.clone()` on a ref-counted pointer
   --> tests/ui/unnecessary_clone.rs:33:5
    |
 LL |     rcweak.clone();
-   |     ^^^^^^^^^^^^^^ help: try: `Weak::<bool>::clone(&rcweak)`
+   |     ^^^^^^^^^^^^^^ help: try: `std::rc::Weak::<bool>::clone(&rcweak)`
 
 error: using `.clone()` on a ref-counted pointer
   --> tests/ui/unnecessary_clone.rs:38:5
    |
 LL |     arc_weak.clone();
-   |     ^^^^^^^^^^^^^^^^ help: try: `Weak::<bool>::clone(&arc_weak)`
+   |     ^^^^^^^^^^^^^^^^ help: try: `std::sync::Weak::<bool>::clone(&arc_weak)`
 
 error: using `.clone()` on a ref-counted pointer
   --> tests/ui/unnecessary_clone.rs:44:33
    |
 LL |     let _: Arc<dyn SomeTrait> = x.clone();
-   |                                 ^^^^^^^^^ help: try: `Arc::<SomeImpl>::clone(&x)`
+   |                                 ^^^^^^^^^ help: try: `std::sync::Arc::<SomeImpl>::clone(&x)`
 
 error: using `clone` on type `T` which implements the `Copy` trait
   --> tests/ui/unnecessary_clone.rs:49:5
@@ -56,7 +56,7 @@ error: using `.clone()` on a ref-counted pointer
   --> tests/ui/unnecessary_clone.rs:108:14
    |
 LL |         Some(try_opt!(Some(rc)).clone())
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Rc::<u8>::clone(&try_opt!(Some(rc)))`
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::rc::Rc::<u8>::clone(&try_opt!(Some(rc)))`
 
 error: aborting due to 9 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.fixed b/src/tools/clippy/tests/ui/unnecessary_operation.fixed
index ac9fa4de20a..db5409bc491 100644
--- a/src/tools/clippy/tests/ui/unnecessary_operation.fixed
+++ b/src/tools/clippy/tests/ui/unnecessary_operation.fixed
@@ -78,25 +78,25 @@ fn main() {
     //~^ unnecessary_operation
     get_number();
     //~^ unnecessary_operation
-    5;get_number();
+    5; get_number();
     //~^ unnecessary_operation
     get_number();
     //~^ unnecessary_operation
     get_number();
     //~^ unnecessary_operation
-    5;6;get_number();
+    5; 6; get_number();
     //~^ unnecessary_operation
     get_number();
     //~^ unnecessary_operation
     get_number();
     //~^ unnecessary_operation
-    5;get_number();
+    5; get_number();
     //~^ unnecessary_operation
-    42;get_number();
+    42; get_number();
     //~^ unnecessary_operation
     assert!([42, 55].len() > get_usize());
     //~^ unnecessary_operation
-    42;get_number();
+    42; get_number();
     //~^ unnecessary_operation
     get_number();
     //~^ unnecessary_operation
diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.stderr b/src/tools/clippy/tests/ui/unnecessary_operation.stderr
index 0fda1dfde19..3439ba2e33e 100644
--- a/src/tools/clippy/tests/ui/unnecessary_operation.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_operation.stderr
@@ -35,7 +35,7 @@ error: unnecessary operation
   --> tests/ui/unnecessary_operation.rs:81:5
    |
 LL |     5 + get_number();
-   |     ^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;get_number();`
+   |     ^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5; get_number();`
 
 error: unnecessary operation
   --> tests/ui/unnecessary_operation.rs:83:5
@@ -53,7 +53,7 @@ error: unnecessary operation
   --> tests/ui/unnecessary_operation.rs:87:5
    |
 LL |     (5, 6, get_number());
-   |     ^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;6;get_number();`
+   |     ^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5; 6; get_number();`
 
 error: unnecessary operation
   --> tests/ui/unnecessary_operation.rs:89:5
@@ -71,13 +71,13 @@ error: unnecessary operation
   --> tests/ui/unnecessary_operation.rs:93:5
    |
 LL |     5..get_number();
-   |     ^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5;get_number();`
+   |     ^^^^^^^^^^^^^^^^ help: statement can be reduced to: `5; get_number();`
 
 error: unnecessary operation
   --> tests/ui/unnecessary_operation.rs:95:5
    |
 LL |     [42, get_number()];
-   |     ^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `42;get_number();`
+   |     ^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `42; get_number();`
 
 error: unnecessary operation
   --> tests/ui/unnecessary_operation.rs:97:5
@@ -89,7 +89,7 @@ error: unnecessary operation
   --> tests/ui/unnecessary_operation.rs:99:5
    |
 LL |     (42, get_number()).1;
-   |     ^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `42;get_number();`
+   |     ^^^^^^^^^^^^^^^^^^^^^ help: statement can be reduced to: `42; get_number();`
 
 error: unnecessary operation
   --> tests/ui/unnecessary_operation.rs:101:5
diff --git a/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2021.fixed b/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2021.fixed
index f10d804c8cc..797f1505f49 100644
--- a/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2021.fixed
+++ b/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2021.fixed
@@ -63,3 +63,12 @@ fn issue14100() -> bool {
     // cast into the `bool` function return type.
     if return true {};
 }
+
+fn issue15426(x: u32) {
+    // removing the `;` would turn the stmt into an expr, but attrs aren't allowed on exprs
+    #[rustfmt::skip]
+    match x {
+        0b00 => {}  0b01 => {}
+        0b11 => {}  _    => {}
+    };
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2024.fixed b/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2024.fixed
index 32a3bb9b408..d2609cea000 100644
--- a/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2024.fixed
+++ b/src/tools/clippy/tests/ui/unnecessary_semicolon.edition2024.fixed
@@ -63,3 +63,12 @@ fn issue14100() -> bool {
     // cast into the `bool` function return type.
     if return true {};
 }
+
+fn issue15426(x: u32) {
+    // removing the `;` would turn the stmt into an expr, but attrs aren't allowed on exprs
+    #[rustfmt::skip]
+    match x {
+        0b00 => {}  0b01 => {}
+        0b11 => {}  _    => {}
+    };
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_semicolon.rs b/src/tools/clippy/tests/ui/unnecessary_semicolon.rs
index 91b28218022..55f1ec84cb0 100644
--- a/src/tools/clippy/tests/ui/unnecessary_semicolon.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_semicolon.rs
@@ -63,3 +63,12 @@ fn issue14100() -> bool {
     // cast into the `bool` function return type.
     if return true {};
 }
+
+fn issue15426(x: u32) {
+    // removing the `;` would turn the stmt into an expr, but attrs aren't allowed on exprs
+    #[rustfmt::skip]
+    match x {
+        0b00 => {}  0b01 => {}
+        0b11 => {}  _    => {}
+    };
+}
diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns.fixed b/src/tools/clippy/tests/ui/unnested_or_patterns.fixed
index 2081772d06b..339d4a95084 100644
--- a/src/tools/clippy/tests/ui/unnested_or_patterns.fixed
+++ b/src/tools/clippy/tests/ui/unnested_or_patterns.fixed
@@ -9,6 +9,11 @@
 )]
 #![allow(unreachable_patterns, irrefutable_let_patterns, unused)]
 
+struct S {
+    x: u8,
+    y: u8,
+}
+
 fn main() {
     // Should be ignored by this lint, as nesting requires more characters.
     if let &0 | &2 = &0 {}
@@ -45,10 +50,6 @@ fn main() {
     //~^ unnested_or_patterns
     if let TS(x, ..) | TS(x, 1 | 2) = TS(0, 0) {}
     //~^ unnested_or_patterns
-    struct S {
-        x: u8,
-        y: u8,
-    }
     if let S { x: 0 | 1, y } = (S { x: 0, y: 1 }) {}
     //~^ unnested_or_patterns
     if let S { x: 0, y, .. } | S { y, x: 1 } = (S { x: 0, y: 1 }) {}
@@ -77,3 +78,19 @@ mod issue9952 {
     fn or_in_param((x | x | x): i32) {}
     //~^ unnested_or_patterns
 }
+
+fn issue15219() {
+    struct Foo {
+        x: u8,
+    }
+
+    // the original repro
+    if let Foo { x } | Foo { x } = (Foo { x: 0 }) {}
+
+    // also works with more fields
+    if let S { x, y } | S { x, y } = (S { x: 0, y: 0 }) {}
+
+    // `y` not triggering the lint doesn't stop the `x` from getting flagged
+    if let S { y, x: 0 | 1 } = (S { x: 0, y: 1 }) {}
+    //~^ unnested_or_patterns
+}
diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns.rs b/src/tools/clippy/tests/ui/unnested_or_patterns.rs
index 6bf8fce3661..f5c99183b0c 100644
--- a/src/tools/clippy/tests/ui/unnested_or_patterns.rs
+++ b/src/tools/clippy/tests/ui/unnested_or_patterns.rs
@@ -9,6 +9,11 @@
 )]
 #![allow(unreachable_patterns, irrefutable_let_patterns, unused)]
 
+struct S {
+    x: u8,
+    y: u8,
+}
+
 fn main() {
     // Should be ignored by this lint, as nesting requires more characters.
     if let &0 | &2 = &0 {}
@@ -45,10 +50,6 @@ fn main() {
     //~^ unnested_or_patterns
     if let TS(x, ..) | TS(x, 1) | TS(x, 2) = TS(0, 0) {}
     //~^ unnested_or_patterns
-    struct S {
-        x: u8,
-        y: u8,
-    }
     if let S { x: 0, y } | S { y, x: 1 } = (S { x: 0, y: 1 }) {}
     //~^ unnested_or_patterns
     if let S { x: 0, y, .. } | S { y, x: 1 } = (S { x: 0, y: 1 }) {}
@@ -77,3 +78,19 @@ mod issue9952 {
     fn or_in_param((x | (x | x)): i32) {}
     //~^ unnested_or_patterns
 }
+
+fn issue15219() {
+    struct Foo {
+        x: u8,
+    }
+
+    // the original repro
+    if let Foo { x } | Foo { x } = (Foo { x: 0 }) {}
+
+    // also works with more fields
+    if let S { x, y } | S { x, y } = (S { x: 0, y: 0 }) {}
+
+    // `y` not triggering the lint doesn't stop the `x` from getting flagged
+    if let S { y, x: 0 } | S { y, x: 1 } = (S { x: 0, y: 1 }) {}
+    //~^ unnested_or_patterns
+}
diff --git a/src/tools/clippy/tests/ui/unnested_or_patterns.stderr b/src/tools/clippy/tests/ui/unnested_or_patterns.stderr
index c805dc992b1..d2b617c322c 100644
--- a/src/tools/clippy/tests/ui/unnested_or_patterns.stderr
+++ b/src/tools/clippy/tests/ui/unnested_or_patterns.stderr
@@ -1,5 +1,5 @@
 error: unnested or-patterns
-  --> tests/ui/unnested_or_patterns.rs:16:12
+  --> tests/ui/unnested_or_patterns.rs:21:12
    |
 LL |     if let box 0 | box 2 = Box::new(0) {}
    |            ^^^^^^^^^^^^^
@@ -13,7 +13,7 @@ LL +     if let box (0 | 2) = Box::new(0) {}
    |
 
 error: unnested or-patterns
-  --> tests/ui/unnested_or_patterns.rs:18:12
+  --> tests/ui/unnested_or_patterns.rs:23:12
    |
 LL |     if let box ((0 | 1)) | box (2 | 3) | box 4 = Box::new(0) {}
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -25,7 +25,7 @@ LL +     if let box (0 | 1 | 2 | 3 | 4) = Box::new(0) {}
    |
 
 error: unnested or-patterns
-  --> tests/ui/unnested_or_patterns.rs:21:12
+  --> tests/ui/unnested_or_patterns.rs:26:12
    |
 LL |     if let Some(1) | C0 | Some(2) = None {}
    |            ^^^^^^^^^^^^^^^^^^^^^^
@@ -37,7 +37,7 @@ LL +     if let Some(1 | 2) | C0 = None {}
    |
 
 error: unnested or-patterns
-  --> tests/ui/unnested_or_patterns.rs:23:12
+  --> tests/ui/unnested_or_patterns.rs:28:12
    |
 LL |     if let &mut 0 | &mut 2 = &mut 0 {}
    |            ^^^^^^^^^^^^^^^
@@ -49,7 +49,7 @@ LL +     if let &mut (0 | 2) = &mut 0 {}
    |
 
 error: unnested or-patterns
-  --> tests/ui/unnested_or_patterns.rs:25:12
+  --> tests/ui/unnested_or_patterns.rs:30:12
    |
 LL |     if let x @ 0 | x @ 2 = 0 {}
    |            ^^^^^^^^^^^^^
@@ -61,7 +61,7 @@ LL +     if let x @ (0 | 2) = 0 {}
    |
 
 error: unnested or-patterns
-  --> tests/ui/unnested_or_patterns.rs:27:12
+  --> tests/ui/unnested_or_patterns.rs:32:12
    |
 LL |     if let (0, 1) | (0, 2) | (0, 3) = (0, 0) {}
    |            ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -73,7 +73,7 @@ LL +     if let (0, 1 | 2 | 3) = (0, 0) {}
    |
 
 error: unnested or-patterns
-  --> tests/ui/unnested_or_patterns.rs:29:12
+  --> tests/ui/unnested_or_patterns.rs:34:12
    |
 LL |     if let (1, 0) | (2, 0) | (3, 0) = (0, 0) {}
    |            ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -85,7 +85,7 @@ LL +     if let (1 | 2 | 3, 0) = (0, 0) {}
    |
 
 error: unnested or-patterns
-  --> tests/ui/unnested_or_patterns.rs:31:12
+  --> tests/ui/unnested_or_patterns.rs:36:12
    |
 LL |     if let (x, ..) | (x, 1) | (x, 2) = (0, 1) {}
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -97,7 +97,7 @@ LL +     if let (x, ..) | (x, 1 | 2) = (0, 1) {}
    |
 
 error: unnested or-patterns
-  --> tests/ui/unnested_or_patterns.rs:33:12
+  --> tests/ui/unnested_or_patterns.rs:38:12
    |
 LL |     if let [0] | [1] = [0] {}
    |            ^^^^^^^^^
@@ -109,7 +109,7 @@ LL +     if let [0 | 1] = [0] {}
    |
 
 error: unnested or-patterns
-  --> tests/ui/unnested_or_patterns.rs:35:12
+  --> tests/ui/unnested_or_patterns.rs:40:12
    |
 LL |     if let [x, 0] | [x, 1] = [0, 1] {}
    |            ^^^^^^^^^^^^^^^
@@ -121,7 +121,7 @@ LL +     if let [x, 0 | 1] = [0, 1] {}
    |
 
 error: unnested or-patterns
-  --> tests/ui/unnested_or_patterns.rs:37:12
+  --> tests/ui/unnested_or_patterns.rs:42:12
    |
 LL |     if let [x, 0] | [x, 1] | [x, 2] = [0, 1] {}
    |            ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -133,7 +133,7 @@ LL +     if let [x, 0 | 1 | 2] = [0, 1] {}
    |
 
 error: unnested or-patterns
-  --> tests/ui/unnested_or_patterns.rs:39:12
+  --> tests/ui/unnested_or_patterns.rs:44:12
    |
 LL |     if let [x, ..] | [x, 1] | [x, 2] = [0, 1] {}
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -145,7 +145,7 @@ LL +     if let [x, ..] | [x, 1 | 2] = [0, 1] {}
    |
 
 error: unnested or-patterns
-  --> tests/ui/unnested_or_patterns.rs:42:12
+  --> tests/ui/unnested_or_patterns.rs:47:12
    |
 LL |     if let TS(0, x) | TS(1, x) = TS(0, 0) {}
    |            ^^^^^^^^^^^^^^^^^^^
@@ -157,7 +157,7 @@ LL +     if let TS(0 | 1, x) = TS(0, 0) {}
    |
 
 error: unnested or-patterns
-  --> tests/ui/unnested_or_patterns.rs:44:12
+  --> tests/ui/unnested_or_patterns.rs:49:12
    |
 LL |     if let TS(1, 0) | TS(2, 0) | TS(3, 0) = TS(0, 0) {}
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -169,7 +169,7 @@ LL +     if let TS(1 | 2 | 3, 0) = TS(0, 0) {}
    |
 
 error: unnested or-patterns
-  --> tests/ui/unnested_or_patterns.rs:46:12
+  --> tests/ui/unnested_or_patterns.rs:51:12
    |
 LL |     if let TS(x, ..) | TS(x, 1) | TS(x, 2) = TS(0, 0) {}
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -181,7 +181,7 @@ LL +     if let TS(x, ..) | TS(x, 1 | 2) = TS(0, 0) {}
    |
 
 error: unnested or-patterns
-  --> tests/ui/unnested_or_patterns.rs:52:12
+  --> tests/ui/unnested_or_patterns.rs:53:12
    |
 LL |     if let S { x: 0, y } | S { y, x: 1 } = (S { x: 0, y: 1 }) {}
    |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -193,7 +193,7 @@ LL +     if let S { x: 0 | 1, y } = (S { x: 0, y: 1 }) {}
    |
 
 error: unnested or-patterns
-  --> tests/ui/unnested_or_patterns.rs:64:12
+  --> tests/ui/unnested_or_patterns.rs:65:12
    |
 LL |     if let [1] | [53] = [0] {}
    |            ^^^^^^^^^^
@@ -205,7 +205,7 @@ LL +     if let [1 | 53] = [0] {}
    |
 
 error: unnested or-patterns
-  --> tests/ui/unnested_or_patterns.rs:70:13
+  --> tests/ui/unnested_or_patterns.rs:71:13
    |
 LL |         let (0 | (1 | _)) = 0;
    |             ^^^^^^^^^^^^^
@@ -217,7 +217,7 @@ LL +         let (0 | 1 | _) = 0;
    |
 
 error: unnested or-patterns
-  --> tests/ui/unnested_or_patterns.rs:73:16
+  --> tests/ui/unnested_or_patterns.rs:74:16
    |
 LL |         if let (0 | (1 | _)) = 0 {}
    |                ^^^^^^^^^^^^^
@@ -229,7 +229,7 @@ LL +         if let (0 | 1 | _) = 0 {}
    |
 
 error: unnested or-patterns
-  --> tests/ui/unnested_or_patterns.rs:77:20
+  --> tests/ui/unnested_or_patterns.rs:78:20
    |
 LL |     fn or_in_param((x | (x | x)): i32) {}
    |                    ^^^^^^^^^^^^^
@@ -240,5 +240,17 @@ LL -     fn or_in_param((x | (x | x)): i32) {}
 LL +     fn or_in_param((x | x | x): i32) {}
    |
 
-error: aborting due to 20 previous errors
+error: unnested or-patterns
+  --> tests/ui/unnested_or_patterns.rs:94:12
+   |
+LL |     if let S { y, x: 0 } | S { y, x: 1 } = (S { x: 0, y: 1 }) {}
+   |            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: nest the patterns
+   |
+LL -     if let S { y, x: 0 } | S { y, x: 1 } = (S { x: 0, y: 1 }) {}
+LL +     if let S { y, x: 0 | 1 } = (S { x: 0, y: 1 }) {}
+   |
+
+error: aborting due to 21 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unused_unit.edition2021.fixed b/src/tools/clippy/tests/ui/unused_unit.edition2021.fixed
index def8ef86e3c..8e12bd2c8c7 100644
--- a/src/tools/clippy/tests/ui/unused_unit.edition2021.fixed
+++ b/src/tools/clippy/tests/ui/unused_unit.edition2021.fixed
@@ -127,14 +127,10 @@ mod issue14577 {
     trait Unit {}
     impl Unit for () {}
 
-    fn run<R: Unit>(f: impl FnOnce() -> R) {
-        f();
-    }
-
     #[allow(dependency_on_unit_never_type_fallback)]
     fn bar() {
-        run(|| { todo!() }); 
         //~[edition2021]^ unused_unit
+        panic!()
     }
 
     struct UnitStruct;
@@ -150,3 +146,24 @@ mod pr14962 {
     type UnusedParensButNoUnit = Box<dyn (Fn())>;
 }
 
+
+mod issue15035 {
+
+    trait Convert<T> {
+        fn from(value: T) -> Self;
+    }
+
+    impl Convert<u64> for () {
+        fn from(_value: u64) -> Self {}
+    }
+
+    fn handle<T: Convert<u64>>(value: u64) -> T {
+        Convert::from(value)
+    }
+
+    pub fn f() -> Option<bool> {
+        let result: Result<bool, u64> = Err(42);
+        // the `-> ()` is required for the inference of `handle`'s return type
+        result.map_err(|err| -> () { handle(err) }).ok()
+    }
+}
diff --git a/src/tools/clippy/tests/ui/unused_unit.edition2021.stderr b/src/tools/clippy/tests/ui/unused_unit.edition2021.stderr
index 13cc20d4d7a..9ad3c2df915 100644
--- a/src/tools/clippy/tests/ui/unused_unit.edition2021.stderr
+++ b/src/tools/clippy/tests/ui/unused_unit.edition2021.stderr
@@ -119,10 +119,10 @@ LL | fn test3()-> (){}
    |           ^^^^^ help: remove the `-> ()`
 
 error: unneeded unit return type
-  --> tests/ui/unused_unit.rs:136:15
+  --> tests/ui/unused_unit.rs:131:13
    |
-LL |         run(|| -> () { todo!() }); 
-   |               ^^^^^^ help: remove the `-> ()`
+LL |     fn bar() -> () {
+   |             ^^^^^^ help: remove the `-> ()`
 
 error: aborting due to 20 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unused_unit.edition2024.fixed b/src/tools/clippy/tests/ui/unused_unit.edition2024.fixed
index f908b958b19..688d2fe9afa 100644
--- a/src/tools/clippy/tests/ui/unused_unit.edition2024.fixed
+++ b/src/tools/clippy/tests/ui/unused_unit.edition2024.fixed
@@ -127,14 +127,10 @@ mod issue14577 {
     trait Unit {}
     impl Unit for () {}
 
-    fn run<R: Unit>(f: impl FnOnce() -> R) {
-        f();
-    }
-
     #[allow(dependency_on_unit_never_type_fallback)]
-    fn bar() {
-        run(|| -> () { todo!() }); 
+    fn bar() -> () {
         //~[edition2021]^ unused_unit
+        panic!()
     }
 
     struct UnitStruct;
@@ -150,3 +146,24 @@ mod pr14962 {
     type UnusedParensButNoUnit = Box<dyn (Fn())>;
 }
 
+
+mod issue15035 {
+
+    trait Convert<T> {
+        fn from(value: T) -> Self;
+    }
+
+    impl Convert<u64> for () {
+        fn from(_value: u64) -> Self {}
+    }
+
+    fn handle<T: Convert<u64>>(value: u64) -> T {
+        Convert::from(value)
+    }
+
+    pub fn f() -> Option<bool> {
+        let result: Result<bool, u64> = Err(42);
+        // the `-> ()` is required for the inference of `handle`'s return type
+        result.map_err(|err| -> () { handle(err) }).ok()
+    }
+}
diff --git a/src/tools/clippy/tests/ui/unused_unit.rs b/src/tools/clippy/tests/ui/unused_unit.rs
index 7298ec40cc2..31e980f2655 100644
--- a/src/tools/clippy/tests/ui/unused_unit.rs
+++ b/src/tools/clippy/tests/ui/unused_unit.rs
@@ -127,14 +127,10 @@ mod issue14577 {
     trait Unit {}
     impl Unit for () {}
 
-    fn run<R: Unit>(f: impl FnOnce() -> R) {
-        f();
-    }
-
     #[allow(dependency_on_unit_never_type_fallback)]
-    fn bar() {
-        run(|| -> () { todo!() }); 
+    fn bar() -> () {
         //~[edition2021]^ unused_unit
+        panic!()
     }
 
     struct UnitStruct;
@@ -150,3 +146,24 @@ mod pr14962 {
     type UnusedParensButNoUnit = Box<dyn (Fn())>;
 }
 
+
+mod issue15035 {
+
+    trait Convert<T> {
+        fn from(value: T) -> Self;
+    }
+
+    impl Convert<u64> for () {
+        fn from(_value: u64) -> Self {}
+    }
+
+    fn handle<T: Convert<u64>>(value: u64) -> T {
+        Convert::from(value)
+    }
+
+    pub fn f() -> Option<bool> {
+        let result: Result<bool, u64> = Err(42);
+        // the `-> ()` is required for the inference of `handle`'s return type
+        result.map_err(|err| -> () { handle(err) }).ok()
+    }
+}
diff --git a/src/tools/clippy/tests/ui/unwrap_in_result.rs b/src/tools/clippy/tests/ui/unwrap_in_result.rs
index 4e872c67b42..70c28fe54f3 100644
--- a/src/tools/clippy/tests/ui/unwrap_in_result.rs
+++ b/src/tools/clippy/tests/ui/unwrap_in_result.rs
@@ -1,4 +1,5 @@
 #![warn(clippy::unwrap_in_result)]
+#![allow(clippy::ok_expect)]
 
 struct A;
 
@@ -20,10 +21,9 @@ impl A {
 
     // should be detected
     fn bad_divisible_by_3(i_str: String) -> Result<bool, String> {
-        //~^ unwrap_in_result
-
         // checks whether a string represents a number divisible by 3
         let i = i_str.parse::<i32>().unwrap();
+        //~^ unwrap_in_result
         if i % 3 == 0 {
             Ok(true)
         } else {
@@ -32,9 +32,8 @@ impl A {
     }
 
     fn example_option_expect(i_str: String) -> Option<bool> {
+        let i = i_str.parse::<i32>().ok().expect("not a number");
         //~^ unwrap_in_result
-
-        let i = i_str.parse::<i32>().expect("not a number");
         if i % 3 == 0 {
             return Some(true);
         }
@@ -42,13 +41,66 @@ impl A {
     }
 
     fn in_closure(a: Option<bool>) -> Option<bool> {
-        //~^ unwrap_in_result
+        // No lint inside a closure
         let c = || a.unwrap();
-        Some(c())
+
+        // But lint outside
+        let a = c().then_some(true);
+        let _ = a.unwrap();
+        //~^ unwrap_in_result
+
+        None
     }
+
+    const fn in_const_inside_fn() -> bool {
+        const A: bool = {
+            const fn inner(b: Option<bool>) -> Option<bool> {
+                Some(b.unwrap())
+                //~^ unwrap_in_result
+            }
+
+            // No lint inside `const`
+            inner(Some(true)).unwrap()
+        };
+        A
+    }
+
+    fn in_static_inside_fn() -> bool {
+        static A: bool = {
+            const fn inner(b: Option<bool>) -> Option<bool> {
+                Some(b.unwrap())
+                //~^ unwrap_in_result
+            }
+
+            // No lint inside `static`
+            inner(Some(true)).unwrap()
+        };
+        A
+    }
+}
+
+macro_rules! mac {
+    () => {
+        Option::unwrap(Some(3))
+    };
+}
+
+fn type_relative_unwrap() -> Option<()> {
+    _ = Option::unwrap(Some(3));
+    //~^ unwrap_in_result
+
+    // Do not lint macro output
+    _ = mac!();
+
+    None
 }
 
-fn main() {
-    A::bad_divisible_by_3("3".to_string());
-    A::good_divisible_by_3("3".to_string());
+fn main() -> Result<(), ()> {
+    A::bad_divisible_by_3("3".to_string()).unwrap();
+    //~^ unwrap_in_result
+    A::good_divisible_by_3("3".to_string()).unwrap();
+    //~^ unwrap_in_result
+    Result::unwrap(A::good_divisible_by_3("3".to_string()));
+    //~^ unwrap_in_result
+    Ok(())
 }
diff --git a/src/tools/clippy/tests/ui/unwrap_in_result.stderr b/src/tools/clippy/tests/ui/unwrap_in_result.stderr
index 5e3eab813e0..804b44246dc 100644
--- a/src/tools/clippy/tests/ui/unwrap_in_result.stderr
+++ b/src/tools/clippy/tests/ui/unwrap_in_result.stderr
@@ -1,55 +1,107 @@
-error: used unwrap or expect in a function that returns result or option
-  --> tests/ui/unwrap_in_result.rs:22:5
-   |
-LL | /     fn bad_divisible_by_3(i_str: String) -> Result<bool, String> {
-...  |
-LL | |     }
-   | |_____^
-   |
-   = help: unwrap and expect should not be used in a function that returns result or option
-note: potential non-recoverable error(s)
-  --> tests/ui/unwrap_in_result.rs:26:17
+error: `unwrap` used in a function that returns a `Result`
+  --> tests/ui/unwrap_in_result.rs:25:17
    |
 LL |         let i = i_str.parse::<i32>().unwrap();
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: in this function signature
+  --> tests/ui/unwrap_in_result.rs:23:45
+   |
+LL |     fn bad_divisible_by_3(i_str: String) -> Result<bool, String> {
+   |                                             ^^^^^^^^^^^^^^^^^^^^
+   = help: consider using the `?` operator or calling the `.map_err()` method
    = note: `-D clippy::unwrap-in-result` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::unwrap_in_result)]`
 
-error: used unwrap or expect in a function that returns result or option
-  --> tests/ui/unwrap_in_result.rs:34:5
+error: `expect` used in a function that returns an `Option`
+  --> tests/ui/unwrap_in_result.rs:35:17
+   |
+LL |         let i = i_str.parse::<i32>().ok().expect("not a number");
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: in this function signature
+  --> tests/ui/unwrap_in_result.rs:34:48
+   |
+LL |     fn example_option_expect(i_str: String) -> Option<bool> {
+   |                                                ^^^^^^^^^^^^
+   = help: consider using the `?` operator
+
+error: `unwrap` used in a function that returns an `Option`
+  --> tests/ui/unwrap_in_result.rs:49:17
+   |
+LL |         let _ = a.unwrap();
+   |                 ^^^^^^^^^^
+   |
+note: in this function signature
+  --> tests/ui/unwrap_in_result.rs:43:39
+   |
+LL |     fn in_closure(a: Option<bool>) -> Option<bool> {
+   |                                       ^^^^^^^^^^^^
+   = help: consider using the `?` operator
+
+error: `unwrap` used in a function that returns an `Option`
+  --> tests/ui/unwrap_in_result.rs:58:22
    |
-LL | /     fn example_option_expect(i_str: String) -> Option<bool> {
-LL | |
-LL | |
-LL | |         let i = i_str.parse::<i32>().expect("not a number");
-...  |
-LL | |         None
-LL | |     }
-   | |_____^
+LL |                 Some(b.unwrap())
+   |                      ^^^^^^^^^^
    |
-   = help: unwrap and expect should not be used in a function that returns result or option
-note: potential non-recoverable error(s)
-  --> tests/ui/unwrap_in_result.rs:37:17
+note: in this function signature
+  --> tests/ui/unwrap_in_result.rs:57:48
    |
-LL |         let i = i_str.parse::<i32>().expect("not a number");
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |             const fn inner(b: Option<bool>) -> Option<bool> {
+   |                                                ^^^^^^^^^^^^
+   = help: consider using the `?` operator
 
-error: used unwrap or expect in a function that returns result or option
-  --> tests/ui/unwrap_in_result.rs:44:5
+error: `unwrap` used in a function that returns an `Option`
+  --> tests/ui/unwrap_in_result.rs:71:22
    |
-LL | /     fn in_closure(a: Option<bool>) -> Option<bool> {
-LL | |
-LL | |         let c = || a.unwrap();
-LL | |         Some(c())
-LL | |     }
-   | |_____^
+LL |                 Some(b.unwrap())
+   |                      ^^^^^^^^^^
    |
-   = help: unwrap and expect should not be used in a function that returns result or option
-note: potential non-recoverable error(s)
-  --> tests/ui/unwrap_in_result.rs:46:20
+note: in this function signature
+  --> tests/ui/unwrap_in_result.rs:70:48
+   |
+LL |             const fn inner(b: Option<bool>) -> Option<bool> {
+   |                                                ^^^^^^^^^^^^
+   = help: consider using the `?` operator
+
+error: `unwrap` used in a function that returns an `Option`
+  --> tests/ui/unwrap_in_result.rs:89:9
+   |
+LL |     _ = Option::unwrap(Some(3));
+   |         ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: in this function signature
+  --> tests/ui/unwrap_in_result.rs:88:30
+   |
+LL | fn type_relative_unwrap() -> Option<()> {
+   |                              ^^^^^^^^^^
+   = help: consider using the `?` operator
+
+error: `unwrap` used in a function that returns a `Result`
+  --> tests/ui/unwrap_in_result.rs:99:5
+   |
+LL |     A::bad_divisible_by_3("3".to_string()).unwrap();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: in this function signature
+  --> tests/ui/unwrap_in_result.rs:98:14
+   |
+LL | fn main() -> Result<(), ()> {
+   |              ^^^^^^^^^^^^^^
+   = help: consider using the `?` operator or calling the `.map_err()` method
+
+error: `unwrap` used in a function that returns a `Result`
+  --> tests/ui/unwrap_in_result.rs:101:5
+   |
+LL |     A::good_divisible_by_3("3".to_string()).unwrap();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: `unwrap` used in a function that returns a `Result`
+  --> tests/ui/unwrap_in_result.rs:103:5
    |
-LL |         let c = || a.unwrap();
-   |                    ^^^^^^^^^^
+LL |     Result::unwrap(A::good_divisible_by_3("3".to_string()));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 3 previous errors
+error: aborting due to 9 previous errors
 
diff --git a/src/tools/clippy/tests/ui/use_self.fixed b/src/tools/clippy/tests/ui/use_self.fixed
index cccb6bffabb..075e31d202b 100644
--- a/src/tools/clippy/tests/ui/use_self.fixed
+++ b/src/tools/clippy/tests/ui/use_self.fixed
@@ -530,8 +530,8 @@ mod issue7206 {
 
     impl<'a> S2<S<'a>> {
         fn new_again() -> Self {
-            Self::new()
-            //~^ use_self
+            S2::new()
+            // FIXME: ^Broken by PR #15611
         }
     }
 }
@@ -755,3 +755,17 @@ mod crash_check_13128 {
         }
     }
 }
+
+mod issue_13277 {
+    trait Foo {
+        type Item<'foo>;
+    }
+    struct Bar<'b> {
+        content: &'b str,
+    }
+    impl<'b> Foo for Option<Bar<'b>> {
+        // when checking whether `Option<Bar<'foo>>` has a lifetime, check not only the outer
+        // `Option<T>`, but also the inner `Bar<'foo>`
+        type Item<'foo> = Option<Bar<'foo>>;
+    }
+}
diff --git a/src/tools/clippy/tests/ui/use_self.rs b/src/tools/clippy/tests/ui/use_self.rs
index 09288677aa7..6fbba0bbc55 100644
--- a/src/tools/clippy/tests/ui/use_self.rs
+++ b/src/tools/clippy/tests/ui/use_self.rs
@@ -531,7 +531,7 @@ mod issue7206 {
     impl<'a> S2<S<'a>> {
         fn new_again() -> Self {
             S2::new()
-            //~^ use_self
+            // FIXME: ^Broken by PR #15611
         }
     }
 }
@@ -755,3 +755,17 @@ mod crash_check_13128 {
         }
     }
 }
+
+mod issue_13277 {
+    trait Foo {
+        type Item<'foo>;
+    }
+    struct Bar<'b> {
+        content: &'b str,
+    }
+    impl<'b> Foo for Option<Bar<'b>> {
+        // when checking whether `Option<Bar<'foo>>` has a lifetime, check not only the outer
+        // `Option<T>`, but also the inner `Bar<'foo>`
+        type Item<'foo> = Option<Bar<'foo>>;
+    }
+}
diff --git a/src/tools/clippy/tests/ui/use_self.stderr b/src/tools/clippy/tests/ui/use_self.stderr
index 781327696ac..5f65c53ea25 100644
--- a/src/tools/clippy/tests/ui/use_self.stderr
+++ b/src/tools/clippy/tests/ui/use_self.stderr
@@ -170,12 +170,6 @@ LL |             A::new::<submod::B>(submod::B {})
    |             ^ help: use the applicable keyword: `Self`
 
 error: unnecessary structure name repetition
-  --> tests/ui/use_self.rs:533:13
-   |
-LL |             S2::new()
-   |             ^^ help: use the applicable keyword: `Self`
-
-error: unnecessary structure name repetition
   --> tests/ui/use_self.rs:571:17
    |
 LL |                 Foo::Bar => unimplemented!(),
@@ -259,5 +253,5 @@ error: unnecessary structure name repetition
 LL |                 E::A => {},
    |                 ^ help: use the applicable keyword: `Self`
 
-error: aborting due to 43 previous errors
+error: aborting due to 42 previous errors
 
diff --git a/src/tools/clippy/tests/ui/useless_attribute.fixed b/src/tools/clippy/tests/ui/useless_attribute.fixed
index be4fb55ddfb..15070dd9c2c 100644
--- a/src/tools/clippy/tests/ui/useless_attribute.fixed
+++ b/src/tools/clippy/tests/ui/useless_attribute.fixed
@@ -158,3 +158,13 @@ pub mod redundant_imports_issue {
 
     empty!();
 }
+
+pub mod issue15636 {
+    pub mod f {
+        #[deprecated(since = "TBD")]
+        pub mod deprec {}
+    }
+
+    #[allow(deprecated_in_future)]
+    pub use f::deprec;
+}
diff --git a/src/tools/clippy/tests/ui/useless_attribute.rs b/src/tools/clippy/tests/ui/useless_attribute.rs
index 5a1bcf97a5b..3f530de7fd8 100644
--- a/src/tools/clippy/tests/ui/useless_attribute.rs
+++ b/src/tools/clippy/tests/ui/useless_attribute.rs
@@ -158,3 +158,13 @@ pub mod redundant_imports_issue {
 
     empty!();
 }
+
+pub mod issue15636 {
+    pub mod f {
+        #[deprecated(since = "TBD")]
+        pub mod deprec {}
+    }
+
+    #[allow(deprecated_in_future)]
+    pub use f::deprec;
+}
diff --git a/src/tools/clippy/tests/ui/vec.fixed b/src/tools/clippy/tests/ui/vec.fixed
index f360a8afadf..55742459c92 100644
--- a/src/tools/clippy/tests/ui/vec.fixed
+++ b/src/tools/clippy/tests/ui/vec.fixed
@@ -242,3 +242,12 @@ fn issue_12101() {
     for a in &[1, 2] {}
     //~^ useless_vec
 }
+
+fn issue_14531() {
+    // The lint used to suggest using an array rather than a reference to a slice.
+
+    fn requires_ref_slice(v: &[()]) {}
+    let v = &[];
+    //~^ useless_vec
+    requires_ref_slice(v);
+}
diff --git a/src/tools/clippy/tests/ui/vec.rs b/src/tools/clippy/tests/ui/vec.rs
index a779d33557c..fbf7131323c 100644
--- a/src/tools/clippy/tests/ui/vec.rs
+++ b/src/tools/clippy/tests/ui/vec.rs
@@ -242,3 +242,12 @@ fn issue_12101() {
     for a in &(vec![1, 2]) {}
     //~^ useless_vec
 }
+
+fn issue_14531() {
+    // The lint used to suggest using an array rather than a reference to a slice.
+
+    fn requires_ref_slice(v: &[()]) {}
+    let v = &vec![];
+    //~^ useless_vec
+    requires_ref_slice(v);
+}
diff --git a/src/tools/clippy/tests/ui/vec.stderr b/src/tools/clippy/tests/ui/vec.stderr
index 806d6617200..d16c8a8944a 100644
--- a/src/tools/clippy/tests/ui/vec.stderr
+++ b/src/tools/clippy/tests/ui/vec.stderr
@@ -127,5 +127,11 @@ error: useless use of `vec!`
 LL |     for a in &(vec![1, 2]) {}
    |              ^^^^^^^^^^^^^ help: you can use a slice directly: `&[1, 2]`
 
-error: aborting due to 21 previous errors
+error: useless use of `vec!`
+  --> tests/ui/vec.rs:250:13
+   |
+LL |     let v = &vec![];
+   |             ^^^^^^^ help: you can use a slice directly: `&[]`
+
+error: aborting due to 22 previous errors
 
diff --git a/src/tools/clippy/tests/ui/zero_sized_hashmap_values.rs b/src/tools/clippy/tests/ui/zero_sized_hashmap_values.rs
index dcbfd16843d..ee2fd19b5ee 100644
--- a/src/tools/clippy/tests/ui/zero_sized_hashmap_values.rs
+++ b/src/tools/clippy/tests/ui/zero_sized_hashmap_values.rs
@@ -92,6 +92,14 @@ fn issue14822() {
     //~^ zero_sized_map_values
 }
 
+fn issue15429() {
+    struct E<'a>(&'a [E<'a>]);
+
+    // The assertion error happens when the type being evaluated has escaping bound vars
+    // as it cannot be wrapped in a dummy binder during size computation.
+    type F = dyn for<'a> FnOnce(HashMap<u32, E<'a>>) -> u32;
+}
+
 fn main() {
     let _: HashMap<String, ()> = HashMap::new();
     //~^ zero_sized_map_values
diff --git a/src/tools/clippy/tests/ui/zero_sized_hashmap_values.stderr b/src/tools/clippy/tests/ui/zero_sized_hashmap_values.stderr
index d29491fa05c..52ffef280c1 100644
--- a/src/tools/clippy/tests/ui/zero_sized_hashmap_values.stderr
+++ b/src/tools/clippy/tests/ui/zero_sized_hashmap_values.stderr
@@ -89,7 +89,7 @@ LL |     type D = HashMap<u32, S<E>>;
    = help: consider using a set instead
 
 error: map with zero-sized value type
-  --> tests/ui/zero_sized_hashmap_values.rs:96:34
+  --> tests/ui/zero_sized_hashmap_values.rs:104:34
    |
 LL |     let _: HashMap<String, ()> = HashMap::new();
    |                                  ^^^^^^^
@@ -97,7 +97,7 @@ LL |     let _: HashMap<String, ()> = HashMap::new();
    = help: consider using a set instead
 
 error: map with zero-sized value type
-  --> tests/ui/zero_sized_hashmap_values.rs:96:12
+  --> tests/ui/zero_sized_hashmap_values.rs:104:12
    |
 LL |     let _: HashMap<String, ()> = HashMap::new();
    |            ^^^^^^^^^^^^^^^^^^^
@@ -105,7 +105,7 @@ LL |     let _: HashMap<String, ()> = HashMap::new();
    = help: consider using a set instead
 
 error: map with zero-sized value type
-  --> tests/ui/zero_sized_hashmap_values.rs:102:12
+  --> tests/ui/zero_sized_hashmap_values.rs:110:12
    |
 LL |     let _: HashMap<_, _> = std::iter::empty::<(String, ())>().collect();
    |            ^^^^^^^^^^^^^
diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml
index 805baf2af6d..7b19f8658c0 100644
--- a/src/tools/clippy/triagebot.toml
+++ b/src/tools/clippy/triagebot.toml
@@ -49,11 +49,18 @@ new_pr = true
 # These labels are set when there are unresolved concerns, removed otherwise
 labels = ["S-waiting-on-concerns"]
 
+# Show differences when a PR is rebased
+[range-diff]
+
+# Amend a review to include a link to what was changed since the review
+[review-changes-since]
+
 [assign]
 contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md"
 users_on_vacation = [
     "matthiaskrgr",
     "Manishearth",
+    "flip1995",
 ]
 
 [assign.owners]
diff --git a/src/tools/compiletest/Cargo.toml b/src/tools/compiletest/Cargo.toml
index cdada5a2230..6597c3c70f6 100644
--- a/src/tools/compiletest/Cargo.toml
+++ b/src/tools/compiletest/Cargo.toml
@@ -12,7 +12,7 @@ path = "src/bin/main.rs"
 
 [dependencies]
 # tidy-alphabetical-start
-anstyle-svg = "0.1.3"
+anstyle-svg = "0.1.11"
 build_helper = { path = "../../build_helper" }
 camino = "1"
 colored = "2"
diff --git a/src/tools/compiletest/src/bin/main.rs b/src/tools/compiletest/src/bin/main.rs
index 1f777e71cf9..8fac6ccdc58 100644
--- a/src/tools/compiletest/src/bin/main.rs
+++ b/src/tools/compiletest/src/bin/main.rs
@@ -2,7 +2,7 @@ use std::env;
 use std::io::IsTerminal;
 use std::sync::Arc;
 
-use compiletest::{early_config_check, log_config, parse_config, run_tests};
+use compiletest::{early_config_check, parse_config, run_tests};
 
 fn main() {
     tracing_subscriber::fmt::init();
@@ -19,6 +19,5 @@ fn main() {
 
     early_config_check(&config);
 
-    log_config(&config);
     run_tests(config);
 }
diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs
index 2d49b1a7097..6da102b1b5f 100644
--- a/src/tools/compiletest/src/common.rs
+++ b/src/tools/compiletest/src/common.rs
@@ -6,9 +6,8 @@ use std::sync::OnceLock;
 use build_helper::git::GitConfig;
 use camino::{Utf8Path, Utf8PathBuf};
 use semver::Version;
-use serde::de::{Deserialize, Deserializer, Error as _};
 
-use crate::executor::{ColorConfig, OutputFormat};
+use crate::executor::ColorConfig;
 use crate::fatal;
 use crate::util::{Utf8PathBufExt, add_dylib_path, string_enum};
 
@@ -68,6 +67,7 @@ string_enum! {
         MirOpt => "mir-opt",
         Pretty => "pretty",
         RunMake => "run-make",
+        RunMakeCargo => "run-make-cargo",
         Rustdoc => "rustdoc",
         RustdocGui => "rustdoc-gui",
         RustdocJs => "rustdoc-js",
@@ -203,6 +203,10 @@ impl CodegenBackend {
             Self::Llvm => "llvm",
         }
     }
+
+    pub fn is_llvm(self) -> bool {
+        matches!(self, Self::Llvm)
+    }
 }
 
 /// Configuration for `compiletest` *per invocation*.
@@ -270,9 +274,6 @@ pub struct Config {
     /// between e.g. beta `cargo` vs in-tree `cargo`.
     ///
     /// FIXME: maybe rename this to reflect that this is a *staged* host cargo.
-    ///
-    /// FIXME(#134109): split `run-make` into two test suites, a test suite *with* staged cargo, and
-    /// another test suite *without*.
     pub cargo_path: Option<Utf8PathBuf>,
 
     /// Path to the stage 0 `rustc` used to build `run-make` recipes. This must not be confused with
@@ -565,13 +566,6 @@ pub struct Config {
     /// FIXME: this is *way* too coarse; the user can't select *which* info to verbosely dump.
     pub verbose: bool,
 
-    /// (Useless) Adjust libtest output format.
-    ///
-    /// FIXME: the hand-rolled executor does not support non-JSON output, because `compiletest` need
-    /// to package test outcome as `libtest`-esque JSON that `bootstrap` can intercept *anyway*.
-    /// However, now that we don't use the `libtest` executor, this is useless.
-    pub format: OutputFormat,
-
     /// Whether to use colors in test output.
     ///
     /// Note: the exact control mechanism is delegated to [`colored`].
@@ -675,6 +669,10 @@ pub struct Config {
     /// to avoid `!nocapture` double-negatives.
     pub nocapture: bool,
 
+    /// True if the experimental new output-capture implementation should be
+    /// used, avoiding the need for `#![feature(internal_output_capture)]`.
+    pub new_output_capture: bool,
+
     /// Needed both to construct [`build_helper::git::GitConfig`].
     pub nightly_branch: String,
     pub git_merge_commit_email: String,
@@ -692,7 +690,9 @@ pub struct Config {
     pub minicore_path: Utf8PathBuf,
 
     /// Current codegen backend used.
-    pub codegen_backend: CodegenBackend,
+    pub default_codegen_backend: CodegenBackend,
+    /// Name/path of the backend to use instead of `default_codegen_backend`.
+    pub override_codegen_backend: Option<String>,
 }
 
 impl Config {
@@ -766,7 +766,6 @@ impl Config {
             adb_device_status: Default::default(),
             lldb_python_dir: Default::default(),
             verbose: Default::default(),
-            format: Default::default(),
             color: Default::default(),
             remote_test_client: Default::default(),
             compare_mode: Default::default(),
@@ -791,12 +790,14 @@ impl Config {
             builtin_cfg_names: Default::default(),
             supported_crate_types: Default::default(),
             nocapture: Default::default(),
+            new_output_capture: Default::default(),
             nightly_branch: Default::default(),
             git_merge_commit_email: Default::default(),
             profiler_runtime: Default::default(),
             diff_command: Default::default(),
             minicore_path: Default::default(),
-            codegen_backend: CodegenBackend::Llvm,
+            default_codegen_backend: CodegenBackend::Llvm,
+            override_codegen_backend: None,
         }
     }
 
@@ -1077,7 +1078,7 @@ pub struct TargetCfg {
     pub(crate) abi: String,
     #[serde(rename = "target-family", default)]
     pub(crate) families: Vec<String>,
-    #[serde(rename = "target-pointer-width", deserialize_with = "serde_parse_u32")]
+    #[serde(rename = "target-pointer-width")]
     pub(crate) pointer_width: u32,
     #[serde(rename = "target-endian", default)]
     endian: Endian,
@@ -1187,11 +1188,6 @@ fn query_rustc_output(config: &Config, args: &[&str], envs: HashMap<String, Stri
     String::from_utf8(output.stdout).unwrap()
 }
 
-fn serde_parse_u32<'de, D: Deserializer<'de>>(deserializer: D) -> Result<u32, D::Error> {
-    let string = String::deserialize(deserializer)?;
-    string.parse().map_err(D::Error::custom)
-}
-
 #[derive(Debug, Clone)]
 pub struct TestPaths {
     pub file: Utf8PathBuf,         // e.g., compile-test/foo/bar/baz.rs
diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs
index 13e694b7f03..e84a2278766 100644
--- a/src/tools/compiletest/src/directives.rs
+++ b/src/tools/compiletest/src/directives.rs
@@ -201,10 +201,14 @@ pub struct TestProps {
     /// Build and use `minicore` as `core` stub for `no_core` tests in cross-compilation scenarios
     /// that don't otherwise want/need `-Z build-std`.
     pub add_core_stubs: bool,
+    /// Add these flags to the build of `minicore`.
+    pub core_stubs_compile_flags: Vec<String>,
     /// Whether line annotatins are required for the given error kind.
     pub dont_require_annotations: HashSet<ErrorKind>,
     /// Whether pretty printers should be disabled in gdb.
     pub disable_gdb_pretty_printers: bool,
+    /// Compare the output by lines, rather than as a single string.
+    pub compare_output_by_lines: bool,
 }
 
 mod directives {
@@ -251,9 +255,11 @@ mod directives {
     pub const FILECHECK_FLAGS: &'static str = "filecheck-flags";
     pub const NO_AUTO_CHECK_CFG: &'static str = "no-auto-check-cfg";
     pub const ADD_CORE_STUBS: &'static str = "add-core-stubs";
+    pub const CORE_STUBS_COMPILE_FLAGS: &'static str = "core-stubs-compile-flags";
     // This isn't a real directive, just one that is probably mistyped often
     pub const INCORRECT_COMPILER_FLAGS: &'static str = "compiler-flags";
     pub const DISABLE_GDB_PRETTY_PRINTERS: &'static str = "disable-gdb-pretty-printers";
+    pub const COMPARE_OUTPUT_BY_LINES: &'static str = "compare-output-by-lines";
 }
 
 impl TestProps {
@@ -308,8 +314,10 @@ impl TestProps {
             no_auto_check_cfg: false,
             has_enzyme: false,
             add_core_stubs: false,
+            core_stubs_compile_flags: vec![],
             dont_require_annotations: Default::default(),
             disable_gdb_pretty_printers: false,
+            compare_output_by_lines: false,
         }
     }
 
@@ -649,6 +657,21 @@ impl TestProps {
 
                     self.update_add_core_stubs(ln, config);
 
+                    if let Some(flags) = config.parse_name_value_directive(
+                        ln,
+                        directives::CORE_STUBS_COMPILE_FLAGS,
+                        testfile,
+                        line_number,
+                    ) {
+                        let flags = split_flags(&flags);
+                        for flag in &flags {
+                            if flag == "--edition" || flag.starts_with("--edition=") {
+                                panic!("you must use `//@ edition` to configure the edition");
+                            }
+                        }
+                        self.core_stubs_compile_flags.extend(flags);
+                    }
+
                     if let Some(err_kind) = config.parse_name_value_directive(
                         ln,
                         DONT_REQUIRE_ANNOTATIONS,
@@ -664,6 +687,11 @@ impl TestProps {
                         DISABLE_GDB_PRETTY_PRINTERS,
                         &mut self.disable_gdb_pretty_printers,
                     );
+                    config.set_name_directive(
+                        ln,
+                        COMPARE_OUTPUT_BY_LINES,
+                        &mut self.compare_output_by_lines,
+                    );
                 },
             );
 
@@ -1003,7 +1031,7 @@ impl Config {
         if let Some(raw) = self.parse_name_value_directive(line, "revisions", testfile, line_number)
         {
             if self.mode == TestMode::RunMake {
-                panic!("`run-make` tests do not support revisions: {}", testfile);
+                panic!("`run-make` mode tests do not support revisions: {}", testfile);
             }
 
             let mut duplicates: HashSet<_> = existing.iter().cloned().collect();
@@ -1437,6 +1465,7 @@ pub(crate) fn make_test_description<R: Read>(
     cache: &DirectivesCache,
     name: String,
     path: &Utf8Path,
+    filterable_path: &Utf8Path,
     src: R,
     test_revision: Option<&str>,
     poisoned: &mut bool,
@@ -1511,7 +1540,13 @@ pub(crate) fn make_test_description<R: Read>(
         _ => ShouldPanic::No,
     };
 
-    CollectedTestDesc { name, ignore, ignore_message, should_panic }
+    CollectedTestDesc {
+        name,
+        filterable_path: filterable_path.to_owned(),
+        ignore,
+        ignore_message,
+        should_panic,
+    }
 }
 
 fn ignore_cdb(config: &Config, line: &str) -> IgnoreDecision {
@@ -1624,7 +1659,7 @@ fn ignore_backends(
                 }
             }
         }) {
-            if config.codegen_backend == backend {
+            if config.default_codegen_backend == backend {
                 return IgnoreDecision::Ignore {
                     reason: format!("{} backend is marked as ignore", backend.as_str()),
                 };
@@ -1651,12 +1686,12 @@ fn needs_backends(
                     panic!("Invalid needs-backends value `{backend}` in `{path}`: {error}")
                 }
             })
-            .any(|backend| config.codegen_backend == backend)
+            .any(|backend| config.default_codegen_backend == backend)
         {
             return IgnoreDecision::Ignore {
                 reason: format!(
                     "{} backend is not part of required backends",
-                    config.codegen_backend.as_str()
+                    config.default_codegen_backend.as_str()
                 ),
             };
         }
diff --git a/src/tools/compiletest/src/directives/directive_names.rs b/src/tools/compiletest/src/directives/directive_names.rs
index f7955429d83..4fef8992567 100644
--- a/src/tools/compiletest/src/directives/directive_names.rs
+++ b/src/tools/compiletest/src/directives/directive_names.rs
@@ -17,7 +17,9 @@ pub(crate) const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "check-run-results",
     "check-stdout",
     "check-test-line-numbers-match",
+    "compare-output-by-lines",
     "compile-flags",
+    "core-stubs-compile-flags",
     "disable-gdb-pretty-printers",
     "doc-flags",
     "dont-check-compiler-stderr",
@@ -194,6 +196,7 @@ pub(crate) const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "only-bpf",
     "only-cdb",
     "only-dist",
+    "only-eabihf",
     "only-elf",
     "only-emscripten",
     "only-gnu",
diff --git a/src/tools/compiletest/src/directives/needs.rs b/src/tools/compiletest/src/directives/needs.rs
index ee46f4c70cb..3b7a9478717 100644
--- a/src/tools/compiletest/src/directives/needs.rs
+++ b/src/tools/compiletest/src/directives/needs.rs
@@ -81,8 +81,8 @@ pub(super) fn handle_needs(
         },
         Need {
             name: "needs-enzyme",
-            condition: config.has_enzyme,
-            ignore_reason: "ignored when LLVM Enzyme is disabled",
+            condition: config.has_enzyme && config.default_codegen_backend.is_llvm(),
+            ignore_reason: "ignored when LLVM Enzyme is disabled or LLVM is not the default codegen backend",
         },
         Need {
             name: "needs-run-enabled",
@@ -161,8 +161,8 @@ pub(super) fn handle_needs(
         },
         Need {
             name: "needs-llvm-zstd",
-            condition: cache.llvm_zstd,
-            ignore_reason: "ignored if LLVM wasn't build with zstd for ELF section compression",
+            condition: cache.llvm_zstd && config.default_codegen_backend.is_llvm(),
+            ignore_reason: "ignored if LLVM wasn't build with zstd for ELF section compression or LLVM is not the default codegen backend",
         },
         Need {
             name: "needs-rustc-debug-assertions",
@@ -279,7 +279,10 @@ pub(super) fn handle_needs(
 
     // Handled elsewhere.
     if name == "needs-llvm-components" {
-        return IgnoreDecision::Continue;
+        if config.default_codegen_backend.is_llvm() {
+            return IgnoreDecision::Continue;
+        }
+        return IgnoreDecision::Ignore { reason: "LLVM specific test".into() };
     }
 
     let mut found_valid = false;
diff --git a/src/tools/compiletest/src/directives/tests.rs b/src/tools/compiletest/src/directives/tests.rs
index 33a02eb29fd..16cf76be9a5 100644
--- a/src/tools/compiletest/src/directives/tests.rs
+++ b/src/tools/compiletest/src/directives/tests.rs
@@ -14,6 +14,7 @@ fn make_test_description<R: Read>(
     config: &Config,
     name: String,
     path: &Utf8Path,
+    filterable_path: &Utf8Path,
     src: R,
     revision: Option<&str>,
 ) -> CollectedTestDesc {
@@ -24,6 +25,7 @@ fn make_test_description<R: Read>(
         &cache,
         name,
         path,
+        filterable_path,
         src,
         revision,
         &mut poisoned,
@@ -221,7 +223,7 @@ fn parse_rs(config: &Config, contents: &str) -> EarlyProps {
 fn check_ignore(config: &Config, contents: &str) -> bool {
     let tn = String::new();
     let p = Utf8Path::new("a.rs");
-    let d = make_test_description(&config, tn, p, std::io::Cursor::new(contents), None);
+    let d = make_test_description(&config, tn, p, p, std::io::Cursor::new(contents), None);
     d.ignore
 }
 
@@ -231,9 +233,9 @@ fn should_fail() {
     let tn = String::new();
     let p = Utf8Path::new("a.rs");
 
-    let d = make_test_description(&config, tn.clone(), p, std::io::Cursor::new(""), None);
+    let d = make_test_description(&config, tn.clone(), p, p, std::io::Cursor::new(""), None);
     assert_eq!(d.should_panic, ShouldPanic::No);
-    let d = make_test_description(&config, tn, p, std::io::Cursor::new("//@ should-fail"), None);
+    let d = make_test_description(&config, tn, p, p, std::io::Cursor::new("//@ should-fail"), None);
     assert_eq!(d.should_panic, ShouldPanic::Yes);
 }
 
diff --git a/src/tools/compiletest/src/executor.rs b/src/tools/compiletest/src/executor.rs
index df64f12784f..c8e13d44573 100644
--- a/src/tools/compiletest/src/executor.rs
+++ b/src/tools/compiletest/src/executor.rs
@@ -1,5 +1,9 @@
 //! This module contains a reimplementation of the subset of libtest
 //! functionality needed by compiletest.
+//!
+//! FIXME(Zalathar): Much of this code was originally designed to mimic libtest
+//! as closely as possible, for ease of migration. Now that libtest is no longer
+//! used, we can potentially redesign things to be a better fit for compiletest.
 
 use std::borrow::Cow;
 use std::collections::HashMap;
@@ -8,7 +12,11 @@ use std::num::NonZero;
 use std::sync::{Arc, Mutex, mpsc};
 use std::{env, hint, io, mem, panic, thread};
 
+use camino::Utf8PathBuf;
+
 use crate::common::{Config, TestPaths};
+use crate::output_capture::{self, ConsoleOut};
+use crate::panic_hook;
 
 mod deadline;
 mod json;
@@ -115,16 +123,28 @@ fn run_test_inner(
     runnable_test: RunnableTest,
     completion_sender: mpsc::Sender<TestCompletion>,
 ) {
-    let is_capture = !runnable_test.config.nocapture;
-    let capture_buf = is_capture.then(|| Arc::new(Mutex::new(vec![])));
+    let capture = CaptureKind::for_config(&runnable_test.config);
+
+    // Install a panic-capture buffer for use by the custom panic hook.
+    if capture.should_set_panic_hook() {
+        panic_hook::set_capture_buf(Default::default());
+    }
 
-    if let Some(capture_buf) = &capture_buf {
-        io::set_output_capture(Some(Arc::clone(capture_buf)));
+    if let CaptureKind::Old { ref buf } = capture {
+        io::set_output_capture(Some(Arc::clone(buf)));
     }
 
-    let panic_payload = panic::catch_unwind(move || runnable_test.run()).err();
+    let stdout = capture.stdout();
+    let stderr = capture.stderr();
+
+    let panic_payload = panic::catch_unwind(move || runnable_test.run(stdout, stderr)).err();
 
-    if is_capture {
+    if let Some(panic_buf) = panic_hook::take_capture_buf() {
+        let panic_buf = panic_buf.lock().unwrap_or_else(|e| e.into_inner());
+        // Forward any captured panic message to (captured) stderr.
+        write!(stderr, "{panic_buf}");
+    }
+    if matches!(capture, CaptureKind::Old { .. }) {
         io::set_output_capture(None);
     }
 
@@ -135,11 +155,70 @@ fn run_test_inner(
             TestOutcome::Failed { message: Some("test did not panic as expected") }
         }
     };
-    let stdout = capture_buf.map(|mutex| mutex.lock().unwrap_or_else(|e| e.into_inner()).to_vec());
 
+    let stdout = capture.into_inner();
     completion_sender.send(TestCompletion { id, outcome, stdout }).unwrap();
 }
 
+enum CaptureKind {
+    /// Do not capture test-runner output, for `--no-capture`.
+    ///
+    /// (This does not affect `rustc` and other subprocesses spawned by test
+    /// runners, whose output is always captured.)
+    None,
+
+    /// Use the old output-capture implementation, which relies on the unstable
+    /// library feature `#![feature(internal_output_capture)]`.
+    Old { buf: Arc<Mutex<Vec<u8>>> },
+
+    /// Use the new output-capture implementation, which only uses stable Rust.
+    New { buf: output_capture::CaptureBuf },
+}
+
+impl CaptureKind {
+    fn for_config(config: &Config) -> Self {
+        if config.nocapture {
+            Self::None
+        } else if config.new_output_capture {
+            Self::New { buf: output_capture::CaptureBuf::new() }
+        } else {
+            // Create a capure buffer for `io::set_output_capture`.
+            Self::Old { buf: Default::default() }
+        }
+    }
+
+    fn should_set_panic_hook(&self) -> bool {
+        match self {
+            Self::None => false,
+            Self::Old { .. } => true,
+            Self::New { .. } => true,
+        }
+    }
+
+    fn stdout(&self) -> &dyn ConsoleOut {
+        self.capture_buf_or(&output_capture::Stdout)
+    }
+
+    fn stderr(&self) -> &dyn ConsoleOut {
+        self.capture_buf_or(&output_capture::Stderr)
+    }
+
+    fn capture_buf_or<'a>(&'a self, fallback: &'a dyn ConsoleOut) -> &'a dyn ConsoleOut {
+        match self {
+            Self::None | Self::Old { .. } => fallback,
+            Self::New { buf } => buf,
+        }
+    }
+
+    fn into_inner(self) -> Option<Vec<u8>> {
+        match self {
+            Self::None => None,
+            Self::Old { buf } => Some(buf.lock().unwrap_or_else(|e| e.into_inner()).to_vec()),
+            Self::New { buf } => Some(buf.into_inner().into()),
+        }
+    }
+}
+
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
 struct TestId(usize);
 
@@ -157,10 +236,12 @@ impl RunnableTest {
         Self { config, testpaths, revision }
     }
 
-    fn run(&self) {
+    fn run(&self, stdout: &dyn ConsoleOut, stderr: &dyn ConsoleOut) {
         __rust_begin_short_backtrace(|| {
             crate::runtest::run(
                 Arc::clone(&self.config),
+                stdout,
+                stderr,
                 &self.testpaths,
                 self.revision.as_deref(),
             );
@@ -207,15 +288,22 @@ impl TestOutcome {
 ///
 /// Adapted from `filter_tests` in libtest.
 ///
-/// FIXME(#139660): After the libtest dependency is removed, redesign the whole filtering system to
+/// FIXME(#139660): Now that libtest has been removed, redesign the whole filtering system to
 /// do a better job of understanding and filtering _paths_, instead of being tied to libtest's
 /// substring/exact matching behaviour.
 fn filter_tests(opts: &Config, tests: Vec<CollectedTest>) -> Vec<CollectedTest> {
     let mut filtered = tests;
 
     let matches_filter = |test: &CollectedTest, filter_str: &str| {
-        let test_name = &test.desc.name;
-        if opts.filter_exact { test_name == filter_str } else { test_name.contains(filter_str) }
+        if opts.filter_exact {
+            // When `--exact` is used we must use `filterable_path` to get
+            // reasonable filtering behavior.
+            test.desc.filterable_path.as_str() == filter_str
+        } else {
+            // For compatibility we use the name (which includes the full path)
+            // if `--exact` is not used.
+            test.desc.name.contains(filter_str)
+        }
     };
 
     // Remove tests that don't match the test filter
@@ -249,7 +337,7 @@ fn get_concurrency() -> usize {
     }
 }
 
-/// Information needed to create a `test::TestDescAndFn`.
+/// Information that was historically needed to create a libtest `TestDescAndFn`.
 pub(crate) struct CollectedTest {
     pub(crate) desc: CollectedTestDesc,
     pub(crate) config: Arc<Config>,
@@ -257,9 +345,10 @@ pub(crate) struct CollectedTest {
     pub(crate) revision: Option<String>,
 }
 
-/// Information needed to create a `test::TestDesc`.
+/// Information that was historically needed to create a libtest `TestDesc`.
 pub(crate) struct CollectedTestDesc {
     pub(crate) name: String,
+    pub(crate) filterable_path: Utf8PathBuf,
     pub(crate) ignore: bool,
     pub(crate) ignore_message: Option<Cow<'static, str>>,
     pub(crate) should_panic: ShouldPanic,
@@ -274,18 +363,6 @@ pub enum ColorConfig {
     NeverColor,
 }
 
-/// Format of the test results output.
-#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
-pub enum OutputFormat {
-    /// Verbose output
-    Pretty,
-    /// Quiet output
-    #[default]
-    Terse,
-    /// JSON output
-    Json,
-}
-
 /// Whether test is expected to panic or not.
 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
 pub(crate) enum ShouldPanic {
diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs
index f5409e78341..ce2a3d4b5fb 100644
--- a/src/tools/compiletest/src/lib.rs
+++ b/src/tools/compiletest/src/lib.rs
@@ -9,13 +9,14 @@
 mod tests;
 
 pub mod common;
-pub mod compute_diff;
 mod debuggers;
 pub mod diagnostics;
 pub mod directives;
 pub mod errors;
 mod executor;
 mod json;
+mod output_capture;
+mod panic_hook;
 mod raise_fd_limit;
 mod read2;
 pub mod runtest;
@@ -43,8 +44,7 @@ use crate::common::{
     expected_output_path, output_base_dir, output_relative_path,
 };
 use crate::directives::DirectivesCache;
-use crate::executor::{CollectedTest, ColorConfig, OutputFormat};
-use crate::util::logv;
+use crate::executor::{CollectedTest, ColorConfig};
 
 /// Creates the `Config` instance for this invocation of compiletest.
 ///
@@ -137,9 +137,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
             "overwrite stderr/stdout files instead of complaining about a mismatch",
         )
         .optflag("", "fail-fast", "stop as soon as possible after any test fails")
-        .optflag("", "quiet", "print one character per test instead of one line")
         .optopt("", "color", "coloring: auto, always, never", "WHEN")
-        .optflag("", "json", "emit json output instead of plaintext output")
         .optopt("", "target", "the target to build for", "TARGET")
         .optopt("", "host", "the host to build for", "HOST")
         .optopt("", "cdb", "path to CDB to use for CDB debuginfo tests", "PATH")
@@ -180,6 +178,12 @@ pub fn parse_config(args: Vec<String>) -> Config {
         // FIXME: Temporarily retained so we can point users to `--no-capture`
         .optflag("", "nocapture", "")
         .optflag("", "no-capture", "don't capture stdout/stderr of tests")
+        .optopt(
+            "N",
+            "new-output-capture",
+            "enables or disables the new output-capture implementation",
+            "off|on",
+        )
         .optflag("", "profiler-runtime", "is the profiler runtime enabled for this target")
         .optflag("h", "help", "show this message")
         .reqopt("", "channel", "current Rust channel", "CHANNEL")
@@ -203,7 +207,6 @@ pub fn parse_config(args: Vec<String>) -> Config {
             "COMMAND",
         )
         .reqopt("", "minicore-path", "path to minicore aux library", "PATH")
-        .optflag("N", "no-new-executor", "disables the new test executor, and uses libtest instead")
         .optopt(
             "",
             "debugger",
@@ -212,9 +215,15 @@ pub fn parse_config(args: Vec<String>) -> Config {
         )
         .optopt(
             "",
-            "codegen-backend",
+            "default-codegen-backend",
             "the codegen backend currently used",
             "CODEGEN BACKEND NAME",
+        )
+        .optopt(
+            "",
+            "override-codegen-backend",
+            "the codegen backend to use instead of the default one",
+            "CODEGEN BACKEND [NAME | PATH]",
         );
 
     let (argv0, args_) = args.split_first().unwrap();
@@ -276,14 +285,17 @@ pub fn parse_config(args: Vec<String>) -> Config {
             || directives::extract_llvm_version_from_binary(&matches.opt_str("llvm-filecheck")?),
         );
 
-    let codegen_backend = match matches.opt_str("codegen-backend").as_deref() {
+    let default_codegen_backend = match matches.opt_str("default-codegen-backend").as_deref() {
         Some(backend) => match CodegenBackend::try_from(backend) {
             Ok(backend) => backend,
-            Err(error) => panic!("invalid value `{backend}` for `--codegen-backend`: {error}"),
+            Err(error) => {
+                panic!("invalid value `{backend}` for `--defalt-codegen-backend`: {error}")
+            }
         },
         // By default, it's always llvm.
         None => CodegenBackend::Llvm,
     };
+    let override_codegen_backend = matches.opt_str("override-codegen-backend");
 
     let run_ignored = matches.opt_present("ignored");
     let with_rustc_debug_assertions = matches.opt_present("with-rustc-debug-assertions");
@@ -305,11 +317,17 @@ pub fn parse_config(args: Vec<String>) -> Config {
             .free
             .iter()
             .map(|f| {
+                // Here `f` is relative to `./tests/run-make`. So if you run
+                //
+                //   ./x test tests/run-make/crate-loading
+                //
+                //  then `f` is "crate-loading".
                 let path = Utf8Path::new(f);
                 let mut iter = path.iter().skip(1);
 
-                // We skip the test folder and check if the user passed `rmake.rs`.
                 if iter.next().is_some_and(|s| s == "rmake.rs") && iter.next().is_none() {
+                    // Strip the "rmake.rs" suffix. For example, if `f` is
+                    // "crate-loading/rmake.rs" then this gives us "crate-loading".
                     path.parent().unwrap().to_string()
                 } else {
                     f.to_string()
@@ -317,6 +335,12 @@ pub fn parse_config(args: Vec<String>) -> Config {
             })
             .collect::<Vec<_>>()
     } else {
+        // Note that the filters are relative to the root dir of the different test
+        // suites. For example, with:
+        //
+        //   ./x test tests/ui/lint/unused
+        //
+        // the filter is "lint/unused".
         matches.free.clone()
     };
     let compare_mode = matches.opt_str("compare-mode").map(|s| {
@@ -427,12 +451,6 @@ pub fn parse_config(args: Vec<String>) -> Config {
             && !opt_str2(matches.opt_str("adb-test-dir")).is_empty(),
         lldb_python_dir: matches.opt_str("lldb-python-dir"),
         verbose: matches.opt_present("verbose"),
-        format: match (matches.opt_present("quiet"), matches.opt_present("json")) {
-            (true, true) => panic!("--quiet and --json are incompatible"),
-            (true, false) => OutputFormat::Terse,
-            (false, true) => OutputFormat::Json,
-            (false, false) => OutputFormat::Pretty,
-        },
         only_modified: matches.opt_present("only-modified"),
         color,
         remote_test_client: matches.opt_str("remote-test-client").map(Utf8PathBuf::from),
@@ -462,6 +480,14 @@ pub fn parse_config(args: Vec<String>) -> Config {
         supported_crate_types: OnceLock::new(),
 
         nocapture: matches.opt_present("no-capture"),
+        new_output_capture: {
+            let value = matches
+                .opt_str("new-output-capture")
+                .or_else(|| env::var("COMPILETEST_NEW_OUTPUT_CAPTURE").ok())
+                .unwrap_or_else(|| "on".to_owned());
+            parse_bool_option(&value)
+                .unwrap_or_else(|| panic!("unknown `--new-output-capture` value `{value}` given"))
+        },
 
         nightly_branch: matches.opt_str("nightly-branch").unwrap(),
         git_merge_commit_email: matches.opt_str("git-merge-commit-email").unwrap(),
@@ -472,54 +498,22 @@ pub fn parse_config(args: Vec<String>) -> Config {
 
         minicore_path: opt_path(matches, "minicore-path"),
 
-        codegen_backend,
+        default_codegen_backend,
+        override_codegen_backend,
     }
 }
 
-pub fn log_config(config: &Config) {
-    let c = config;
-    logv(c, "configuration:".to_string());
-    logv(c, format!("compile_lib_path: {}", config.compile_lib_path));
-    logv(c, format!("run_lib_path: {}", config.run_lib_path));
-    logv(c, format!("rustc_path: {}", config.rustc_path));
-    logv(c, format!("cargo_path: {:?}", config.cargo_path));
-    logv(c, format!("rustdoc_path: {:?}", config.rustdoc_path));
-
-    logv(c, format!("src_root: {}", config.src_root));
-    logv(c, format!("src_test_suite_root: {}", config.src_test_suite_root));
-
-    logv(c, format!("build_root: {}", config.build_root));
-    logv(c, format!("build_test_suite_root: {}", config.build_test_suite_root));
-
-    logv(c, format!("sysroot_base: {}", config.sysroot_base));
-
-    logv(c, format!("stage: {}", config.stage));
-    logv(c, format!("stage_id: {}", config.stage_id));
-    logv(c, format!("mode: {}", config.mode));
-    logv(c, format!("run_ignored: {}", config.run_ignored));
-    logv(c, format!("filters: {:?}", config.filters));
-    logv(c, format!("skip: {:?}", config.skip));
-    logv(c, format!("filter_exact: {}", config.filter_exact));
-    logv(
-        c,
-        format!("force_pass_mode: {}", opt_str(&config.force_pass_mode.map(|m| format!("{}", m))),),
-    );
-    logv(c, format!("runner: {}", opt_str(&config.runner)));
-    logv(c, format!("host-rustcflags: {:?}", config.host_rustcflags));
-    logv(c, format!("target-rustcflags: {:?}", config.target_rustcflags));
-    logv(c, format!("target: {}", config.target));
-    logv(c, format!("host: {}", config.host));
-    logv(c, format!("android-cross-path: {}", config.android_cross_path));
-    logv(c, format!("adb_path: {}", config.adb_path));
-    logv(c, format!("adb_test_dir: {}", config.adb_test_dir));
-    logv(c, format!("adb_device_status: {}", config.adb_device_status));
-    logv(c, format!("ar: {}", config.ar));
-    logv(c, format!("target-linker: {:?}", config.target_linker));
-    logv(c, format!("host-linker: {:?}", config.host_linker));
-    logv(c, format!("verbose: {}", config.verbose));
-    logv(c, format!("format: {:?}", config.format));
-    logv(c, format!("minicore_path: {}", config.minicore_path));
-    logv(c, "\n".to_string());
+/// Parses the same set of boolean values accepted by rustc command-line arguments.
+///
+/// Accepting all of these values is more complicated than just picking one
+/// pair, but has the advantage that contributors who are used to rustc
+/// shouldn't have to think about which values are legal.
+fn parse_bool_option(value: &str) -> Option<bool> {
+    match value {
+        "off" | "no" | "n" | "false" => Some(false),
+        "on" | "yes" | "y" | "true" => Some(true),
+        _ => None,
+    }
 }
 
 pub fn opt_str(maybestr: &Option<String>) -> &str {
@@ -538,6 +532,10 @@ pub fn opt_str2(maybestr: Option<String>) -> String {
 
 /// Called by `main` after the config has been parsed.
 pub fn run_tests(config: Arc<Config>) {
+    debug!(?config, "run_tests");
+
+    panic_hook::install_panic_hook();
+
     // If we want to collect rustfix coverage information,
     // we first make sure that the coverage file does not exist.
     // It will be created later on.
@@ -591,7 +589,7 @@ pub fn run_tests(config: Arc<Config>) {
         configs.push(config.clone());
     };
 
-    // Discover all of the tests in the test suite directory, and build a libtest
+    // Discover all of the tests in the test suite directory, and build a `CollectedTest`
     // structure for each test (or each revision of a multi-revision test).
     let mut tests = Vec::new();
     for c in configs {
@@ -603,50 +601,35 @@ pub fn run_tests(config: Arc<Config>) {
     // Delegate to the executor to filter and run the big list of test structures
     // created during test discovery. When the executor decides to run a test,
     // it will return control to the rest of compiletest by calling `runtest::run`.
-    // FIXME(Zalathar): Once we're confident that we won't need to revert the
-    // removal of the libtest-based executor, remove this Result and other
-    // remnants of the old executor.
-    let res: io::Result<bool> = Ok(executor::run_tests(&config, tests));
-
-    // Check the outcome reported by libtest.
-    match res {
-        Ok(true) => {}
-        Ok(false) => {
-            // We want to report that the tests failed, but we also want to give
-            // some indication of just what tests we were running. Especially on
-            // CI, where there can be cross-compiled tests for a lot of
-            // architectures, without this critical information it can be quite
-            // easy to miss which tests failed, and as such fail to reproduce
-            // the failure locally.
-
-            let mut msg = String::from("Some tests failed in compiletest");
-            write!(msg, " suite={}", config.suite).unwrap();
-
-            if let Some(compare_mode) = config.compare_mode.as_ref() {
-                write!(msg, " compare_mode={}", compare_mode).unwrap();
-            }
+    let ok = executor::run_tests(&config, tests);
+
+    // Check the outcome reported by the executor.
+    if !ok {
+        // We want to report that the tests failed, but we also want to give
+        // some indication of just what tests we were running. Especially on
+        // CI, where there can be cross-compiled tests for a lot of
+        // architectures, without this critical information it can be quite
+        // easy to miss which tests failed, and as such fail to reproduce
+        // the failure locally.
+
+        let mut msg = String::from("Some tests failed in compiletest");
+        write!(msg, " suite={}", config.suite).unwrap();
+
+        if let Some(compare_mode) = config.compare_mode.as_ref() {
+            write!(msg, " compare_mode={}", compare_mode).unwrap();
+        }
 
-            if let Some(pass_mode) = config.force_pass_mode.as_ref() {
-                write!(msg, " pass_mode={}", pass_mode).unwrap();
-            }
+        if let Some(pass_mode) = config.force_pass_mode.as_ref() {
+            write!(msg, " pass_mode={}", pass_mode).unwrap();
+        }
 
-            write!(msg, " mode={}", config.mode).unwrap();
-            write!(msg, " host={}", config.host).unwrap();
-            write!(msg, " target={}", config.target).unwrap();
+        write!(msg, " mode={}", config.mode).unwrap();
+        write!(msg, " host={}", config.host).unwrap();
+        write!(msg, " target={}", config.target).unwrap();
 
-            println!("{msg}");
+        println!("{msg}");
 
-            std::process::exit(1);
-        }
-        Err(e) => {
-            // We don't know if tests passed or not, but if there was an error
-            // during testing we don't want to just succeed (we may not have
-            // tested something), so fail.
-            //
-            // This should realistically "never" happen, so don't try to make
-            // this a pretty error message.
-            panic!("I/O failure during tests: {:?}", e);
-        }
+        std::process::exit(1);
     }
 }
 
@@ -681,7 +664,11 @@ impl TestCollector {
 ///
 /// This always inspects _all_ test files in the suite (e.g. all 17k+ ui tests),
 /// regardless of whether any filters/tests were specified on the command-line,
-/// because filtering is handled later by libtest.
+/// because filtering is handled later by code that was copied from libtest.
+///
+/// FIXME(Zalathar): Now that we no longer rely on libtest, try to overhaul
+/// test discovery to take into account the filters/tests specified on the
+/// command-line, instead of having to enumerate everything.
 pub(crate) fn collect_and_make_tests(config: Arc<Config>) -> Vec<CollectedTest> {
     debug!("making tests from {}", config.src_test_suite_root);
     let common_inputs_stamp = common_inputs_stamp(&config);
@@ -795,7 +782,7 @@ fn modified_tests(config: &Config, dir: &Utf8Path) -> Result<Vec<Utf8PathBuf>, S
 }
 
 /// Recursively scans a directory to find test files and create test structures
-/// that will be handed over to libtest.
+/// that will be handed over to the executor.
 fn collect_tests_from_dir(
     cx: &TestCollectorCx,
     dir: &Utf8Path,
@@ -812,13 +799,13 @@ fn collect_tests_from_dir(
         && let Some(Utf8Component::Normal(parent)) = components.next()
         && parent == "tests"
         && let Ok(backend) = CodegenBackend::try_from(backend)
-        && backend != cx.config.codegen_backend
+        && backend != cx.config.default_codegen_backend
     {
         // We ignore asm tests which don't match the current codegen backend.
         warning!(
             "Ignoring tests in `{dir}` because they don't match the configured codegen \
              backend (`{}`)",
-            cx.config.codegen_backend.as_str(),
+            cx.config.default_codegen_backend.as_str(),
         );
         return Ok(TestCollector::new());
     }
@@ -861,7 +848,7 @@ fn collect_tests_from_dir(
             if is_test(file_name)
                 && (!cx.config.only_modified || cx.modified_tests.contains(&file_path))
             {
-                // We found a test file, so create the corresponding libtest structures.
+                // We found a test file, so create the corresponding test structures.
                 debug!(%file_path, "found test file");
 
                 // Record the stem of the test file, to check for overlaps later.
@@ -905,7 +892,7 @@ pub fn is_test(file_name: &str) -> bool {
 }
 
 /// For a single test file, creates one or more test structures (one per revision) that can be
-/// handed over to libtest to run, possibly in parallel.
+/// handed over to the executor to run, possibly in parallel.
 fn make_test(cx: &TestCollectorCx, collector: &mut TestCollector, testpaths: &TestPaths) {
     // For run-make tests, each "test file" is actually a _directory_ containing an `rmake.rs`. But
     // for the purposes of directive parsing, we want to look at that recipe file, not the directory
@@ -919,7 +906,7 @@ fn make_test(cx: &TestCollectorCx, collector: &mut TestCollector, testpaths: &Te
     // Scan the test file to discover its revisions, if any.
     let early_props = EarlyProps::from_file(&cx.config, &test_path);
 
-    // Normally we create one libtest structure per revision, with two exceptions:
+    // Normally we create one structure per revision, with two exceptions:
     // - If a test doesn't use revisions, create a dummy revision (None) so that
     //   the test can still run.
     // - Incremental tests inherently can't run their revisions in parallel, so
@@ -934,27 +921,31 @@ fn make_test(cx: &TestCollectorCx, collector: &mut TestCollector, testpaths: &Te
     // For each revision (or the sole dummy revision), create and append a
     // `CollectedTest` that can be handed over to the test executor.
     collector.tests.extend(revisions.into_iter().map(|revision| {
-        // Create a test name and description to hand over to libtest.
+        // Create a test name and description to hand over to the executor.
         let src_file = fs::File::open(&test_path).expect("open test file to parse ignores");
-        let test_name = make_test_name(&cx.config, testpaths, revision);
-        // Create a libtest description for the test/revision.
+        let (test_name, filterable_path) =
+            make_test_name_and_filterable_path(&cx.config, testpaths, revision);
+        // Create a description struct for the test/revision.
         // This is where `ignore-*`/`only-*`/`needs-*` directives are handled,
-        // because they need to set the libtest ignored flag.
+        // because they historically needed to set the libtest ignored flag.
         let mut desc = make_test_description(
             &cx.config,
             &cx.cache,
             test_name,
             &test_path,
+            &filterable_path,
             src_file,
             revision,
             &mut collector.poisoned,
         );
 
         // If a test's inputs haven't changed since the last time it ran,
-        // mark it as ignored so that libtest will skip it.
+        // mark it as ignored so that the executor will skip it.
         if !cx.config.force_rerun && is_up_to_date(cx, testpaths, &early_props, revision) {
             desc.ignore = true;
             // Keep this in sync with the "up-to-date" message detected by bootstrap.
+            // FIXME(Zalathar): Now that we are no longer tied to libtest, we could
+            // find a less fragile way to communicate this status to bootstrap.
             desc.ignore_message = Some("up-to-date".into());
         }
 
@@ -1094,8 +1085,12 @@ impl Stamp {
     }
 }
 
-/// Creates a name for this test/revision that can be handed over to libtest.
-fn make_test_name(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> String {
+/// Creates a name for this test/revision that can be handed over to the executor.
+fn make_test_name_and_filterable_path(
+    config: &Config,
+    testpaths: &TestPaths,
+    revision: Option<&str>,
+) -> (String, Utf8PathBuf) {
     // Print the name of the file, relative to the sources root.
     let path = testpaths.file.strip_prefix(&config.src_root).unwrap();
     let debugger = match config.debugger {
@@ -1107,14 +1102,23 @@ fn make_test_name(config: &Config, testpaths: &TestPaths, revision: Option<&str>
         None => String::new(),
     };
 
-    format!(
+    let name = format!(
         "[{}{}{}] {}{}",
         config.mode,
         debugger,
         mode_suffix,
         path,
         revision.map_or("".to_string(), |rev| format!("#{}", rev))
-    )
+    );
+
+    // `path` is the full path from the repo root like, `tests/ui/foo/bar.rs`.
+    // Filtering is applied without the `tests/ui/` part, so strip that off.
+    // First strip off "tests" to make sure we don't have some unexpected path.
+    let mut filterable_path = path.strip_prefix("tests").unwrap().to_owned();
+    // Now strip off e.g. "ui" or "run-make" component.
+    filterable_path = filterable_path.components().skip(1).collect();
+
+    (name, filterable_path)
 }
 
 /// Checks that test discovery didn't find any tests whose name stem is a prefix
diff --git a/src/tools/compiletest/src/output_capture.rs b/src/tools/compiletest/src/output_capture.rs
new file mode 100644
index 00000000000..de1aea11ade
--- /dev/null
+++ b/src/tools/compiletest/src/output_capture.rs
@@ -0,0 +1,52 @@
+use std::fmt;
+use std::panic::RefUnwindSafe;
+use std::sync::Mutex;
+
+pub trait ConsoleOut: fmt::Debug + RefUnwindSafe {
+    fn write_fmt(&self, args: fmt::Arguments<'_>);
+}
+
+#[derive(Debug)]
+pub(crate) struct Stdout;
+
+impl ConsoleOut for Stdout {
+    fn write_fmt(&self, args: fmt::Arguments<'_>) {
+        print!("{args}");
+    }
+}
+
+#[derive(Debug)]
+pub(crate) struct Stderr;
+
+impl ConsoleOut for Stderr {
+    fn write_fmt(&self, args: fmt::Arguments<'_>) {
+        eprint!("{args}");
+    }
+}
+
+pub(crate) struct CaptureBuf {
+    inner: Mutex<String>,
+}
+
+impl CaptureBuf {
+    pub(crate) fn new() -> Self {
+        Self { inner: Mutex::new(String::new()) }
+    }
+
+    pub(crate) fn into_inner(self) -> String {
+        self.inner.into_inner().unwrap_or_else(|e| e.into_inner())
+    }
+}
+
+impl fmt::Debug for CaptureBuf {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.debug_struct("CaptureBuf").finish_non_exhaustive()
+    }
+}
+
+impl ConsoleOut for CaptureBuf {
+    fn write_fmt(&self, args: fmt::Arguments<'_>) {
+        let mut s = self.inner.lock().unwrap_or_else(|e| e.into_inner());
+        <String as fmt::Write>::write_fmt(&mut s, args).unwrap();
+    }
+}
diff --git a/src/tools/compiletest/src/panic_hook.rs b/src/tools/compiletest/src/panic_hook.rs
new file mode 100644
index 00000000000..1661ca6dabe
--- /dev/null
+++ b/src/tools/compiletest/src/panic_hook.rs
@@ -0,0 +1,136 @@
+use std::backtrace::{Backtrace, BacktraceStatus};
+use std::cell::Cell;
+use std::fmt::{Display, Write};
+use std::panic::PanicHookInfo;
+use std::sync::{Arc, LazyLock, Mutex};
+use std::{env, mem, panic, thread};
+
+type PanicHook = Box<dyn Fn(&PanicHookInfo<'_>) + Sync + Send + 'static>;
+type CaptureBuf = Arc<Mutex<String>>;
+
+thread_local!(
+    static CAPTURE_BUF: Cell<Option<CaptureBuf>> = const { Cell::new(None) };
+);
+
+/// Installs a custom panic hook that will divert panic output to a thread-local
+/// capture buffer, but only for threads that have a capture buffer set.
+///
+/// Otherwise, the custom hook delegates to a copy of the default panic hook.
+pub(crate) fn install_panic_hook() {
+    let default_hook = panic::take_hook();
+    panic::set_hook(Box::new(move |info| custom_panic_hook(&default_hook, info)));
+}
+
+pub(crate) fn set_capture_buf(buf: CaptureBuf) {
+    CAPTURE_BUF.set(Some(buf));
+}
+
+pub(crate) fn take_capture_buf() -> Option<CaptureBuf> {
+    CAPTURE_BUF.take()
+}
+
+fn custom_panic_hook(default_hook: &PanicHook, info: &panic::PanicHookInfo<'_>) {
+    // Temporarily taking the capture buffer means that if a panic occurs in
+    // the subsequent code, that panic will fall back to the default hook.
+    let Some(buf) = take_capture_buf() else {
+        // There was no capture buffer, so delegate to the default hook.
+        default_hook(info);
+        return;
+    };
+
+    let mut out = buf.lock().unwrap_or_else(|e| e.into_inner());
+
+    let thread = thread::current().name().unwrap_or("(test runner)").to_owned();
+    let location = get_location(info);
+    let payload = payload_as_str(info).unwrap_or("Box<dyn Any>");
+    let backtrace = Backtrace::capture();
+
+    writeln!(out, "\nthread '{thread}' panicked at {location}:\n{payload}").unwrap();
+    match backtrace.status() {
+        BacktraceStatus::Captured => {
+            let bt = trim_backtrace(backtrace.to_string());
+            write!(out, "stack backtrace:\n{bt}",).unwrap();
+        }
+        BacktraceStatus::Disabled => {
+            writeln!(
+                out,
+                "note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace",
+            )
+            .unwrap();
+        }
+        _ => {}
+    }
+
+    drop(out);
+    set_capture_buf(buf);
+}
+
+fn get_location<'a>(info: &'a PanicHookInfo<'_>) -> &'a dyn Display {
+    match info.location() {
+        Some(location) => location,
+        None => &"(unknown)",
+    }
+}
+
+/// FIXME(Zalathar): Replace with `PanicHookInfo::payload_as_str` when that's
+/// stable in beta.
+fn payload_as_str<'a>(info: &'a PanicHookInfo<'_>) -> Option<&'a str> {
+    let payload = info.payload();
+    if let Some(s) = payload.downcast_ref::<&str>() {
+        Some(s)
+    } else if let Some(s) = payload.downcast_ref::<String>() {
+        Some(s)
+    } else {
+        None
+    }
+}
+
+fn rust_backtrace_full() -> bool {
+    static RUST_BACKTRACE_FULL: LazyLock<bool> =
+        LazyLock::new(|| matches!(env::var("RUST_BACKTRACE").as_deref(), Ok("full")));
+    *RUST_BACKTRACE_FULL
+}
+
+/// On stable, short backtraces are only available to the default panic hook,
+/// so if we want something similar we have to resort to string processing.
+fn trim_backtrace(full_backtrace: String) -> String {
+    if rust_backtrace_full() {
+        return full_backtrace;
+    }
+
+    let mut buf = String::with_capacity(full_backtrace.len());
+    // Don't print any frames until after the first `__rust_end_short_backtrace`.
+    let mut on = false;
+    // After the short-backtrace state is toggled, skip its associated "at" if present.
+    let mut skip_next_at = false;
+
+    let mut lines = full_backtrace.lines();
+    while let Some(line) = lines.next() {
+        if mem::replace(&mut skip_next_at, false) && line.trim_start().starts_with("at ") {
+            continue;
+        }
+
+        if line.contains("__rust_end_short_backtrace") {
+            on = true;
+            skip_next_at = true;
+            continue;
+        }
+        if line.contains("__rust_begin_short_backtrace") {
+            on = false;
+            skip_next_at = true;
+            continue;
+        }
+
+        if on {
+            writeln!(buf, "{line}").unwrap();
+        }
+    }
+
+    writeln!(
+        buf,
+        "note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace."
+    )
+    .unwrap();
+
+    buf
+}
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index debf67e2741..bd32bec383f 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -7,7 +7,7 @@ use std::io::prelude::*;
 use std::io::{self, BufReader};
 use std::process::{Child, Command, ExitStatus, Output, Stdio};
 use std::sync::Arc;
-use std::{env, iter, str};
+use std::{env, fmt, iter, str};
 
 use build_helper::fs::remove_and_create_dir_all;
 use camino::{Utf8Path, Utf8PathBuf};
@@ -21,15 +21,14 @@ use crate::common::{
     UI_WINDOWS_SVG, expected_output_path, incremental_dir, output_base_dir, output_base_name,
     output_testname_unique,
 };
-use crate::compute_diff::{DiffLine, make_diff, write_diff, write_filtered_diff};
 use crate::directives::TestProps;
 use crate::errors::{Error, ErrorKind, load_errors};
+use crate::output_capture::ConsoleOut;
 use crate::read2::{Truncated, read2_abbreviated};
-use crate::util::{Utf8PathBufExt, add_dylib_path, logv, static_regex};
+use crate::runtest::compute_diff::{DiffLine, make_diff, write_diff, write_filtered_diff};
+use crate::util::{Utf8PathBufExt, add_dylib_path, static_regex};
 use crate::{ColorConfig, help, json, stamp_file_path, warning};
 
-mod debugger;
-
 // Helper modules that implement test running logic for each test suite.
 // tidy-alphabetical-start
 mod assembly;
@@ -48,6 +47,8 @@ mod rustdoc_json;
 mod ui;
 // tidy-alphabetical-end
 
+mod compute_diff;
+mod debugger;
 #[cfg(test)]
 mod tests;
 
@@ -108,7 +109,13 @@ fn dylib_name(name: &str) -> String {
     format!("{}{name}.{}", std::env::consts::DLL_PREFIX, std::env::consts::DLL_EXTENSION)
 }
 
-pub fn run(config: Arc<Config>, testpaths: &TestPaths, revision: Option<&str>) {
+pub fn run(
+    config: Arc<Config>,
+    stdout: &dyn ConsoleOut,
+    stderr: &dyn ConsoleOut,
+    testpaths: &TestPaths,
+    revision: Option<&str>,
+) {
     match &*config.target {
         "arm-linux-androideabi"
         | "armv7-linux-androideabi"
@@ -131,7 +138,7 @@ pub fn run(config: Arc<Config>, testpaths: &TestPaths, revision: Option<&str>) {
 
     if config.verbose {
         // We're going to be dumping a lot of info. Start on a new line.
-        print!("\n\n");
+        write!(stdout, "\n\n");
     }
     debug!("running {}", testpaths.file);
     let mut props = TestProps::from_file(&testpaths.file, revision, &config);
@@ -143,7 +150,7 @@ pub fn run(config: Arc<Config>, testpaths: &TestPaths, revision: Option<&str>) {
         props.incremental_dir = Some(incremental_dir(&config, testpaths, revision));
     }
 
-    let cx = TestCx { config: &config, props: &props, testpaths, revision };
+    let cx = TestCx { config: &config, stdout, stderr, props: &props, testpaths, revision };
 
     if let Err(e) = create_dir_all(&cx.output_base_dir()) {
         panic!("failed to create output base directory {}: {e}", cx.output_base_dir());
@@ -162,6 +169,8 @@ pub fn run(config: Arc<Config>, testpaths: &TestPaths, revision: Option<&str>) {
             revision_props.incremental_dir = props.incremental_dir.clone();
             let rev_cx = TestCx {
                 config: &config,
+                stdout,
+                stderr,
                 props: &revision_props,
                 testpaths,
                 revision: Some(revision),
@@ -212,6 +221,8 @@ pub fn compute_stamp_hash(config: &Config) -> String {
 #[derive(Copy, Clone, Debug)]
 struct TestCx<'test> {
     config: &'test Config,
+    stdout: &'test dyn ConsoleOut,
+    stderr: &'test dyn ConsoleOut,
     props: &'test TestProps,
     testpaths: &'test TestPaths,
     revision: Option<&'test str>,
@@ -412,7 +423,7 @@ impl<'test> TestCx<'test> {
             cmdline: format!("{cmd:?}"),
         };
         self.dump_output(
-            self.config.verbose,
+            self.config.verbose || !proc_res.status.success(),
             &cmd.get_program().to_string_lossy(),
             &proc_res.stdout,
             &proc_res.stderr,
@@ -603,7 +614,8 @@ impl<'test> TestCx<'test> {
             );
         } else {
             for pattern in missing_patterns {
-                println!(
+                writeln!(
+                    self.stdout,
                     "\n{prefix}: error pattern '{pattern}' not found!",
                     prefix = self.error_prefix()
                 );
@@ -783,7 +795,8 @@ impl<'test> TestCx<'test> {
                 };
                 format!("{file_name}:{line_num}{opt_col_num}")
             };
-            let print_error = |e| println!("{}: {}: {}", line_str(e), e.kind, e.msg.cyan());
+            let print_error =
+                |e| writeln!(self.stdout, "{}: {}: {}", line_str(e), e.kind, e.msg.cyan());
             let push_suggestion =
                 |suggestions: &mut Vec<_>, e: &Error, kind, line, msg, color, rank| {
                     let mut ret = String::new();
@@ -811,7 +824,7 @@ impl<'test> TestCx<'test> {
                 if let Some(&(_, top_rank)) = suggestions.first() {
                     for (suggestion, rank) in suggestions {
                         if rank == top_rank {
-                            println!("  {} {suggestion}", prefix.color(color));
+                            writeln!(self.stdout, "  {} {suggestion}", prefix.color(color));
                         }
                     }
                 }
@@ -824,7 +837,8 @@ impl<'test> TestCx<'test> {
             // - only known line - meh, but suggested
             // - others are not worth suggesting
             if !unexpected.is_empty() {
-                println!(
+                writeln!(
+                    self.stdout,
                     "\n{prefix}: {n} diagnostics reported in JSON output but not expected in test file",
                     prefix = self.error_prefix(),
                     n = unexpected.len(),
@@ -858,7 +872,8 @@ impl<'test> TestCx<'test> {
                 }
             }
             if !not_found.is_empty() {
-                println!(
+                writeln!(
+                    self.stdout,
                     "\n{prefix}: {n} diagnostics expected in test file but not reported in JSON output",
                     prefix = self.error_prefix(),
                     n = not_found.len(),
@@ -978,6 +993,8 @@ impl<'test> TestCx<'test> {
                     self.props.from_aux_file(&aux_testpaths.file, self.revision, self.config);
                 let aux_cx = TestCx {
                     config: self.config,
+                    stdout: self.stdout,
+                    stderr: self.stderr,
                     props: &props_for_aux,
                     testpaths: &aux_testpaths,
                     revision: self.revision,
@@ -1305,6 +1322,7 @@ impl<'test> TestCx<'test> {
 
         rustc.args(&["--crate-type", "rlib"]);
         rustc.arg("-Cpanic=abort");
+        rustc.args(self.props.core_stubs_compile_flags.clone());
 
         let res = self.compose_and_run(rustc, self.config.compile_lib_path.as_path(), None, None);
         if !res.status.success() {
@@ -1343,6 +1361,8 @@ impl<'test> TestCx<'test> {
         let aux_output = TargetLocation::ThisDirectory(aux_dir.clone());
         let aux_cx = TestCx {
             config: self.config,
+            stdout: self.stdout,
+            stderr: self.stderr,
             props: &aux_props,
             testpaths: &aux_testpaths,
             revision: self.revision,
@@ -1413,6 +1433,12 @@ impl<'test> TestCx<'test> {
 
         aux_rustc.arg("-L").arg(&aux_dir);
 
+        if aux_props.add_core_stubs {
+            let minicore_path = self.build_minicore();
+            aux_rustc.arg("--extern");
+            aux_rustc.arg(&format!("minicore={}", minicore_path));
+        }
+
         let auxres = aux_cx.compose_and_run(
             aux_rustc,
             aux_cx.config.compile_lib_path.as_path(),
@@ -1459,7 +1485,7 @@ impl<'test> TestCx<'test> {
     ) -> ProcRes {
         let cmdline = {
             let cmdline = self.make_cmdline(&command, lib_path);
-            logv(self.config, format!("executing {}", cmdline));
+            self.logv(format_args!("executing {cmdline}"));
             cmdline
         };
 
@@ -1486,7 +1512,7 @@ impl<'test> TestCx<'test> {
         };
 
         self.dump_output(
-            self.config.verbose,
+            self.config.verbose || (!result.status.success() && self.config.mode != TestMode::Ui),
             &command.get_program().to_string_lossy(),
             &result.stdout,
             &result.stderr,
@@ -1558,6 +1584,11 @@ impl<'test> TestCx<'test> {
             rustc.arg("--sysroot").arg(&self.config.sysroot_base);
         }
 
+        // If the provided codegen backend is not LLVM, we need to pass it.
+        if let Some(ref backend) = self.config.override_codegen_backend {
+            rustc.arg(format!("-Zcodegen-backend={}", backend));
+        }
+
         // Optionally prevent default --target if specified in test compile-flags.
         let custom_target = self.props.compile_flags.iter().any(|x| x.starts_with("--target"));
 
@@ -1695,6 +1726,10 @@ impl<'test> TestCx<'test> {
             }
             TestMode::Assembly | TestMode::Codegen => {
                 rustc.arg("-Cdebug-assertions=no");
+                // For assembly and codegen tests, we want to use the same order
+                // of the items of a codegen unit as the source order, so that
+                // we can compare the output with the source code through filecheck.
+                rustc.arg("-Zcodegen-source-order");
             }
             TestMode::Crashes => {
                 set_mir_dump_dir(&mut rustc);
@@ -1830,14 +1865,13 @@ impl<'test> TestCx<'test> {
             }
         }
 
-        rustc.args(&self.props.compile_flags);
-
         // FIXME(jieyouxu): we should report a fatal error or warning if user wrote `-Cpanic=` with
-        // something that's not `abort` and `-Cforce-unwind-tables` with a value that is not `yes`,
-        // however, by moving this last we should override previous `-Cpanic`s and
-        // `-Cforce-unwind-tables`s. Note that checking here is very fragile, because we'd have to
-        // account for all possible compile flag splittings (they have some... intricacies and are
-        // not yet normalized).
+        // something that's not `abort` and `-Cforce-unwind-tables` with a value that is not `yes`.
+        //
+        // We could apply these last and override any provided flags. That would ensure that the
+        // build works, but some tests want to exercise that mixing panic modes in specific ways is
+        // rejected. So we enable aborting panics and unwind tables before adding flags, just to
+        // change the default.
         //
         // `minicore` requires `#![no_std]` and `#![no_core]`, which means no unwinding panics.
         if self.props.add_core_stubs {
@@ -1845,6 +1879,8 @@ impl<'test> TestCx<'test> {
             rustc.arg("-Cforce-unwind-tables=yes");
         }
 
+        rustc.args(&self.props.compile_flags);
+
         rustc
     }
 
@@ -1939,11 +1975,11 @@ impl<'test> TestCx<'test> {
         } else {
             path.file_name().unwrap().into()
         };
-        println!("------{proc_name} stdout------------------------------");
-        println!("{}", out);
-        println!("------{proc_name} stderr------------------------------");
-        println!("{}", err);
-        println!("------------------------------------------");
+        writeln!(self.stdout, "------{proc_name} stdout------------------------------");
+        writeln!(self.stdout, "{}", out);
+        writeln!(self.stdout, "------{proc_name} stderr------------------------------");
+        writeln!(self.stdout, "{}", err);
+        writeln!(self.stdout, "------------------------------------------");
     }
 
     fn dump_output_file(&self, out: &str, extension: &str) {
@@ -1997,6 +2033,18 @@ impl<'test> TestCx<'test> {
         output_base_name(self.config, self.testpaths, self.safe_revision())
     }
 
+    /// Prints a message to (captured) stdout if `config.verbose` is true.
+    /// The message is also logged to `tracing::debug!` regardles of verbosity.
+    ///
+    /// Use `format_args!` as the argument to perform formatting if required.
+    fn logv(&self, message: impl fmt::Display) {
+        debug!("{message}");
+        if self.config.verbose {
+            // Note: `./x test ... --verbose --no-capture` is needed to see this print.
+            writeln!(self.stdout, "{message}");
+        }
+    }
+
     /// Prefix to print before error messages. Normally just `error`, but also
     /// includes the revision name for tests that use revisions.
     #[must_use]
@@ -2009,7 +2057,7 @@ impl<'test> TestCx<'test> {
 
     #[track_caller]
     fn fatal(&self, err: &str) -> ! {
-        println!("\n{prefix}: {err}", prefix = self.error_prefix());
+        writeln!(self.stdout, "\n{prefix}: {err}", prefix = self.error_prefix());
         error!("fatal error, panic: {:?}", err);
         panic!("fatal error");
     }
@@ -2027,15 +2075,15 @@ impl<'test> TestCx<'test> {
         proc_res: &ProcRes,
         callback_before_unwind: impl FnOnce(),
     ) -> ! {
-        println!("\n{prefix}: {err}", prefix = self.error_prefix());
+        writeln!(self.stdout, "\n{prefix}: {err}", prefix = self.error_prefix());
 
         // Some callers want to print additional notes after the main error message.
         if let Some(note) = extra_note {
-            println!("{note}");
+            writeln!(self.stdout, "{note}");
         }
 
         // Print the details and output of the subprocess that caused this test to fail.
-        println!("{}", proc_res.format_info());
+        writeln!(self.stdout, "{}", proc_res.format_info());
 
         // Some callers want print more context or show a custom diff before the unwind occurs.
         callback_before_unwind();
@@ -2105,7 +2153,7 @@ impl<'test> TestCx<'test> {
         if !self.config.has_html_tidy {
             return;
         }
-        println!("info: generating a diff against nightly rustdoc");
+        writeln!(self.stdout, "info: generating a diff against nightly rustdoc");
 
         let suffix =
             self.safe_revision().map_or("nightly".into(), |path| path.to_owned() + "-nightly");
@@ -2141,13 +2189,13 @@ impl<'test> TestCx<'test> {
 
         let proc_res = new_rustdoc.document(&compare_dir, &new_rustdoc.testpaths);
         if !proc_res.status.success() {
-            eprintln!("failed to run nightly rustdoc");
+            writeln!(self.stderr, "failed to run nightly rustdoc");
             return;
         }
 
         #[rustfmt::skip]
         let tidy_args = [
-            "--new-blocklevel-tags", "rustdoc-search,rustdoc-toolbar",
+            "--new-blocklevel-tags", "rustdoc-search,rustdoc-toolbar,rustdoc-topbar",
             "--indent", "yes",
             "--indent-spaces", "2",
             "--wrap", "0",
@@ -2186,6 +2234,7 @@ impl<'test> TestCx<'test> {
         let diff_filename = format!("build/tmp/rustdoc-compare-{}.diff", std::process::id());
 
         if !write_filtered_diff(
+            self,
             &diff_filename,
             out_dir,
             &compare_dir,
@@ -2206,19 +2255,19 @@ impl<'test> TestCx<'test> {
         if let Some(pager) = pager {
             let pager = pager.trim();
             if self.config.verbose {
-                eprintln!("using pager {}", pager);
+                writeln!(self.stderr, "using pager {}", pager);
             }
             let output = Command::new(pager)
                 // disable paging; we want this to be non-interactive
                 .env("PAGER", "")
                 .stdin(File::open(&diff_filename).unwrap())
                 // Capture output and print it explicitly so it will in turn be
-                // captured by libtest.
+                // captured by output-capture.
                 .output()
                 .unwrap();
             assert!(output.status.success());
-            println!("{}", String::from_utf8_lossy(&output.stdout));
-            eprintln!("{}", String::from_utf8_lossy(&output.stderr));
+            writeln!(self.stdout, "{}", String::from_utf8_lossy(&output.stdout));
+            writeln!(self.stderr, "{}", String::from_utf8_lossy(&output.stderr));
         } else {
             warning!("no pager configured, falling back to unified diff");
             help!(
@@ -2233,7 +2282,7 @@ impl<'test> TestCx<'test> {
                 match diff.read_until(b'\n', &mut line) {
                     Ok(0) => break,
                     Ok(_) => {}
-                    Err(e) => eprintln!("ERROR: {:?}", e),
+                    Err(e) => writeln!(self.stderr, "ERROR: {:?}", e),
                 }
                 match String::from_utf8(line.clone()) {
                     Ok(line) => {
@@ -2614,7 +2663,7 @@ impl<'test> TestCx<'test> {
 
             // The alloc-id appears in pretty-printed allocations.
             normalized = static_regex!(
-                r"╾─*a(lloc)?([0-9]+)(\+0x[0-9]+)?(<imm>)?( \([0-9]+ ptr bytes\))?─*╼"
+                r"╾─*a(lloc)?([0-9]+)(\+0x[0-9a-f]+)?(<imm>)?( \([0-9]+ ptr bytes\))?─*╼"
             )
             .replace_all(&normalized, |caps: &Captures<'_>| {
                 // Renumber the captured index.
@@ -2657,8 +2706,8 @@ impl<'test> TestCx<'test> {
         //
         // It's not possible to detect paths in the error messages generally, but this is a
         // decent enough heuristic.
-        static_regex!(
-                r#"(?x)
+        let re = static_regex!(
+            r#"(?x)
                 (?:
                   # Match paths that don't include spaces.
                   (?:\\[\pL\pN\.\-_']+)+\.\pL+
@@ -2666,11 +2715,8 @@ impl<'test> TestCx<'test> {
                   # If the path starts with a well-known root, then allow spaces and no file extension.
                   \$(?:DIR|SRC_DIR|TEST_BUILD_DIR|BUILD_DIR|LIB_DIR)(?:\\[\pL\pN\.\-_'\ ]+)+
                 )"#
-            )
-            .replace_all(&output, |caps: &Captures<'_>| {
-                println!("{}", &caps[0]);
-                caps[0].replace(r"\", "/")
-            })
+        );
+        re.replace_all(&output, |caps: &Captures<'_>| caps[0].replace(r"\", "/"))
             .replace("\r\n", "\n")
     }
 
@@ -2745,7 +2791,11 @@ impl<'test> TestCx<'test> {
         // Wrapper tools set by `runner` might provide extra output on failure,
         // for example a WebAssembly runtime might print the stack trace of an
         // `unreachable` instruction by default.
-        let compare_output_by_lines = self.config.runner.is_some();
+        //
+        // Also, some tests like `ui/parallel-rustc` have non-deterministic
+        // orders of output, so we need to compare by lines.
+        let compare_output_by_lines =
+            self.props.compare_output_by_lines || self.config.runner.is_some();
 
         let tmp;
         let (expected, actual): (&str, &str) = if compare_output_by_lines {
@@ -2780,11 +2830,11 @@ impl<'test> TestCx<'test> {
         if let Err(err) = fs::write(&actual_path, &actual) {
             self.fatal(&format!("failed to write {stream} to `{actual_path}`: {err}",));
         }
-        println!("Saved the actual {stream} to `{actual_path}`");
+        writeln!(self.stdout, "Saved the actual {stream} to `{actual_path}`");
 
         if !self.config.bless {
             if expected.is_empty() {
-                println!("normalized {}:\n{}\n", stream, actual);
+                writeln!(self.stdout, "normalized {}:\n{}\n", stream, actual);
             } else {
                 self.show_diff(
                     stream,
@@ -2808,14 +2858,15 @@ impl<'test> TestCx<'test> {
                 if let Err(err) = fs::write(&expected_path, &actual) {
                     self.fatal(&format!("failed to write {stream} to `{expected_path}`: {err}"));
                 }
-                println!(
+                writeln!(
+                    self.stdout,
                     "Blessing the {stream} of `{test_name}` as `{expected_path}`",
                     test_name = self.testpaths.file
                 );
             }
         }
 
-        println!("\nThe actual {stream} differed from the expected {stream}");
+        writeln!(self.stdout, "\nThe actual {stream} differed from the expected {stream}");
 
         if self.config.bless { CompareOutcome::Blessed } else { CompareOutcome::Differed }
     }
@@ -2830,7 +2881,7 @@ impl<'test> TestCx<'test> {
         actual: &str,
         actual_unnormalized: &str,
     ) {
-        eprintln!("diff of {stream}:\n");
+        writeln!(self.stderr, "diff of {stream}:\n");
         if let Some(diff_command) = self.config.diff_command.as_deref() {
             let mut args = diff_command.split_whitespace();
             let name = args.next().unwrap();
@@ -2842,11 +2893,11 @@ impl<'test> TestCx<'test> {
                 }
                 Ok(output) => {
                     let output = String::from_utf8_lossy(&output.stdout);
-                    eprint!("{output}");
+                    write!(self.stderr, "{output}");
                 }
             }
         } else {
-            eprint!("{}", write_diff(expected, actual, 3));
+            write!(self.stderr, "{}", write_diff(expected, actual, 3));
         }
 
         // NOTE: argument order is important, we need `actual` to be on the left so the line number match up when we compare it to `actual_unnormalized` below.
@@ -2884,9 +2935,16 @@ impl<'test> TestCx<'test> {
             && !mismatches_unnormalized.is_empty()
             && !mismatches_normalized.is_empty()
         {
-            eprintln!("Note: some mismatched output was normalized before being compared");
+            writeln!(
+                self.stderr,
+                "Note: some mismatched output was normalized before being compared"
+            );
             // FIXME: respect diff_command
-            eprint!("{}", write_diff(&mismatches_unnormalized, &mismatches_normalized, 0));
+            write!(
+                self.stderr,
+                "{}",
+                write_diff(&mismatches_unnormalized, &mismatches_normalized, 0)
+            );
         }
     }
 
@@ -2964,7 +3022,7 @@ impl<'test> TestCx<'test> {
         fs::create_dir_all(&incremental_dir).unwrap();
 
         if self.config.verbose {
-            println!("init_incremental_test: incremental_dir={incremental_dir}");
+            writeln!(self.stdout, "init_incremental_test: incremental_dir={incremental_dir}");
         }
     }
 }
@@ -2974,6 +3032,7 @@ struct ProcArgs {
     args: Vec<OsString>,
 }
 
+#[derive(Debug)]
 pub struct ProcRes {
     status: ExitStatus,
     stdout: String,
diff --git a/src/tools/compiletest/src/runtest/codegen_units.rs b/src/tools/compiletest/src/runtest/codegen_units.rs
index 44ddcb1d288..16c251c3c9e 100644
--- a/src/tools/compiletest/src/runtest/codegen_units.rs
+++ b/src/tools/compiletest/src/runtest/codegen_units.rs
@@ -62,13 +62,13 @@ impl TestCx<'_> {
         if !missing.is_empty() {
             missing.sort();
 
-            println!("\nThese items should have been contained but were not:\n");
+            writeln!(self.stdout, "\nThese items should have been contained but were not:\n");
 
             for item in &missing {
-                println!("{}", item);
+                writeln!(self.stdout, "{}", item);
             }
 
-            println!("\n");
+            writeln!(self.stdout, "\n");
         }
 
         if !unexpected.is_empty() {
@@ -78,24 +78,32 @@ impl TestCx<'_> {
                 sorted
             };
 
-            println!("\nThese items were contained but should not have been:\n");
+            writeln!(self.stdout, "\nThese items were contained but should not have been:\n");
 
             for item in sorted {
-                println!("{}", item);
+                writeln!(self.stdout, "{}", item);
             }
 
-            println!("\n");
+            writeln!(self.stdout, "\n");
         }
 
         if !wrong_cgus.is_empty() {
             wrong_cgus.sort_by_key(|pair| pair.0.name.clone());
-            println!("\nThe following items were assigned to wrong codegen units:\n");
+            writeln!(self.stdout, "\nThe following items were assigned to wrong codegen units:\n");
 
             for &(ref expected_item, ref actual_item) in &wrong_cgus {
-                println!("{}", expected_item.name);
-                println!("  expected: {}", codegen_units_to_str(&expected_item.codegen_units));
-                println!("  actual:   {}", codegen_units_to_str(&actual_item.codegen_units));
-                println!();
+                writeln!(self.stdout, "{}", expected_item.name);
+                writeln!(
+                    self.stdout,
+                    "  expected: {}",
+                    codegen_units_to_str(&expected_item.codegen_units)
+                );
+                writeln!(
+                    self.stdout,
+                    "  actual:   {}",
+                    codegen_units_to_str(&actual_item.codegen_units)
+                );
+                writeln!(self.stdout);
             }
         }
 
diff --git a/src/tools/compiletest/src/compute_diff.rs b/src/tools/compiletest/src/runtest/compute_diff.rs
index 509e7e11703..3363127b3ea 100644
--- a/src/tools/compiletest/src/compute_diff.rs
+++ b/src/tools/compiletest/src/runtest/compute_diff.rs
@@ -3,6 +3,8 @@ use std::fs::{File, FileType};
 
 use camino::Utf8Path;
 
+use crate::runtest::TestCx;
+
 #[derive(Debug, PartialEq)]
 pub enum DiffLine {
     Context(String),
@@ -112,6 +114,7 @@ pub(crate) fn write_diff(expected: &str, actual: &str, context_size: usize) -> S
 ///
 /// Returns whether any data was actually written.
 pub(crate) fn write_filtered_diff<Filter>(
+    cx: &TestCx<'_>,
     diff_filename: &str,
     out_dir: &Utf8Path,
     compare_dir: &Utf8Path,
@@ -147,11 +150,11 @@ where
     }
 
     if !wrote_data {
-        println!("note: diff is identical to nightly rustdoc");
+        writeln!(cx.stdout, "note: diff is identical to nightly rustdoc");
         assert!(diff_output.metadata().unwrap().len() == 0);
         return false;
     } else if verbose {
-        eprintln!("printing diff:");
+        writeln!(cx.stderr, "printing diff:");
         let mut buf = Vec::new();
         diff_output.read_to_end(&mut buf).unwrap();
         std::io::stderr().lock().write_all(&mut buf).unwrap();
diff --git a/src/tools/compiletest/src/runtest/crashes.rs b/src/tools/compiletest/src/runtest/crashes.rs
index da1e74b4a56..0aae7eaa39c 100644
--- a/src/tools/compiletest/src/runtest/crashes.rs
+++ b/src/tools/compiletest/src/runtest/crashes.rs
@@ -6,10 +6,10 @@ impl TestCx<'_> {
         let proc_res = self.compile_test(WillExecute::No, self.should_emit_metadata(pm));
 
         if std::env::var("COMPILETEST_VERBOSE_CRASHES").is_ok() {
-            eprintln!("{}", proc_res.status);
-            eprintln!("{}", proc_res.stdout);
-            eprintln!("{}", proc_res.stderr);
-            eprintln!("{}", proc_res.cmdline);
+            writeln!(self.stderr, "{}", proc_res.status);
+            writeln!(self.stderr, "{}", proc_res.stdout);
+            writeln!(self.stderr, "{}", proc_res.stderr);
+            writeln!(self.stderr, "{}", proc_res.cmdline);
         }
 
         // if a test does not crash, consider it an error
diff --git a/src/tools/compiletest/src/runtest/debuginfo.rs b/src/tools/compiletest/src/runtest/debuginfo.rs
index 6114afdc9df..071c0863b7e 100644
--- a/src/tools/compiletest/src/runtest/debuginfo.rs
+++ b/src/tools/compiletest/src/runtest/debuginfo.rs
@@ -10,7 +10,6 @@ use super::debugger::DebuggerCommands;
 use super::{Debugger, Emit, ProcRes, TestCx, Truncated, WillExecute};
 use crate::common::Config;
 use crate::debuggers::{extract_gdb_version, is_android_gdb_target};
-use crate::util::logv;
 
 impl TestCx<'_> {
     pub(super) fn run_debuginfo_test(&self) {
@@ -234,7 +233,7 @@ impl TestCx<'_> {
                 gdb.args(debugger_opts);
                 // FIXME(jieyouxu): don't pass an empty Path
                 let cmdline = self.make_cmdline(&gdb, Utf8Path::new(""));
-                logv(self.config, format!("executing {}", cmdline));
+                self.logv(format_args!("executing {cmdline}"));
                 cmdline
             };
 
@@ -246,7 +245,7 @@ impl TestCx<'_> {
                 cmdline,
             };
             if adb.kill().is_err() {
-                println!("Adb process is already finished.");
+                writeln!(self.stdout, "Adb process is already finished.");
             }
         } else {
             let rust_pp_module_abs_path = self.config.src_root.join("src").join("etc");
@@ -257,7 +256,11 @@ impl TestCx<'_> {
 
             match self.config.gdb_version {
                 Some(version) => {
-                    println!("NOTE: compiletest thinks it is using GDB version {}", version);
+                    writeln!(
+                        self.stdout,
+                        "NOTE: compiletest thinks it is using GDB version {}",
+                        version
+                    );
 
                     if !self.props.disable_gdb_pretty_printers
                         && version > extract_gdb_version("7.4").unwrap()
@@ -279,7 +282,8 @@ impl TestCx<'_> {
                     }
                 }
                 _ => {
-                    println!(
+                    writeln!(
+                        self.stdout,
                         "NOTE: compiletest does not know which version of \
                          GDB it is using"
                     );
@@ -377,10 +381,15 @@ impl TestCx<'_> {
 
         match self.config.lldb_version {
             Some(ref version) => {
-                println!("NOTE: compiletest thinks it is using LLDB version {}", version);
+                writeln!(
+                    self.stdout,
+                    "NOTE: compiletest thinks it is using LLDB version {}",
+                    version
+                );
             }
             _ => {
-                println!(
+                writeln!(
+                    self.stdout,
                     "NOTE: compiletest does not know which version of \
                      LLDB it is using"
                 );
@@ -395,6 +404,35 @@ impl TestCx<'_> {
         // We don't want to hang when calling `quit` while the process is still running
         let mut script_str = String::from("settings set auto-confirm true\n");
 
+        // macOS has a system for restricting access to files and peripherals
+        // called Transparency, Consent, and Control (TCC), which can be
+        // configured using the "Security & Privacy" tab in your settings.
+        //
+        // This system is provenance-based: if Terminal.app is given access to
+        // your Desktop, and you launch a binary within Terminal.app, the new
+        // binary also has access to the files on your Desktop.
+        //
+        // By default though, LLDB launches binaries in very isolated
+        // contexts. This includes resetting any TCC grants that might
+        // otherwise have been inherited.
+        //
+        // In effect, this means that if the developer has placed the rust
+        // repository under one of the system-protected folders, they will get
+        // a pop-up _for each binary_ asking for permissions to access the
+        // folder - quite annoying.
+        //
+        // To avoid this, we tell LLDB to spawn processes with TCC grants
+        // inherited from the parent process.
+        //
+        // Setting this also avoids unnecessary overhead from XprotectService
+        // when running with the Developer Tool grant.
+        //
+        // TIP: If you want to allow launching `lldb ~/Desktop/my_binary`
+        // without being prompted, you can put this in your `~/.lldbinit` too.
+        if self.config.host.contains("darwin") {
+            script_str.push_str("settings set target.inherit-tcc true\n");
+        }
+
         // Make LLDB emit its version, so we have it documented in the test output
         script_str.push_str("version\n");
 
diff --git a/src/tools/compiletest/src/runtest/incremental.rs b/src/tools/compiletest/src/runtest/incremental.rs
index 90cff6bab4d..44eb80300c3 100644
--- a/src/tools/compiletest/src/runtest/incremental.rs
+++ b/src/tools/compiletest/src/runtest/incremental.rs
@@ -30,7 +30,7 @@ impl TestCx<'_> {
         assert!(incremental_dir.exists(), "init_incremental_test failed to create incremental dir");
 
         if self.config.verbose {
-            print!("revision={:?} props={:#?}", revision, self.props);
+            write!(self.stdout, "revision={:?} props={:#?}", revision, self.props);
         }
 
         if revision.starts_with("cpass") {
diff --git a/src/tools/compiletest/src/runtest/mir_opt.rs b/src/tools/compiletest/src/runtest/mir_opt.rs
index efdb131bf14..94487926383 100644
--- a/src/tools/compiletest/src/runtest/mir_opt.rs
+++ b/src/tools/compiletest/src/runtest/mir_opt.rs
@@ -6,7 +6,7 @@ use miropt_test_tools::{MiroptTest, MiroptTestFile, files_for_miropt_test};
 use tracing::debug;
 
 use super::{Emit, TestCx, WillExecute};
-use crate::compute_diff::write_diff;
+use crate::runtest::compute_diff::write_diff;
 
 impl TestCx<'_> {
     pub(super) fn run_mir_opt_test(&self) {
@@ -80,7 +80,7 @@ impl TestCx<'_> {
                 }
                 let expected_string = fs::read_to_string(&expected_file).unwrap();
                 if dumped_string != expected_string {
-                    print!("{}", write_diff(&expected_string, &dumped_string, 3));
+                    write!(self.stdout, "{}", write_diff(&expected_string, &dumped_string, 3));
                     panic!(
                         "Actual MIR output differs from expected MIR output {}",
                         expected_file.display()
diff --git a/src/tools/compiletest/src/runtest/pretty.rs b/src/tools/compiletest/src/runtest/pretty.rs
index e3b07f1d63d..26557727233 100644
--- a/src/tools/compiletest/src/runtest/pretty.rs
+++ b/src/tools/compiletest/src/runtest/pretty.rs
@@ -1,14 +1,13 @@
 use std::fs;
 
 use super::{ProcRes, ReadFrom, TestCx};
-use crate::util::logv;
 
 impl TestCx<'_> {
     pub(super) fn run_pretty_test(&self) {
         if self.props.pp_exact.is_some() {
-            logv(self.config, "testing for exact pretty-printing".to_owned());
+            self.logv("testing for exact pretty-printing");
         } else {
-            logv(self.config, "testing for converging pretty-printing".to_owned());
+            self.logv("testing for converging pretty-printing");
         }
 
         let rounds = match self.props.pp_exact {
@@ -21,10 +20,7 @@ impl TestCx<'_> {
 
         let mut round = 0;
         while round < rounds {
-            logv(
-                self.config,
-                format!("pretty-printing round {} revision {:?}", round, self.revision),
-            );
+            self.logv(format_args!("pretty-printing round {round} revision {:?}", self.revision));
             let read_from =
                 if round == 0 { ReadFrom::Path } else { ReadFrom::Stdin(srcs[round].to_owned()) };
 
diff --git a/src/tools/compiletest/src/runtest/run_make.rs b/src/tools/compiletest/src/runtest/run_make.rs
index c8d5190c039..738f504d5c1 100644
--- a/src/tools/compiletest/src/runtest/run_make.rs
+++ b/src/tools/compiletest/src/runtest/run_make.rs
@@ -5,11 +5,12 @@ use build_helper::fs::{ignore_not_found, recursive_remove};
 use camino::{Utf8Path, Utf8PathBuf};
 
 use super::{ProcRes, TestCx, disable_error_reporting};
+use crate::common::TestSuite;
 use crate::util::{copy_dir_all, dylib_env_var};
 
 impl TestCx<'_> {
     pub(super) fn run_rmake_test(&self) {
-        // For `run-make` V2, we need to perform 2 steps to build and run a `run-make` V2 recipe
+        // For `run-make`, we need to perform 2 steps to build and run a `run-make` recipe
         // (`rmake.rs`) to run the actual tests. The support library is already built as a tool rust
         // library and is available under
         // `build/$HOST/bootstrap-tools/$TARGET/release/librun_make_support.rlib`.
@@ -189,8 +190,12 @@ impl TestCx<'_> {
             // through a specific CI runner).
             .env("LLVM_COMPONENTS", &self.config.llvm_components);
 
-        if let Some(ref cargo) = self.config.cargo_path {
-            cmd.env("CARGO", cargo);
+        // Only `run-make-cargo` test suite gets an in-tree `cargo`, not `run-make`.
+        if self.config.suite == TestSuite::RunMakeCargo {
+            cmd.env(
+                "CARGO",
+                self.config.cargo_path.as_ref().expect("cargo must be built and made available"),
+            );
         }
 
         if let Some(ref rustdoc) = self.config.rustdoc_path {
@@ -308,7 +313,7 @@ impl TestCx<'_> {
         let stdout = String::from_utf8_lossy(&stdout).into_owned();
         let stderr = String::from_utf8_lossy(&stderr).into_owned();
         // This conditions on `status.success()` so we don't print output twice on error.
-        // NOTE: this code is called from a libtest thread, so it's hidden by default unless --nocapture is passed.
+        // NOTE: this code is called from an executor thread, so it's hidden by default unless --no-capture is passed.
         self.dump_output(status.success(), &cmd.get_program().to_string_lossy(), &stdout, &stderr);
         if !status.success() {
             let res = ProcRes { status, stdout, stderr, truncated, cmdline: format!("{:?}", cmd) };
diff --git a/src/tools/compiletest/src/runtest/rustdoc_json.rs b/src/tools/compiletest/src/runtest/rustdoc_json.rs
index 083398f9274..b8da6e2ac52 100644
--- a/src/tools/compiletest/src/runtest/rustdoc_json.rs
+++ b/src/tools/compiletest/src/runtest/rustdoc_json.rs
@@ -30,8 +30,8 @@ impl TestCx<'_> {
 
         if !res.status.success() {
             self.fatal_proc_rec_general("jsondocck failed!", None, &res, || {
-                println!("Rustdoc Output:");
-                println!("{}", proc_res.format_info());
+                writeln!(self.stdout, "Rustdoc Output:");
+                writeln!(self.stdout, "{}", proc_res.format_info());
             })
         }
 
diff --git a/src/tools/compiletest/src/runtest/ui.rs b/src/tools/compiletest/src/runtest/ui.rs
index 0507c2600ae..d683a325c86 100644
--- a/src/tools/compiletest/src/runtest/ui.rs
+++ b/src/tools/compiletest/src/runtest/ui.rs
@@ -10,6 +10,7 @@ use super::{
     TestCx, TestOutput, Truncated, UI_FIXED, WillExecute,
 };
 use crate::json;
+use crate::runtest::ProcRes;
 
 impl TestCx<'_> {
     pub(super) fn run_ui_test(&self) {
@@ -114,10 +115,14 @@ impl TestCx<'_> {
         }
 
         if errors > 0 {
-            println!("To update references, rerun the tests and pass the `--bless` flag");
+            writeln!(
+                self.stdout,
+                "To update references, rerun the tests and pass the `--bless` flag"
+            );
             let relative_path_to_file =
                 self.testpaths.relative_dir.join(self.testpaths.file.file_name().unwrap());
-            println!(
+            writeln!(
+                self.stdout,
                 "To only update this specific test, also pass `--test-args {}`",
                 relative_path_to_file,
             );
@@ -127,6 +132,9 @@ impl TestCx<'_> {
             );
         }
 
+        // If the test is executed, capture its ProcRes separately so that
+        // pattern/forbid checks can report the *runtime* stdout/stderr when they fail.
+        let mut run_proc_res: Option<ProcRes> = None;
         let output_to_check = if let WillExecute::Yes = should_run {
             let proc_res = self.exec_compiled_test();
             let run_output_errors = if self.props.check_run_results {
@@ -189,7 +197,10 @@ impl TestCx<'_> {
                 unreachable!("run_ui_test() must not be called if the test should not run");
             }
 
-            self.get_output(&proc_res)
+            let output = self.get_output(&proc_res);
+            // Move the proc_res into our option after we've extracted output.
+            run_proc_res = Some(proc_res);
+            output
         } else {
             self.get_output(&proc_res)
         };
@@ -200,9 +211,14 @@ impl TestCx<'_> {
             explicit, self.config.compare_mode, proc_res.status, self.props.error_patterns
         );
 
+        // Compiler diagnostics (expected errors) are always tied to the compile-time ProcRes.
         self.check_expected_errors(&proc_res);
-        self.check_all_error_patterns(&output_to_check, &proc_res);
-        self.check_forbid_output(&output_to_check, &proc_res);
+
+        // For runtime pattern/forbid checks prefer the executed program's ProcRes if available
+        // so that missing pattern failures include the program's stdout/stderr.
+        let pattern_proc_res = run_proc_res.as_ref().unwrap_or(&proc_res);
+        self.check_all_error_patterns(&output_to_check, pattern_proc_res);
+        self.check_forbid_output(&output_to_check, pattern_proc_res);
 
         if self.props.run_rustfix && self.config.compare_mode.is_none() {
             // And finally, compile the fixed code and make sure it both
diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs
index fb047548c45..558e9a58697 100644
--- a/src/tools/compiletest/src/util.rs
+++ b/src/tools/compiletest/src/util.rs
@@ -2,9 +2,6 @@ use std::env;
 use std::process::Command;
 
 use camino::{Utf8Path, Utf8PathBuf};
-use tracing::*;
-
-use crate::common::Config;
 
 #[cfg(test)]
 mod tests;
@@ -26,14 +23,6 @@ fn path_div() -> &'static str {
     ";"
 }
 
-pub fn logv(config: &Config, s: String) {
-    debug!("{}", s);
-    if config.verbose {
-        // Note: `./x test ... --verbose --no-capture` is needed to see this print.
-        println!("{}", s);
-    }
-}
-
 pub trait Utf8PathBufExt {
     /// Append an extension to the path, even if it already has one.
     fn with_extra_extension(&self, extension: &str) -> Utf8PathBuf;
@@ -56,7 +45,7 @@ impl Utf8PathBufExt for Utf8PathBuf {
 
 /// The name of the environment variable that holds dynamic library locations.
 pub fn dylib_env_var() -> &'static str {
-    if cfg!(windows) {
+    if cfg!(any(windows, target_os = "cygwin")) {
         "PATH"
     } else if cfg!(target_vendor = "apple") {
         "DYLD_LIBRARY_PATH"
diff --git a/src/tools/enzyme b/src/tools/enzyme
-Subproject 58af4e9e6c047534ba059b12af17cecb8a2e9f9
+Subproject 09f4820b78e2d71b85a3278bbb41dc3a012e84d
diff --git a/src/tools/features-status-dump/src/main.rs b/src/tools/features-status-dump/src/main.rs
index 1ce98d1506d..a4f88362ab8 100644
--- a/src/tools/features-status-dump/src/main.rs
+++ b/src/tools/features-status-dump/src/main.rs
@@ -5,6 +5,7 @@ use std::path::PathBuf;
 
 use anyhow::{Context, Result};
 use clap::Parser;
+use tidy::diagnostics::RunningCheck;
 use tidy::features::{Feature, collect_lang_features, collect_lib_features};
 
 #[derive(Debug, Parser)]
@@ -29,7 +30,7 @@ struct FeaturesStatus {
 fn main() -> Result<()> {
     let Cli { compiler_path, library_path, output_path } = Cli::parse();
 
-    let lang_features_status = collect_lang_features(&compiler_path, &mut false);
+    let lang_features_status = collect_lang_features(&compiler_path, &mut RunningCheck::new_noop());
     let lib_features_status = collect_lib_features(&library_path)
         .into_iter()
         .filter(|&(ref name, _)| !lang_features_status.contains_key(name))
diff --git a/src/tools/html-checker/main.rs b/src/tools/html-checker/main.rs
index 5cdc4d53ab5..d5335d9e72e 100644
--- a/src/tools/html-checker/main.rs
+++ b/src/tools/html-checker/main.rs
@@ -31,7 +31,7 @@ fn check_html_file(file: &Path) -> usize {
         .arg("--mute-id") // this option is useful in case we want to mute more warnings
         .arg("yes")
         .arg("--new-blocklevel-tags")
-        .arg("rustdoc-search,rustdoc-toolbar") // custom elements
+        .arg("rustdoc-search,rustdoc-toolbar,rustdoc-topbar") // custom elements
         .arg("--mute")
         .arg(&to_mute_s)
         .arg(file);
diff --git a/src/tools/jsondoclint/src/item_kind.rs b/src/tools/jsondoclint/src/item_kind.rs
index 51146831efa..e2738636a14 100644
--- a/src/tools/jsondoclint/src/item_kind.rs
+++ b/src/tools/jsondoclint/src/item_kind.rs
@@ -26,6 +26,7 @@ pub(crate) enum Kind {
     AssocType,
     Primitive,
     Keyword,
+    Attribute,
     // Not in ItemKind
     ProcMacro,
 }
@@ -53,6 +54,7 @@ impl Kind {
             ExternType => true,
 
             // FIXME(adotinthevoid): I'm not sure if these are correct
+            Attribute => false,
             Keyword => false,
             ProcAttribute => false,
             ProcDerive => false,
@@ -109,6 +111,7 @@ impl Kind {
             Kind::Primitive => false,
             Kind::Keyword => false,
             Kind::ProcMacro => false,
+            Kind::Attribute => false,
         }
     }
 
@@ -163,6 +166,7 @@ impl Kind {
         match s.kind {
             ItemKind::AssocConst => AssocConst,
             ItemKind::AssocType => AssocType,
+            ItemKind::Attribute => Attribute,
             ItemKind::Constant => Constant,
             ItemKind::Enum => Enum,
             ItemKind::ExternCrate => ExternCrate,
diff --git a/src/tools/linkchecker/Cargo.toml b/src/tools/linkchecker/Cargo.toml
index fb5bff3fe63..f0886e31b24 100644
--- a/src/tools/linkchecker/Cargo.toml
+++ b/src/tools/linkchecker/Cargo.toml
@@ -10,3 +10,4 @@ path = "main.rs"
 [dependencies]
 regex = "1"
 html5ever = "0.29.0"
+urlencoding = "2.1.3"
diff --git a/src/tools/linkchecker/main.rs b/src/tools/linkchecker/main.rs
index 1dc45728c90..e07a0784cdb 100644
--- a/src/tools/linkchecker/main.rs
+++ b/src/tools/linkchecker/main.rs
@@ -232,18 +232,7 @@ enum FileEntry {
 type Cache = HashMap<String, FileEntry>;
 
 fn small_url_encode(s: &str) -> String {
-    s.replace('<', "%3C")
-        .replace('>', "%3E")
-        .replace(' ', "%20")
-        .replace('?', "%3F")
-        .replace('\'', "%27")
-        .replace('&', "%26")
-        .replace(',', "%2C")
-        .replace(':', "%3A")
-        .replace(';', "%3B")
-        .replace('[', "%5B")
-        .replace(']', "%5D")
-        .replace('\"', "%22")
+    urlencoding::encode(s).to_string()
 }
 
 impl Checker {
diff --git a/src/tools/linkchecker/tests/valid/inner/bar.html b/src/tools/linkchecker/tests/valid/inner/bar.html
index 4b500d78b76..6ffda259c40 100644
--- a/src/tools/linkchecker/tests/valid/inner/bar.html
+++ b/src/tools/linkchecker/tests/valid/inner/bar.html
@@ -3,5 +3,8 @@
 
   <h2 id="barfrag">Bar</h2>
 
+  <!-- testing urlecoded anchor link against a non-urlencoded heading IDs -->
+  <h2 id="barfrag-è">Bar</h2>
+
 </body>
 </html>
diff --git a/src/tools/linkchecker/tests/valid/inner/foo.html b/src/tools/linkchecker/tests/valid/inner/foo.html
index 3c6a7483bcd..f30bf718205 100644
--- a/src/tools/linkchecker/tests/valid/inner/foo.html
+++ b/src/tools/linkchecker/tests/valid/inner/foo.html
@@ -8,7 +8,15 @@
   <a href="https://example.com/doesnotexist">external links not validated</a>
   <a href="redir.html#redirfrag">Redirect</a>
 
+  <!-- testing urlecoded anchor link against a non-urlencoded heading IDs -->
+  <a href="#localfrag-%C3%A8"></a>
+  <a href="bar.html#barfrag-%C3%A8"></a>
+  <a href="redir.html#redirfrag-%C3%A8"></a>
+
   <h2 id="localfrag">Local</h2>
 
+  <!-- testing urlecoded anchor link against a non-urlencoded heading IDs -->
+  <h2 id="localfrag-è">Local</h2>
+
 </body>
 </html>
diff --git a/src/tools/linkchecker/tests/valid/inner/redir-target.html b/src/tools/linkchecker/tests/valid/inner/redir-target.html
index bd59884a01e..ac1dec6d5b4 100644
--- a/src/tools/linkchecker/tests/valid/inner/redir-target.html
+++ b/src/tools/linkchecker/tests/valid/inner/redir-target.html
@@ -1,5 +1,8 @@
 <html>
 <body>
   <h2 id="redirfrag">Redir</h2>
+
+  <!-- testing urlecoded anchor link against a non-urlencoded heading IDs -->
+  <h2 id="redirfrag-è">Redir</h2>
 </body>
 </html>
diff --git a/src/tools/lint-docs/src/lib.rs b/src/tools/lint-docs/src/lib.rs
index b33344ca5dd..bc38e931fe5 100644
--- a/src/tools/lint-docs/src/lib.rs
+++ b/src/tools/lint-docs/src/lib.rs
@@ -59,6 +59,8 @@ pub struct LintExtractor<'a> {
     pub rustc_target: &'a str,
     /// The target linker overriding `rustc`'s default
     pub rustc_linker: Option<&'a str>,
+    /// Stage of the compiler that builds the docs (the stage of `rustc_path`).
+    pub build_rustc_stage: u32,
     /// Verbose output.
     pub verbose: bool,
     /// Validate the style and the code example.
@@ -216,14 +218,7 @@ impl<'a> LintExtractor<'a> {
                         if let Some(text) = line.strip_prefix("/// ") {
                             doc_lines.push(text.to_string());
                         } else if let Some(text) = line.strip_prefix("#[doc = \"") {
-                            let escaped = text.strip_suffix("\"]").unwrap();
-                            let mut buf = String::new();
-                            unescape_str(escaped, |_, res| match res {
-                                Ok(c) => buf.push(c),
-                                Err(err) => {
-                                    assert!(!err.is_fatal(), "failed to unescape string literal")
-                                }
-                            });
+                            let buf = parse_doc_string(text);
                             doc_lines.push(buf);
                         } else if line == "///" {
                             doc_lines.push("".to_string());
@@ -234,6 +229,20 @@ impl<'a> LintExtractor<'a> {
                             // Ignore allow of lints (useful for
                             // invalid_rust_codeblocks).
                             continue;
+                        } else if let Some(text) =
+                            line.strip_prefix("#[cfg_attr(not(bootstrap), doc = \"")
+                        {
+                            if self.build_rustc_stage >= 1 {
+                                let buf = parse_doc_string(text);
+                                doc_lines.push(buf);
+                            }
+                        } else if let Some(text) =
+                            line.strip_prefix("#[cfg_attr(bootstrap, doc = \"")
+                        {
+                            if self.build_rustc_stage == 0 {
+                                let buf = parse_doc_string(text);
+                                doc_lines.push(buf);
+                            }
                         } else {
                             let name = lint_name(line).map_err(|e| {
                                 format!(
@@ -580,6 +589,23 @@ impl<'a> LintExtractor<'a> {
     }
 }
 
+/// Parses a doc string that follows `#[doc = "`.
+fn parse_doc_string(text: &str) -> String {
+    let escaped = text.strip_suffix("]").unwrap_or(text);
+    let escaped = escaped.strip_suffix(")").unwrap_or(escaped).strip_suffix("\"");
+    let Some(escaped) = escaped else {
+        panic!("Cannot extract docstring content from {text}");
+    };
+    let mut buf = String::new();
+    unescape_str(escaped, |_, res| match res {
+        Ok(c) => buf.push(c),
+        Err(err) => {
+            assert!(!err.is_fatal(), "failed to unescape string literal")
+        }
+    });
+    buf
+}
+
 /// Adds `Lint`s that have been renamed.
 fn add_renamed_lints(lints: &mut Vec<Lint>) {
     for (level, names) in RENAMES {
diff --git a/src/tools/lint-docs/src/main.rs b/src/tools/lint-docs/src/main.rs
index e377283b1a4..1933ce4d2f1 100644
--- a/src/tools/lint-docs/src/main.rs
+++ b/src/tools/lint-docs/src/main.rs
@@ -25,6 +25,7 @@ fn doit() -> Result<(), Box<dyn Error>> {
     let mut args = std::env::args().skip(1);
     let mut src_path = None;
     let mut out_path = None;
+    let mut build_rustc_stage = None;
     let mut rustc_path = None;
     let mut rustc_target = None;
     let mut rustc_linker = None;
@@ -32,6 +33,14 @@ fn doit() -> Result<(), Box<dyn Error>> {
     let mut validate = false;
     while let Some(arg) = args.next() {
         match arg.as_str() {
+            "--build-rustc-stage" => {
+                build_rustc_stage = match args.next() {
+                    Some(s) => {
+                        Some(s.parse::<u32>().expect("build rustc stage has to be an integer"))
+                    }
+                    None => return Err("--build-rustc-stage requires a value".into()),
+                };
+            }
             "--src" => {
                 src_path = match args.next() {
                     Some(s) => Some(PathBuf::from(s)),
@@ -67,6 +76,9 @@ fn doit() -> Result<(), Box<dyn Error>> {
             s => return Err(format!("unexpected argument `{}`", s).into()),
         }
     }
+    if build_rustc_stage.is_none() {
+        return Err("--build-rustc-stage must be specified to the stage of the compiler that generates the docs".into());
+    }
     if src_path.is_none() {
         return Err("--src must be specified to the directory with the compiler source".into());
     }
@@ -85,6 +97,7 @@ fn doit() -> Result<(), Box<dyn Error>> {
         rustc_path: &rustc_path.unwrap(),
         rustc_target: &rustc_target.unwrap(),
         rustc_linker: rustc_linker.as_deref(),
+        build_rustc_stage: build_rustc_stage.unwrap(),
         verbose,
         validate,
     };
diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml
index c47f9695624..740118bb4a0 100644
--- a/src/tools/miri/.github/workflows/ci.yml
+++ b/src/tools/miri/.github/workflows/ci.yml
@@ -31,16 +31,22 @@ jobs:
             os: ubuntu-24.04-arm
             multiarch: armhf
             gcc_cross: arm-linux-gnueabihf
-          - host_target: riscv64gc-unknown-linux-gnu
-            os: ubuntu-latest
-            multiarch: riscv64
-            gcc_cross: riscv64-linux-gnu
-            qemu: true
-          - host_target: s390x-unknown-linux-gnu
-            os: ubuntu-latest
-            multiarch: s390x
-            gcc_cross: s390x-linux-gnu
-            qemu: true
+          # Disabled due to Ubuntu repo trouble
+          # - host_target: riscv64gc-unknown-linux-gnu
+          #   os: ubuntu-latest
+          #   multiarch: riscv64
+          #   gcc_cross: riscv64-linux-gnu
+          #   qemu: true
+          # - host_target: s390x-unknown-linux-gnu
+          #   os: ubuntu-latest
+          #   multiarch: s390x
+          #   gcc_cross: s390x-linux-gnu
+          #   qemu: true
+          # - host_target: powerpc64le-unknown-linux-gnu
+          #   os: ubuntu-latest
+          #   multiarch: ppc64el
+          #   gcc_cross: powerpc64le-linux-gnu
+          #   qemu: true
           - host_target: aarch64-apple-darwin
             os: macos-latest
           - host_target: i686-pc-windows-msvc
@@ -62,19 +68,13 @@ jobs:
       - name: install multiarch
         if: ${{ matrix.multiarch != '' }}
         run: |
-          # s390x, ppc64el need Ubuntu Ports to be in the mirror list
+          # s390x, ppc64el, riscv64 need Ubuntu Ports to be in the mirror list
           sudo bash -c "echo 'https://ports.ubuntu.com/	priority:4' >> /etc/apt/apt-mirrors.txt"
           # Add architecture
           sudo dpkg --add-architecture ${{ matrix.multiarch }}
           sudo apt update
           # Install needed packages
           sudo apt install $(echo "libatomic1: zlib1g-dev:" | sed 's/:/:${{ matrix.multiarch }}/g')
-      - name: Install rustup on Windows ARM
-        if: ${{ matrix.os == 'windows-11-arm' }}
-        run: |
-            curl -LOs https://static.rust-lang.org/rustup/dist/aarch64-pc-windows-msvc/rustup-init.exe
-            ./rustup-init.exe -y --no-modify-path
-            echo "$USERPROFILE/.cargo/bin" >> "$GITHUB_PATH"
       - uses: ./.github/workflows/setup
         with:
           toolchain_flags: "--host ${{ matrix.host_target }}"
@@ -117,6 +117,41 @@ jobs:
       - name: rustdoc
         run: RUSTDOCFLAGS="-Dwarnings" ./miri doc --document-private-items
 
+  bootstrap:
+    name: bootstrap build
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v4
+      # Deliberately skipping `./.github/workflows/setup` as we do our own setup
+      - name: Add cache for cargo
+        id: cache
+        uses: actions/cache@v4
+        with:
+          path: |
+            # Taken from <https://doc.rust-lang.org/nightly/cargo/guide/cargo-home.html#caching-the-cargo-home-in-ci>.
+            # Cache package/registry information
+            ~/.cargo/registry/index
+            ~/.cargo/registry/cache
+            ~/.cargo/git/db
+            # Cache bootstrap downloads
+            ../rust/build/cache
+          key: cargo-bootstrap-${{ hashFiles('rust-version') }}
+          restore-keys: cargo-bootstrap
+      - name: prepare build environment
+        run: |
+          MIRIDIR=$(pwd)
+          cd ..
+          # Bootstrap needs at least depth 2 to function.
+          git clone https://github.com/rust-lang/rust/ rust --depth 2 --revision $(cat "$MIRIDIR/rust-version")
+          cd rust
+          # Replace the in-tree Miri with the current version.
+          rm src/tools/miri -rf
+          ln -s "$MIRIDIR" src/tools/miri
+      - name: check build
+        run: |
+          cd ../rust # ./x does not seem to like being invoked from elsewhere
+          ./x check miri
+
   coverage:
     name: coverage report
     runs-on: ubuntu-latest
@@ -130,7 +165,7 @@ jobs:
   # 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: [test, style, coverage]
+    needs: [test, style, bootstrap, 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
@@ -169,7 +204,13 @@ jobs:
         run: rustup toolchain install nightly --profile minimal
       - name: Install rustup-toolchain-install-master
         run: cargo install -f rustup-toolchain-install-master
-      - name: Push changes to a branch and create PR
+      # Create a token for the next step so it can create a PR that actually runs CI.
+      - uses: actions/create-github-app-token@v2
+        id: app-token
+        with:
+          app-id: ${{ vars.APP_CLIENT_ID }}
+          private-key: ${{ secrets.APP_PRIVATE_KEY }}
+      - name: pull changes from rustc and create PR
         run: |
           # Make it easier to see what happens.
           set -x
@@ -198,14 +239,14 @@ jobs:
           BRANCH="rustup-$(date -u +%Y-%m-%d)"
           git switch -c $BRANCH
           git push -u origin $BRANCH
-          gh pr create -B master --title 'Automatic Rustup' --body 'Please close and re-open this PR to trigger CI, then enable auto-merge.'
+          gh pr create -B master --title 'Automatic Rustup' --body "Update \`rustc\` to https://github.com/rust-lang/rust/commit/$(cat rust-version)."
         env:
-          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+          GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
 
   cron-fail-notify:
     name: cronjob failure notification
     runs-on: ubuntu-latest
-    needs: [test, style, coverage]
+    needs: [test, style, bootstrap, 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 7d78fdddbad..073ad267476 100644
--- a/src/tools/miri/CONTRIBUTING.md
+++ b/src/tools/miri/CONTRIBUTING.md
@@ -255,6 +255,12 @@ when installing the Miri toolchain. Alternatively, set the `RUSTUP_TOOLCHAIN` en
 
 [`etc/rust_analyzer_helix.toml`]: https://github.com/rust-lang/miri/blob/master/etc/rust_analyzer_helix.toml
 
+### Zed
+
+Copy [`etc/rust_analyzer_zed.json`] to `.zed/settings.json` in the project root directory.
+
+[`etc/rust_analyzer_zed.json`]: https://github.com/rust-lang/miri/blob/master/etc/rust_analyzer_zed.json
+
 ### Advanced configuration
 
 If you are building Miri with a locally built rustc, set
diff --git a/src/tools/miri/Cargo.lock b/src/tools/miri/Cargo.lock
index b46f0f83420..8792d90ac53 100644
--- a/src/tools/miri/Cargo.lock
+++ b/src/tools/miri/Cargo.lock
@@ -363,9 +363,9 @@ dependencies = [
 
 [[package]]
 name = "cxx"
-version = "1.0.161"
+version = "1.0.173"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a3523cc02ad831111491dd64b27ad999f1ae189986728e477604e61b81f828df"
+checksum = "6c64ed3da1c337cbaae7223cdcff8b4dddf698d188cd3eaddd1116f6b0295950"
 dependencies = [
  "cc",
  "cxxbridge-cmd",
@@ -377,9 +377,9 @@ dependencies = [
 
 [[package]]
 name = "cxx-build"
-version = "1.0.161"
+version = "1.0.173"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "212b754247a6f07b10fa626628c157593f0abf640a3dd04cce2760eca970f909"
+checksum = "ae0a26a75a05551f5ae3d75b3557543d06682284eaa7419113162d602cb45766"
 dependencies = [
  "cc",
  "codespan-reporting",
@@ -392,9 +392,9 @@ dependencies = [
 
 [[package]]
 name = "cxxbridge-cmd"
-version = "1.0.161"
+version = "1.0.173"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f426a20413ec2e742520ba6837c9324b55ffac24ead47491a6e29f933c5b135a"
+checksum = "952d408b6002b7db4b36da07c682a9cbb34f2db0efa03e976ae50a388414e16c"
 dependencies = [
  "clap",
  "codespan-reporting",
@@ -406,15 +406,15 @@ dependencies = [
 
 [[package]]
 name = "cxxbridge-flags"
-version = "1.0.161"
+version = "1.0.173"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a258b6069020b4e5da6415df94a50ee4f586a6c38b037a180e940a43d06a070d"
+checksum = "ccbd201b471c75c6abb6321cace706d1982d270e308b891c11a3262d320f5265"
 
 [[package]]
 name = "cxxbridge-macro"
-version = "1.0.161"
+version = "1.0.173"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8dec184b52be5008d6eaf7e62fc1802caf1ad1227d11b3b7df2c409c7ffc3f4"
+checksum = "2bea8b915bbc4cb4288f242aa7ca18b23ecc6965e4d6e7c1b07905e3fe2e0c41"
 dependencies = [
  "indexmap",
  "proc-macro2",
@@ -501,9 +501,9 @@ checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
 
 [[package]]
 name = "foldhash"
-version = "0.1.5"
+version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
+checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
 
 [[package]]
 name = "form_urlencoded"
@@ -1569,9 +1569,9 @@ dependencies = [
 
 [[package]]
 name = "tracing-subscriber"
-version = "0.3.19"
+version = "0.3.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008"
+checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5"
 dependencies = [
  "sharded-slab",
  "thread_local",
diff --git a/src/tools/miri/Cargo.toml b/src/tools/miri/Cargo.toml
index 91dadf78a2f..12123c260da 100644
--- a/src/tools/miri/Cargo.toml
+++ b/src/tools/miri/Cargo.toml
@@ -39,7 +39,7 @@ features = ['unprefixed_malloc_on_supported_platforms']
 [target.'cfg(unix)'.dependencies]
 libc = "0.2"
 # native-lib dependencies
-libffi = { version = "4.0.0", optional = true }
+libffi = { version = "4.1.1", optional = true }
 libloading = { version = "0.8", optional = true }
 serde = { version = "1.0.219", features = ["derive"], optional = true }
 
@@ -70,7 +70,7 @@ harness = false
 
 [features]
 default = ["stack-cache", "native-lib"]
-genmc = ["dep:genmc-sys"] # this enables a GPL dependency!
+genmc = ["dep:genmc-sys"]
 stack-cache = []
 stack-cache-consistency-check = ["stack-cache"]
 tracing = ["serde_json"]
@@ -78,6 +78,7 @@ native-lib = ["dep:libffi", "dep:libloading", "dep:capstone", "dep:ipc-channel",
 
 [lints.rust.unexpected_cfgs]
 level = "warn"
+check-cfg = ['cfg(bootstrap)']
 
 # Be aware that this file is inside a workspace when used via the
 # submodule in the rustc repo. That means there are many cargo features
diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md
index 7ccd27d7b83..a5214e213b3 100644
--- a/src/tools/miri/README.md
+++ b/src/tools/miri/README.md
@@ -220,7 +220,7 @@ degree documented below):
   - `solaris` / `illumos`: maintained by @devnexen. Supports the entire test suite.
   - `freebsd`: maintained by @YohDeadfall and @LorrensP-2158466. Supports the entire test suite.
   - `android`: **maintainer wanted**. Support very incomplete, but a basic "hello world" works.
-  - `wasi`: **maintainer wanted**. Support very incomplete, not even standard output works, but an empty `main` function works.
+  - `wasi`: **maintainer wanted**. Support very incomplete, but a basic "hello world" works.
 - For targets on other operating systems, Miri might fail before even reaching the `main` function.
 
 However, even for targets that we do support, the degree of support for accessing platform APIs
@@ -319,8 +319,14 @@ environment variable. We first document the most relevant and most commonly used
   Can be used without a value; in that case the range defaults to `0..64`.
 * `-Zmiri-many-seeds-keep-going` tells Miri to really try all the seeds in the given range, even if
   a failing seed has already been found. This is useful to determine which fraction of seeds fails.
+* `-Zmiri-max-extra-rounding-error` tells Miri to always apply the maximum error to float operations
+  that do not have a guaranteed precision. The sign of the error is still non-deterministic.
 * `-Zmiri-no-extra-rounding-error` stops Miri from adding extra rounding errors to float operations
   that do not have a guaranteed precision.
+* `-Zmiri-no-short-fd-operations` stops Miri from artificially forcing `read`/`write` operations
+  to only process a part of their buffer. Note that whenever Miri uses host operations to
+  implement `read`/`write` (e.g. for file-backed file descriptors), the host system can still
+  introduce short reads/writes.
 * `-Zmiri-num-cpus` states the number of available CPUs to be reported by miri. By default, the
   number of available CPUs is `1`. Note that this flag does not affect how miri handles threads in
   any way.
@@ -600,6 +606,7 @@ Definite bugs found:
 * [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)
 * [Mockall reading uninitialized memory when mocking `std::io::Read::read`, even if all expectations are satisfied](https://github.com/asomers/mockall/issues/647) (caught by Miri running Tokio's test suite)
 * [`ReentrantLock` not correctly dealing with reuse of addresses for TLS storage of different threads](https://github.com/rust-lang/rust/pull/141248)
+* [Rare Deadlock in the thread (un)parking example code](https://github.com/rust-lang/rust/issues/145816)
 
 Violations of [Stacked Borrows] found that are likely bugs (but Stacked Borrows is currently just an experiment):
 
diff --git a/src/tools/miri/cargo-miri/Cargo.lock b/src/tools/miri/cargo-miri/Cargo.lock
index b3f5dafab64..ea9c04a3cb5 100644
--- a/src/tools/miri/cargo-miri/Cargo.lock
+++ b/src/tools/miri/cargo-miri/Cargo.lock
@@ -429,9 +429,9 @@ dependencies = [
 
 [[package]]
 name = "rustc-build-sysroot"
-version = "0.5.9"
+version = "0.5.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fdb13874a0e55baf4ac3d49d38206aecb31a55b75d6c4d04fd850b53942c8cc8"
+checksum = "dd41ead66a69880951b2f7df3139db401d44451b4da123344d27eaa791b89c95"
 dependencies = [
  "anyhow",
  "rustc_version",
diff --git a/src/tools/miri/cargo-miri/Cargo.toml b/src/tools/miri/cargo-miri/Cargo.toml
index 77cb1df8e74..64b56ea114e 100644
--- a/src/tools/miri/cargo-miri/Cargo.toml
+++ b/src/tools/miri/cargo-miri/Cargo.toml
@@ -18,7 +18,7 @@ directories = "6"
 rustc_version = "0.4"
 serde_json = "1.0.40"
 cargo_metadata = "0.21"
-rustc-build-sysroot = "0.5.8"
+rustc-build-sysroot = "0.5.10"
 
 # Enable some feature flags that dev-dependencies need but dependencies
 # do not.  This makes `./miri install` after `./miri build` faster.
diff --git a/src/tools/miri/cargo-miri/src/phases.rs b/src/tools/miri/cargo-miri/src/phases.rs
index efb9053f69a..0716f4add9d 100644
--- a/src/tools/miri/cargo-miri/src/phases.rs
+++ b/src/tools/miri/cargo-miri/src/phases.rs
@@ -65,16 +65,6 @@ fn forward_patched_extern_arg(args: &mut impl Iterator<Item = String>, cmd: &mut
 }
 
 pub fn phase_cargo_miri(mut args: impl Iterator<Item = String>) {
-    // Check for version and help flags even when invoked as `cargo-miri`.
-    if has_arg_flag("--help") || has_arg_flag("-h") {
-        show_help();
-        return;
-    }
-    if has_arg_flag("--version") || has_arg_flag("-V") {
-        show_version();
-        return;
-    }
-
     // Require a subcommand before any flags.
     // We cannot know which of those flags take arguments and which do not,
     // so we cannot detect subcommands later.
@@ -85,11 +75,36 @@ pub fn phase_cargo_miri(mut args: impl Iterator<Item = String>) {
         "setup" => MiriCommand::Setup,
         "test" | "t" | "run" | "r" | "nextest" => MiriCommand::Forward(subcommand),
         "clean" => MiriCommand::Clean,
-        _ =>
+        _ => {
+            // Check for version and help flags.
+            if has_arg_flag("--help") || has_arg_flag("-h") {
+                show_help();
+                return;
+            }
+            if has_arg_flag("--version") || has_arg_flag("-V") {
+                show_version();
+                return;
+            }
             show_error!(
                 "`cargo miri` supports the following subcommands: `run`, `test`, `nextest`, `clean`, and `setup`."
-            ),
+            )
+        }
     };
+    if has_arg_flag("--help") || has_arg_flag("-h") {
+        match subcommand {
+            MiriCommand::Forward(verb) => {
+                println!("`cargo miri {verb}` supports the same flags as `cargo {verb}`:\n");
+                let mut cmd = cargo();
+                cmd.arg(verb);
+                cmd.arg("--help");
+                exec(cmd);
+            }
+            _ => {
+                show_help();
+                return;
+            }
+        }
+    }
     let verbose = num_arg_flag("-v") + num_arg_flag("--verbose");
     let quiet = has_arg_flag("-q") || has_arg_flag("--quiet");
 
diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh
index b66530e77b8..bcc110f648b 100755
--- a/src/tools/miri/ci/ci.sh
+++ b/src/tools/miri/ci/ci.sh
@@ -137,6 +137,7 @@ function run_tests_minimal {
 
 # In particular, fully cover all tier 1 targets.
 # We also want to run the many-seeds tests on all tier 1 targets.
+# We run GC_STRESS only once for each tier 1 OS.
 case $HOST_TARGET in
   x86_64-unknown-linux-gnu)
     # Host
@@ -147,29 +148,31 @@ case $HOST_TARGET in
     ;;
   i686-unknown-linux-gnu)
     # Host
-    # Without GC_STRESS as this is a slow runner.
     MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests
     # Partially supported targets (tier 2)
     BASIC="empty_main integer heap_alloc libc-mem vec string btreemap" # ensures we have the basics: pre-main code, system allocator
     UNIX="hello panic/panic panic/unwind concurrency/simple atomic libc-mem libc-misc libc-random env num_cpus" # the things that are very similar across all Unixes, and hence easily supported there
     TEST_TARGET=aarch64-linux-android  run_tests_minimal $BASIC $UNIX time hashmap random thread sync concurrency epoll eventfd
-    TEST_TARGET=wasm32-wasip2          run_tests_minimal $BASIC wasm
+    TEST_TARGET=wasm32-wasip2          run_tests_minimal $BASIC hello 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
     ;;
   aarch64-unknown-linux-gnu)
     # Host
-    GC_STRESS=1 MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests
+    MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests
     # Extra tier 2
     MANY_SEEDS=16 TEST_TARGET=arm-unknown-linux-gnueabi run_tests # 32bit ARM
     MANY_SEEDS=16 TEST_TARGET=aarch64-pc-windows-gnullvm run_tests # gnullvm ABI
     MANY_SEEDS=16 TEST_TARGET=s390x-unknown-linux-gnu run_tests # big-endian architecture of choice
-    # Custom target JSON file
-    TEST_TARGET=tests/x86_64-unknown-kernel.json MIRI_NO_STD=1 run_tests_minimal no_std
+    # Not officially supported tier 2
+    MANY_SEEDS=16 TEST_TARGET=x86_64-unknown-freebsd run_tests
+    MANY_SEEDS=16 TEST_TARGET=i686-unknown-freebsd run_tests
     ;;
   armv7-unknown-linux-gnueabihf)
     # Host
-    GC_STRESS=1 MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests
+    MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests
+    # Custom target JSON file
+    TEST_TARGET=tests/x86_64-unknown-kernel.json MIRI_NO_STD=1 run_tests_minimal no_std
     ;;
   aarch64-apple-darwin)
     # Host
@@ -181,12 +184,9 @@ case $HOST_TARGET in
     MANY_SEEDS=16 TEST_TARGET=mips-unknown-linux-gnu run_tests # a 32bit big-endian target, and also a target without 64bit atomics
     MANY_SEEDS=16 TEST_TARGET=x86_64-unknown-illumos run_tests
     MANY_SEEDS=16 TEST_TARGET=x86_64-pc-solaris run_tests
-    MANY_SEEDS=16 TEST_TARGET=x86_64-unknown-freebsd run_tests
-    MANY_SEEDS=16 TEST_TARGET=i686-unknown-freebsd run_tests
     ;;
   i686-pc-windows-msvc)
     # Host
-    # Without GC_STRESS as this is a very slow runner.
     MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 run_tests
     # Extra tier 1
     # We really want to ensure a Linux target works on a Windows host,
@@ -195,8 +195,7 @@ case $HOST_TARGET in
     ;;
   aarch64-pc-windows-msvc)
     # Host
-    # Without GC_STRESS as this is a very slow runner.
-    MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests
+    GC_STRESS=1 MIR_OPT=1 MANY_SEEDS=64 TEST_BENCH=1 CARGO_MIRI_ENV=1 run_tests
     # Extra tier 1
     MANY_SEEDS=64 TEST_TARGET=i686-unknown-linux-gnu run_tests
     ;;
diff --git a/src/tools/miri/doc/genmc.md b/src/tools/miri/doc/genmc.md
index 5aabe90b5da..44e11dcbec4 100644
--- a/src/tools/miri/doc/genmc.md
+++ b/src/tools/miri/doc/genmc.md
@@ -9,9 +9,7 @@ Miri-GenMC integrates that model checker into Miri.
 
 ## Usage
 
-**IMPORTANT: The license of GenMC and thus the `genmc-sys` crate in the Miri repo is currently "GPL-3.0-or-later", so a binary produced with the `genmc` feature is subject to the requirements of the GPL. As long as that remains the case, the `genmc` feature of Miri is OFF-BY-DEFAULT and must be OFF for all Miri releases.**
-
-For testing/developing Miri-GenMC (while keeping in mind the licensing issues):
+For testing/developing Miri-GenMC:
 - clone the Miri repo.
 - build Miri-GenMC with `./miri build --features=genmc`.
 - OR: install Miri-GenMC in the current system with `./miri install --features=genmc`
@@ -21,7 +19,30 @@ Basic usage:
 MIRIFLAGS="-Zmiri-genmc" cargo miri run
 ```
 
-<!-- FIXME(genmc): explain options. -->
+Note that `cargo miri test` in GenMC mode is currently not supported.
+
+### Supported Parameters
+
+- `-Zmiri-genmc`: Enable GenMC mode (not required if any other GenMC options are used).
+- `-Zmiri-genmc-estimate`: This enables estimation of the concurrent execution space and verification time, before running the full verification. This should help users detect when their program is too complex to fully verify in a reasonable time. This will explore enough executions to make a good estimation, but at least 10 and at most `estimation-max` executions.
+- `-Zmiri-genmc-estimation-max={MAX_ITERATIONS}`: Set the maximum number of executions that will be explored during estimation (default: 1000).
+- `-Zmiri-genmc-print-exec-graphs={none,explored,blocked,all}`: Make GenMC print the execution graph of the program after every explored, every blocked, or after every execution (default: None).
+- `-Zmiri-genmc-print-exec-graphs`: Shorthand for suffix `=explored`.
+- `-Zmiri-genmc-print-genmc-output`: Print the output that GenMC provides. NOTE: this output is quite verbose and the events in the printed execution graph are hard to map back to the Rust code location they originate from.
+- `-Zmiri-genmc-log=LOG_LEVEL`: Change the log level for GenMC. Default: `warning`.
+  - `quiet`:    Disable logging.
+  - `error`:    Print errors.
+  - `warning`:  Print errors and warnings.
+  - `tip`:      Print errors, warnings and tips.
+  - If Miri is built with debug assertions, there are additional log levels available (downgraded to `tip` without debug assertions):
+    - `debug1`:   Print revisits considered by GenMC.
+    - `debug2`:   Print the execution graph after every memory access.
+    - `debug3`:   Print reads-from values considered by GenMC.
+- `-Zmiri-genmc-verbose`: Show more information, such as estimated number of executions, and time taken for verification.
+
+#### Regular Miri parameters useful for GenMC mode
+
+- `-Zmiri-disable-weak-memory-emulation`: Disable any weak memory effects (effectively upgrading all atomic orderings in the program to `SeqCst`). This option may reduce the number of explored program executions, but any bugs related to weak memory effects will be missed. This option can help determine if an error is caused by weak memory effects (i.e., if it disappears with this option enabled).
 
 <!-- FIXME(genmc): explain Miri-GenMC specific functions. -->
 
@@ -57,6 +78,15 @@ The process for obtaining them is as follows:
   If you place this directory inside the Miri folder, it is recommended to call it `genmc-src` as that tells `./miri fmt` to avoid
   formatting the Rust files inside that folder.
 
+### Formatting the C++ code
+
+For formatting the C++ code we provide a `.clang-format` file in the `genmc-sys` directory.
+With `clang-format` installed, run this command to format the c++ files (replace the `-i` with `--dry-run` to just see the changes.):
+```
+find ./genmc-sys/cpp/ -name "*.cpp" -o -name "*.hpp" | xargs clang-format --style=file:"./genmc-sys/.clang-format" -i
+```
+NOTE: this is currently not done automatically on pull requests to Miri.
+
 <!-- FIXME(genmc): explain how submitting code to GenMC should be handled. -->
 
 <!-- FIXME(genmc): explain development. -->
diff --git a/src/tools/miri/doc/img/perfetto_aggregate_statistics.png b/src/tools/miri/doc/img/perfetto_aggregate_statistics.png
new file mode 100644
index 00000000000..d4fd3826f47
--- /dev/null
+++ b/src/tools/miri/doc/img/perfetto_aggregate_statistics.png
Binary files differdiff --git a/src/tools/miri/doc/img/perfetto_aggregate_statistics_sql.png b/src/tools/miri/doc/img/perfetto_aggregate_statistics_sql.png
new file mode 100644
index 00000000000..bda92d3885a
--- /dev/null
+++ b/src/tools/miri/doc/img/perfetto_aggregate_statistics_sql.png
Binary files differdiff --git a/src/tools/miri/doc/img/perfetto_span.png b/src/tools/miri/doc/img/perfetto_span.png
new file mode 100644
index 00000000000..1a7184f22ae
--- /dev/null
+++ b/src/tools/miri/doc/img/perfetto_span.png
Binary files differdiff --git a/src/tools/miri/doc/img/perfetto_subname_statistics.png b/src/tools/miri/doc/img/perfetto_subname_statistics.png
new file mode 100644
index 00000000000..8c86b07e925
--- /dev/null
+++ b/src/tools/miri/doc/img/perfetto_subname_statistics.png
Binary files differdiff --git a/src/tools/miri/doc/img/perfetto_timeline.png b/src/tools/miri/doc/img/perfetto_timeline.png
new file mode 100644
index 00000000000..49f8a1fac1d
--- /dev/null
+++ b/src/tools/miri/doc/img/perfetto_timeline.png
Binary files differdiff --git a/src/tools/miri/doc/img/perfetto_visualize_argument_values.png b/src/tools/miri/doc/img/perfetto_visualize_argument_values.png
new file mode 100644
index 00000000000..1dcbacaf9cb
--- /dev/null
+++ b/src/tools/miri/doc/img/perfetto_visualize_argument_values.png
Binary files differdiff --git a/src/tools/miri/doc/img/perfetto_visualize_argument_values_misbehaving.png b/src/tools/miri/doc/img/perfetto_visualize_argument_values_misbehaving.png
new file mode 100644
index 00000000000..beeba8a4a3a
--- /dev/null
+++ b/src/tools/miri/doc/img/perfetto_visualize_argument_values_misbehaving.png
Binary files differdiff --git a/src/tools/miri/doc/img/perfetto_visualize_argument_values_sql.png b/src/tools/miri/doc/img/perfetto_visualize_argument_values_sql.png
new file mode 100644
index 00000000000..c7b163b0a57
--- /dev/null
+++ b/src/tools/miri/doc/img/perfetto_visualize_argument_values_sql.png
Binary files differdiff --git a/src/tools/miri/doc/tracing.md b/src/tools/miri/doc/tracing.md
new file mode 100644
index 00000000000..d7114af947d
--- /dev/null
+++ b/src/tools/miri/doc/tracing.md
@@ -0,0 +1,292 @@
+# Documentation for the tracing infrastructure in Miri
+
+Miri can be traced to understand how much time is spent in its various components (e.g. borrow tracker, data race checker, etc.). When tracing is enabled, running Miri will create a `.json` trace file that can be opened and analyzed in [Perfetto](https://ui.perfetto.dev/). For any questions regarding this documentation you may contact [Stypox](https://rust-lang.zulipchat.com/#narrow/dm/627563-Stypox) on Zulip.
+
+## Obtaining a trace file
+
+### From the Miri codebase
+
+All of the tracing functionality in Miri is gated by the `"tracing"` feature flag to ensure it does not create any overhead when unneeded. To compile Miri with this feature enabled, you can pass `--features=tracing` to `./miri`. Then, to make running Miri actually produce a trace file, you also need to set the `MIRI_TRACING` environment variable. For example:
+
+```sh
+MIRI_TRACING=1 ./miri run --features=tracing ./tests/pass/hello.rs
+```
+
+### From the rustc codebase
+
+If you are building Miri from within the rustc tree, you need to enable the `"tracing"` feature by adding this line to `bootstrap.toml`:
+
+```toml
+build.tool.miri.features = ["tracing"]
+```
+
+And then you could run the following:
+
+```sh
+MIRI_TRACING=1 ./x.py run miri --stage 1 --args ./src/tools/miri/tests/pass/hello.rs
+```
+
+### The trace file
+
+After running Miri with tracing enabled you will get a `.json` trace file that contains a list of all events and spans that occurred throughout the execution. The file follows [this format](https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview).
+
+## Analyzing a trace file
+
+To analyze traces you can use [Perfetto UI](https://ui.perfetto.dev/), a trace analyzer made by Google that was originally a part of the Chrome browser. Just open Perfetto and drag and drop the `.json` file there. Official documentation for the controls in the UI can be found [here](https://perfetto.dev/docs/visualization/perfetto-ui).
+
+### The timeline
+
+You will see the boxes "Global Legacy Events" and "Process 1" on the left of the workspace: after clicking on either of them their timeline will expand and you will be able to zoom in and look at individual spans (and events).
+
+- "Process 1" contains tracing spans for the various components of Miri, all in a single timeline line (e.g. borrow tracker, data race checker, etc.)
+- "Global Legacy Events" contains auxiliary spans on two separate lines that allow understanding what code is being executed at any point in time:
+    - "frame": what is the current stack frame in the interpreted program
+    - "step": what statement/terminator in the MIR of the interpreted program is being executed
+
+Spans are represented as colored boxes in the timeline, while instantaneous events are represented by tiny arrows. (Events exist because rustc and Miri also use the `tracing` crate for debug logging, and those logs turn into events in the trace.)
+
+![](./img/perfetto_timeline.png)
+
+### Span/event data
+
+You can click on a span or an event to get more information about it, including some arguments that were passed when the span/event was entered/fired. In the following screenshot you can see the details of a "layouting" span that was generated by the following line in Miri's code:
+
+```rust
+let _trace = enter_trace_span!(M, layouting::fn_abi_of_instance, ?instance, ?extra_args);
+```
+
+![](./img/perfetto_span.png)
+
+### SQL tables
+
+Perfetto supports querying the span/event database using SQL queries (see the [docs](https://perfetto.dev/docs/analysis/perfetto-sql-syntax)). Just type `:` in the search bar at the top to enter SQL mode, and then you will be able to enter SQL queries there. The relevant SQL tables are:
+- `slices`: contains all spans and events; events can be distinguished from spans since their `dur` is 0. Relevant columns are:
+    - `id`: a unique primary-key ID for the span (assigned by Perfetto, not present in the trace file)
+    - `ts` and `dur`: the beginning and duration of the span, in nanoseconds
+    - `name`: the name of the span
+    - `parent_id`: the parent span ID, or null if there is no parent (assigned by Perfetto based on the timing at which spans occur, i.e. two nested spans must be one the child of the other)
+    - `arg_set_id`: a foreign key into the table of arguments (1-to-N)
+- `args`: contains all of the arguments of the various events/spans. Relevant columns are:
+    - `arg_set_id`: the key used to join the slices and args tables
+    - `key`: the name of the argument prepended with "args."
+    - `display_value`: the value of the argument
+
+Some useful queries are provided in the following sections.
+
+### Enhancing the timeline
+
+On the "Process 1" timeline line there are some spans with the same name, that are actually generated from different places in Miri's code. In those cases the span name indicates the component that was invoked (e.g. the data race checker), but not the specific function that was run. To inspect the specific function, we store a "subname" in an argument with the same name as the span, which unfortunately can be seen only after clicking on the span.
+
+To make it quicker to look at subnames, you can add a new timeline line that specifically shows the subnames for spans with a specific name. To do so:
+1. select any span with the name you care about (call this name `$NAME`)
+2. click on the dropdown highlighted in blue next on the argument with name `$NAME` (or `args.$NAME`)
+3. click on "Visualize argument values"
+4. a new timeline line will appear with only spans originally named `$NAME`, but now with the subname displayed instead
+
+The following screenshot shows the 4 steps for spans named "data_race":
+
+![](./img/perfetto_visualize_argument_values.png)
+
+### Visualizing which "frame" or "step" is being executed
+
+Unfortunately the instructions in [Enhancing the timeline](#enhancing-the-timeline) only work well with spans under "Process 1", but misbehave with spans under "Global Legacy Events" (see the screenshot below). This might be a bug in Perfetto, but nevertheless a workaround is available:
+
+1. click on the search bar at the top and write `:` to enter SQL mode
+2. copy-paste the following SQL, replace "SPAN_NAME" at the end with either "frame" or "step" (i.e. one of the two span names under "Global Legacy Events"), and press Enter to execute it:
+    ```sql
+    select slices.id, ts, dur, track_id, category, args.string_value as name, depth, stack_id, parent_stack_id, parent_id, slices.arg_set_id, thread_ts, thread_instruction_count, thread_instruction_delta, cat, slice_id
+    from slices inner join args using (arg_set_id)
+    where args.key = "args." || name and name = "SPAN_NAME"
+    ```
+3. at the top-right of the box at the bottom, click on "Show debug track"
+4. press on "Show" in the popup that just appeared
+5. a new debug track will appear with the names of steps or frames
+
+What the SQL does is to select only spans with the name "SPAN_NAME" and keep all of the span fields untouched, except for the name which is replaced with the subname. As explained in [Enhancing the timeline](#enhancing-the-timeline), remember that the subname is stored in an argument with the same name as the span.
+
+![](./img/perfetto_visualize_argument_values_sql.png)
+
+<img src="./img/perfetto_visualize_argument_values_misbehaving.png" width="300px">
+
+### Compute aggregate statistics
+
+The simplest way to get aggregate statistics about a time range is to:
+
+1. select a time range by drag-clicking along a trace line
+2. click on the "Current Selection" tab at the bottom if it's not already open
+3. see various tables/visualizations of how much time is spent in each span by clicking on "Slices", "Pivot Table" or "Slice Flamegraph"
+
+Note that the numbers shown in the "Slices" and "Pivot Table" tabs also include nested spans, so they cannot be used to compute statistics such as "X% of time is spent in spans named Y" because two spans named Y might be nested and their duration would be counted twice. For such statistics use the method in [Compute aggregate statistics (enhanced)](#compute-aggregate-statistics-enhanced).
+
+![](./img/perfetto_aggregate_statistics.png)
+
+### Compute aggregate statistics (enhanced)
+
+The following (long but not complicated) query can be used to find out how much time is spent in spans (grouped by their name). Only spans without a parent are considered towards the computations (see `where parent_id is null`): so for example if `validate_operand` in turn calls `layouting` (which generates a nested/child span), only the `validate_operand` statistics are increased. This query also excludes auxiliary spans (see `name != "frame" and name != "step"`).
+
+Note that this query does not allow selecting a time range, but that can be done by adding a condition, e.g. `ts + dur > MIN_T and ts < MAX_T` would match only spans that intersect the range `(MIN_T, MAX_T)`. Remember that the time unit is nanoseconds.
+
+```sql
+select "TOTAL PROGRAM DURATION" as name, count(*), max(ts + dur) as "sum(dur)", 100.0 as "%", null as "min(dur)", null as "max(dur)", null as "avg(dur)", null as "stddev(dur)"
+from slices
+
+union select "TOTAL OVER ALL SPANS (excluding events)" as name, count(*), sum(dur), cast(cast(sum(dur) as float) / (select max(ts + dur) from slices) * 1000 as int) / 10.0 as "%", min(dur), max(dur), cast(avg(dur) as int) as "avg(dur)", cast(sqrt(avg(dur*dur)-avg(dur)*avg(dur)) as int) as "stddev(dur)"
+from slices
+where parent_id is null and name != "frame" and name != "step" and dur > 0
+
+union select name, count(*), sum(dur), cast(cast(sum(dur) as float) / (select max(ts + dur) from slices) * 1000 as int) / 10.0 as "%", min(dur), max(dur), cast(avg(dur) as int) as "avg(dur)", cast(sqrt(avg(dur*dur)-avg(dur)*avg(dur)) as int) as "stddev(dur)"
+from slices
+where parent_id is null and name != "frame" and name != "step"
+group by name
+order by sum(dur) desc, count(*) desc
+```
+
+This is the kind of table you would get out:
+
+![](./img/perfetto_aggregate_statistics_sql.png)
+
+### Statistics about subnames of a span
+
+Use the following SQL to see statistics about the subnames of spans with the same name (replace "SPAN_NAME" with the name of the span you want to see subname statistics of):
+
+```sql
+select args.string_value as name, count(*), sum(dur), min(dur), max(dur), cast(avg(dur) as int) as "avg(dur)", cast(sqrt(avg(dur*dur)-avg(dur)*avg(dur)) as int) as "stddev(dur)"
+from slices inner join args using (arg_set_id)
+where args.key = "args." || name and name = "SPAN_NAME"
+group by args.string_value
+order by count(*) desc
+```
+
+For example, this is the table of how much time is spent in each borrow tracker function: 
+
+![](./img/perfetto_subname_statistics.png)
+
+### Finding long periods of time without any tracing
+
+The following SQL finds the longest periods of time where time is being spent, with the ability to click on IDs in the table of results to quickly reach the corresponding place. This can be useful to spot things that use up a significant amount of time but that are not yet covered by tracing calls.
+
+```sql
+with ordered as (
+    select s1.*, row_number() over (order by s1.ts) as rn
+    from slices as s1
+    where s1.parent_id is null and s1.dur > 0 and s1.name != "frame" and s1.name != "step"
+)
+select a.ts+a.dur as ts, b.ts-a.ts-a.dur as dur, a.id, a.track_id, a.category, a.depth, a.stack_id, a.parent_stack_id, a.parent_id, a.arg_set_id, a.thread_ts, a.thread_instruction_count, a.thread_instruction_delta, a.cat, a.slice_id, "empty" as name
+from ordered as a inner join ordered as b on a.rn=b.rn-1
+order by b.ts-a.ts-a.dur desc
+```
+
+### Saving Perfetto's state as a preset
+
+Unfortunately Perfetto does not seem to support saving the UI state as a preset that can be used to repeat the same analysis on multiple traces. You have to click through the various menus or run the various SQL queries every time to setup the UI as you want.
+
+## Adding new tracing calls to the code
+
+### The "tracing" feature
+
+Miri is highly interconnected with `rustc_const_eval`, and therefore collecting proper trace data about Miri also involves adding some tracing calls within `rustc_const_eval`'s codebase. As explained in [Obtaining a trace file](#obtaining-a-trace-file), tracing calls are disabled (and optimized out) when Miri's "tracing" feature is not enabled. However, while it is possible to check for the feature from Miri's codebase, it's not possible to do so from `rustc_const_eval` (since it's a separate crate, and it's even in a precompiled `.rlib` in case of out-of-tree builds).
+
+The solution to make it possible to check whether tracing is enabled at compile time even in `rustc_const_eval` was to add a function with this signature to the `Machine` trait:
+```rust
+fn enter_trace_span(span: impl FnOnce() -> tracing::Span) -> impl EnteredTraceSpan
+```
+
+where `EnteredTraceSpan` is just a marker trait implemented by `()` and `tracing::span::EnteredSpan`. This function returns `()` by default (without calling the `span` closure), except in `MiriMachine` where if tracing is enabled it will return `span().entered()`.
+
+The code in `rustc_const_eval` calls this function when it wants to do tracing, and the compiler will (hopefully) optimize out tracing calls when tracing is disabled.
+
+### The `enter_trace_span!()` macro
+
+To add tracing to a section of code in Miri or in `rustc_const_eval`, you can use the `enter_trace_span!()` macro, which takes care of the details explained in [The "tracing" feature](#the-tracing-feature).
+
+The `enter_trace_span!()` macro accepts the same syntax as `tracing::span!()` ([documentation](https://docs.rs/tracing/latest/tracing/#using-the-macros)) except for a few customizations, and returns an already entered trace span. The returned value is a drop guard that will exit the span when dropped, so **make sure to give it a proper scope** by storing it in a variable like this:
+
+```rust
+let _trace = enter_trace_span!("My span");
+```
+
+When calling this macro from `rustc_const_eval` you need to pass a type implementing the `Machine` trait as the first argument (since it will be used to call `Machine::enter_trace_span()`). This is usually available in various parts of `rustc_const_eval` under the name `M`, since most of `rustc_const_eval`'s code is `Machine`-agnostic.
+
+```rust
+let _trace = enter_trace_span!("My span");    // from Miri
+let _trace = enter_trace_span!(M, "My span"); // from rustc_const_eval
+```
+
+You can make sense of the syntaxes explained below also by looking at this Perfetto screenshot from [Span/event data](#spanevent-data).
+
+![](./img/perfetto_span.png)
+
+### Syntax accepted by `tracing::span!()`
+
+The full documentation for the `tracing::span!()` syntax can be found [here](https://docs.rs/tracing/latest/tracing/#using-the-macros) under "Using the Macros". A few possibly confusing syntaxes are listed here:
+```rust
+// logs a span named "hello" with a field named "arg" of value 42 (works only because
+// 42 implements the tracing::Value trait, otherwise use one of the options below)
+let _trace = enter_trace_span!(M, "hello", arg = 42);
+// logs a field called "my_display_var" using the Display implementation
+let _trace = enter_trace_span!(M, "hello", %my_display_var);
+// logs a field called "my_debug_var" using the Debug implementation
+let _trace = enter_trace_span!(M, "hello", ?my_debug_var);
+```
+
+### `NAME::SUBNAME` syntax
+
+In addition to the syntax accepted by `tracing::span!()`, the `enter_trace_span!()` macro optionally allows passing the span name (i.e. the first macro argument) in the form `NAME::SUBNAME` (without quotes) to indicate that the span has name "NAME" (usually the name of the component) and has an additional more specific name "SUBNAME" (usually the function name). The latter is passed to the tracing crate as a span field with the name "NAME". This allows not being distracted by subnames when looking at the trace in Perfetto, but when deeper introspection is needed within a component, it's still possible to view the subnames directly with a few steps (see [Enhancing the timeline](#enhancing-the-timeline)).
+```rust
+// for example, the first will expand to the second
+let _trace = enter_trace_span!(M, borrow_tracker::on_stack_pop);
+let _trace = enter_trace_span!(M, "borrow_tracker", borrow_tracker = "on_stack_pop");
+```
+
+### `tracing_separate_thread` parameter
+
+Miri saves traces using the the `tracing_chrome` `tracing::Layer` so that they can be visualized in Perfetto. To instruct `tracing_chrome` to put some spans on a separate trace thread/line than other spans when viewed in Perfetto, you can pass `tracing_separate_thread = tracing::field::Empty` to the tracing macros. This is useful to separate out spans which just indicate the current step or program frame being processed by the interpreter. As explained in [The timeline](#the-timeline), those spans end up under the "Global Legacy Events" track. You should use a value of `tracing::field::Empty` so that other tracing layers (e.g. the logger) will ignore the `tracing_separate_thread` field. For example:
+```rust
+let _trace = enter_trace_span!(M, step::eval_statement, tracing_separate_thread = tracing::field::Empty);
+```
+
+### Executing something else when tracing is disabled
+
+The `EnteredTraceSpan` trait contains a `or_if_tracing_disabled()` function that you can use to e.g. log a line as an alternative to the tracing span for when tracing is disabled. For example:
+```rust
+let _trace = enter_trace_span!(M, step::eval_statement)
+    .or_if_tracing_disabled(|| tracing::info!("eval_statement"));
+```
+
+## Implementation details
+
+Here we explain how tracing is implemented internally.
+
+The events and spans generated throughout the codebase are collected by [the `tracing` crate](https://crates.io/crates/tracing), which then dispatches them to the code that writes to the trace file, but also to the logger if logging is enabled. 
+
+### Choice of tracing library
+
+The crate that was chosen for collecting traces is [tracing](https://crates.io/crates/tracing), since:
+- it is very well maintained
+- it supports various different trace formats through plug-and-play `Layer`s (in Miri we are using `tracing_chrome` to export traces for perfetto, see [The `tracing_chrome` layer](#the-tracing_chrome-layer))
+- spans and events are collected with not just their name, but also file, line, module, and any number of custom arguments
+- it was already used in Miri and rustc as a logging framework 
+
+One major drawback of the tracing crate is, however, its big overhead. Entering and exiting a span takes on the order of 100ns, and many of Miri's spans are shorter than that, so their measurements are completely off and the program execution increases significantly. E.g. at the point of writing this documentation, enabling tracing makes Miri 5x slower. Note that this used to be even worse, see [Time measurements](#time-measurements).
+
+### The `tracing_chrome` layer
+
+Miri uses [tracing-chrome](https://github.com/thoren-d/tracing-chrome) as the `Layer` that collects spans and events from the tracing crate and saves them to a file that can be opened in Perfetto. Although the crate [is published](https://crates.io/crates/tracing-chrome) on crates.io, it was not possible to depend on it from Miri, because it would bring in a separate compilation of the `tracing` crate. This is because Miri does not directly depend on `tracing`, and instead uses rustc's version through rustc-private, and apparently cargo can't realize that the same library is being built again when rustc-private is involved.
+
+So the solution was to copy-paste [the only file](https://github.com/thoren-d/tracing-chrome/blob/develop/src/lib.rs) in tracing-chrome into Miri. Nevertheless, this gave the possibility to make some changes to tracing-chrome, which you can read about in documentation at the top of [the file](https://github.com/rust-lang/miri/blob/master/src/bin/log/tracing_chrome.rs) that was copied to Miri.
+
+### Time measurements
+
+tracing-chrome originally used `std::time::Instant` to measure time, however on some x86/x86_64 Linux systems it might be unbearably slow since the underlying system call (`clock_gettime`) would take ≈1.3µs. Read more [here](https://btorpey.github.io/blog/2014/02/18/clock-sources-in-linux/) about how the Linux kernel chooses the clock source.
+
+Therefore, on x86/x86_64 Linux systems with a CPU that has an invariant TSC counter, we read from that instead to measure time, which takes only ≈13ns. There are unfortunately a lot of caveats to this approach though, as explained [in the code](https://github.com/rust-lang/miri/blob/master/src/bin/log/tracing_chrome_instant.rs) and [in the PR](https://github.com/rust-lang/miri/pull/4524). The most impactful one is that: every thread spawned in Miri that wants to trace something (which requires measuring time) needs to pin itself to a single CPU core (using `sched_setaffinity`).
+
+## Other useful stuff
+
+### Making a flamegraph
+
+After compiling Miri, you can run the following command to make a flamegraph using Linux' `perf`. It can be useful to spot functions that use up a significant amount of time but that are not yet covered by tracing calls.
+
+```sh
+perf record  --call-graph dwarf -F 999 ./miri/target/debug/miri --edition 2021 --sysroot ~/.cache/miri ./tests/pass/hashmap.rs && perf script | inferno-collapse-perf | inferno-flamegraph > flamegraph.svg
+```
diff --git a/src/tools/miri/etc/rust_analyzer_zed.json b/src/tools/miri/etc/rust_analyzer_zed.json
new file mode 100644
index 00000000000..839914c8b68
--- /dev/null
+++ b/src/tools/miri/etc/rust_analyzer_zed.json
@@ -0,0 +1,41 @@
+{
+    "lsp": {
+        "rust-analyzer": {
+            "initialization_options": {
+                "rustc": {
+                    "source": "discover"
+                },
+                "linkedProjects": [
+                    "./Cargo.toml",
+                    "./cargo-miri/Cargo.toml",
+                    "./genmc-sys/Cargo.toml",
+                    "./miri-script/Cargo.toml"
+                ],
+                "check": {
+                    "invocationStrategy": "once",
+                    "overrideCommand": [
+                        "./miri",
+                        "clippy", // make this `check` when working with a locally built rustc
+                        "--message-format=json"
+                    ]
+                },
+                "cargo": {
+                    "extraEnv": {
+                        "MIRI_AUTO_OPS": "no",
+                        "MIRI_IN_RA": "1"
+                    },
+                    // Contrary to what the name suggests, this also affects proc macros.
+                    "buildScripts": {
+                        "invocationStrategy": "once",
+                        "overrideCommand": [
+                            "./miri",
+                            "check",
+                            "--no-default-features",
+                            "--message-format=json"
+                        ]
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/src/tools/miri/genmc-sys/.clang-format b/src/tools/miri/genmc-sys/.clang-format
new file mode 100644
index 00000000000..669d76ea6c9
--- /dev/null
+++ b/src/tools/miri/genmc-sys/.clang-format
@@ -0,0 +1,52 @@
+# .clang-format
+
+BasedOnStyle: LLVM
+Standard: c++20
+
+ColumnLimit: 100
+AllowShortFunctionsOnASingleLine: Empty
+AllowShortIfStatementsOnASingleLine: false
+AllowShortLoopsOnASingleLine: false
+AllowShortBlocksOnASingleLine: Empty
+BreakBeforeBraces: Attach
+
+BinPackArguments: false
+BinPackParameters: false
+AllowAllParametersOfDeclarationOnNextLine: false
+AlwaysBreakAfterReturnType: None
+
+# Force parameters to break and align
+AlignAfterOpenBracket: BlockIndent
+AllowAllArgumentsOnNextLine: false
+
+# Spacing around braces and parentheses
+SpaceBeforeCtorInitializerColon: true
+SpaceBeforeInheritanceColon: true
+SpaceInEmptyBlock: false
+SpacesInContainerLiterals: true
+SpacesInParensOptions:
+  InCStyleCasts: false
+  InConditionalStatements: false
+  InEmptyParentheses: false
+  Other: false
+SpacesInSquareBrackets: false
+
+# Brace spacing for initializers
+Cpp11BracedListStyle: false
+SpaceBeforeCpp11BracedList: true
+
+# Import grouping: group standard, external, and project includes.
+IncludeBlocks: Regroup
+SortIncludes: true
+
+# Granularity: sort includes per module/file.
+IncludeIsMainRegex: '([-_](test|unittest))?$'
+
+# Miscellaneous
+SpaceAfterCStyleCast: true
+SpaceBeforeParens: ControlStatements
+PointerAlignment: Left
+IndentCaseLabels: true
+IndentWidth: 4
+TabWidth: 4
+UseTab: Never
\ No newline at end of file
diff --git a/src/tools/miri/genmc-sys/Cargo.toml b/src/tools/miri/genmc-sys/Cargo.toml
index 737ab9073bf..6443ecd969d 100644
--- a/src/tools/miri/genmc-sys/Cargo.toml
+++ b/src/tools/miri/genmc-sys/Cargo.toml
@@ -1,17 +1,15 @@
 [package]
 authors = ["Miri Team"]
-# The parts in this repo are MIT OR Apache-2.0, but we are linking in
-# code from https://github.com/MPI-SWS/genmc which is GPL-3.0-or-later.
-license = "(MIT OR Apache-2.0) AND GPL-3.0-or-later"
+license = "MIT OR Apache-2.0"
 name = "genmc-sys"
 version = "0.1.0"
 edition = "2024"
 
 [dependencies]
-cxx = { version = "1.0.160", features = ["c++20"] }
+cxx = { version = "1.0.173", features = ["c++20"] }
 
 [build-dependencies]
 cc = "1.2.16"
 cmake = "0.1.54"
 git2 = { version = "0.20.2", default-features = false, features = ["https"] }
-cxx-build = { version = "1.0.160", features = ["parallel"] }
+cxx-build = { version = "1.0.173", features = ["parallel"] }
diff --git a/src/tools/miri/genmc-sys/build.rs b/src/tools/miri/genmc-sys/build.rs
index 479a3bd7186..8d437c20a09 100644
--- a/src/tools/miri/genmc-sys/build.rs
+++ b/src/tools/miri/genmc-sys/build.rs
@@ -26,17 +26,27 @@ mod downloading {
     use super::GENMC_DOWNLOAD_PATH;
 
     /// The GenMC repository the we get our commit from.
-    pub(crate) const GENMC_GITHUB_URL: &str = "https://github.com/MPI-SWS/genmc.git";
+    pub(crate) const GENMC_GITHUB_URL: &str = "https://gitlab.inf.ethz.ch/public-plf/genmc.git";
     /// The GenMC commit we depend on. It must be available on the specified GenMC repository.
-    pub(crate) const GENMC_COMMIT: &str = "3438dd2c1202cd4a47ed7881d099abf23e4167ab";
+    pub(crate) const GENMC_COMMIT: &str = "af9cc9ccd5d412b16defc35dbf36571c63a19c76";
 
-    pub(crate) fn download_genmc() -> PathBuf {
+    /// Ensure that a local GenMC repo is present and set to the correct commit.
+    /// Return the path of the GenMC repo and whether the checked out commit was changed.
+    pub(crate) fn download_genmc() -> (PathBuf, bool) {
         let Ok(genmc_download_path) = PathBuf::from_str(GENMC_DOWNLOAD_PATH);
         let commit_oid = Oid::from_str(GENMC_COMMIT).expect("Commit should be valid.");
 
         match Repository::open(&genmc_download_path) {
             Ok(repo) => {
                 assert_repo_unmodified(&repo);
+                if let Ok(head) = repo.head()
+                    && let Ok(head_commit) = head.peel_to_commit()
+                    && head_commit.id() == commit_oid
+                {
+                    // Fast path: The expected commit is already checked out.
+                    return (genmc_download_path, false);
+                }
+                // Check if the local repository already contains the commit we need, download it otherwise.
                 let commit = update_local_repo(&repo, commit_oid);
                 checkout_commit(&repo, &commit);
             }
@@ -51,7 +61,7 @@ mod downloading {
             }
         };
 
-        genmc_download_path
+        (genmc_download_path, true)
     }
 
     fn get_remote(repo: &Repository) -> Remote<'_> {
@@ -71,7 +81,8 @@ mod downloading {
 
         // Update remote URL.
         println!(
-            "cargo::warning=GenMC repository remote URL has changed from '{remote_url:?}' to '{GENMC_GITHUB_URL}'"
+            "cargo::warning=GenMC repository remote URL has changed from '{}' to '{GENMC_GITHUB_URL}'",
+            remote_url.unwrap_or_default()
         );
         repo.remote_set_url("origin", GENMC_GITHUB_URL)
             .expect("cannot rename url of remote 'origin'");
@@ -175,19 +186,25 @@ fn link_to_llvm(config_file: &Path) -> (String, String) {
 }
 
 /// Build the GenMC model checker library and the Rust-C++ interop library with cxx.rs
-fn compile_cpp_dependencies(genmc_path: &Path) {
+fn compile_cpp_dependencies(genmc_path: &Path, always_configure: bool) {
+    // Give each step a separate build directory to prevent interference.
+    let out_dir = PathBuf::from(std::env::var("OUT_DIR").as_deref().unwrap());
+    let genmc_build_dir = out_dir.join("genmc");
+    let interface_build_dir = out_dir.join("miri_genmc");
+
     // Part 1:
     // Compile the GenMC library using cmake.
 
-    let cmakelists_path = genmc_path.join("CMakeLists.txt");
-
     // FIXME(genmc,cargo): Switch to using `CARGO_CFG_DEBUG_ASSERTIONS` once https://github.com/rust-lang/cargo/issues/15760 is completed.
     // Enable/disable additional debug checks, prints and options for GenMC, based on the Rust profile (debug/release)
     let enable_genmc_debug = matches!(std::env::var("PROFILE").as_deref().unwrap(), "debug");
 
-    let mut config = cmake::Config::new(cmakelists_path);
-    config.profile(GENMC_CMAKE_PROFILE);
-    config.define("GENMC_DEBUG", if enable_genmc_debug { "ON" } else { "OFF" });
+    let mut config = cmake::Config::new(genmc_path);
+    config
+        .always_configure(always_configure) // We force running the configure step when the GenMC commit changed.
+        .out_dir(genmc_build_dir)
+        .profile(GENMC_CMAKE_PROFILE)
+        .define("GENMC_DEBUG", if enable_genmc_debug { "ON" } else { "OFF" });
 
     // The actual compilation happens here:
     let genmc_install_dir = config.build();
@@ -210,6 +227,13 @@ fn compile_cpp_dependencies(genmc_path: &Path) {
     // These definitions are parsed into a cmake list and then printed to the config.h file, so they are ';' separated.
     let definitions = llvm_definitions.split(";");
 
+    let cpp_files = [
+        "./cpp/src/MiriInterface/EventHandling.cpp",
+        "./cpp/src/MiriInterface/Exploration.cpp",
+        "./cpp/src/MiriInterface/Setup.cpp",
+        "./cpp/src/MiriInterface/ThreadManagement.cpp",
+    ];
+
     let mut bridge = cxx_build::bridge("src/lib.rs");
     // FIXME(genmc,cmake): Remove once the GenMC debug setting is available in the config.h file.
     if enable_genmc_debug {
@@ -225,9 +249,9 @@ fn compile_cpp_dependencies(genmc_path: &Path) {
         .std("c++23")
         .include(genmc_include_dir)
         .include(llvm_include_dirs)
-        .include("./src_cpp")
-        .file("./src_cpp/MiriInterface.hpp")
-        .file("./src_cpp/MiriInterface.cpp")
+        .include("./cpp/include")
+        .files(&cpp_files)
+        .out_dir(interface_build_dir)
         .compile("genmc_interop");
 
     // Link the Rust-C++ interface library generated by cxx_build:
@@ -235,15 +259,9 @@ fn compile_cpp_dependencies(genmc_path: &Path) {
 }
 
 fn main() {
-    // Make sure we don't accidentally distribute a binary with GPL code.
-    if option_env!("RUSTC_STAGE").is_some() {
-        panic!(
-            "genmc should not be enabled in the rustc workspace since it includes a GPL dependency"
-        );
-    }
-
     // Select which path to use for the GenMC repo:
-    let genmc_path = if let Ok(genmc_src_path) = std::env::var("GENMC_SRC_PATH") {
+    let (genmc_path, always_configure) = if let Some(genmc_src_path) = option_env!("GENMC_SRC_PATH")
+    {
         let genmc_src_path =
             PathBuf::from_str(&genmc_src_path).expect("GENMC_SRC_PATH should contain a valid path");
         assert!(
@@ -251,13 +269,20 @@ fn main() {
             "GENMC_SRC_PATH={} does not exist!",
             genmc_src_path.display()
         );
-        genmc_src_path
+        // Rebuild files in the given path change.
+        println!("cargo::rerun-if-changed={}", genmc_src_path.display());
+        // We disable `always_configure` when working with a local repository,
+        // since it increases compile times when working on `genmc-sys`.
+        (genmc_src_path, false)
     } else {
+        // Download GenMC if required and ensure that the correct commit is checked out.
+        // If anything changed in the downloaded repository (e.g., the commit),
+        // we set `always_configure` to ensure there are no weird configs from previous builds.
         downloading::download_genmc()
     };
 
     // Build all required components:
-    compile_cpp_dependencies(&genmc_path);
+    compile_cpp_dependencies(&genmc_path, always_configure);
 
     // Only rebuild if anything changes:
     // Note that we don't add the downloaded GenMC repo, since that should never be modified
@@ -265,5 +290,5 @@ fn main() {
     // cloned (since cargo detects that as a file modification).
     println!("cargo::rerun-if-changed={RUST_CXX_BRIDGE_FILE_PATH}");
     println!("cargo::rerun-if-changed=./src");
-    println!("cargo::rerun-if-changed=./src_cpp");
+    println!("cargo::rerun-if-changed=./cpp");
 }
diff --git a/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp b/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp
new file mode 100644
index 00000000000..444c9375319
--- /dev/null
+++ b/src/tools/miri/genmc-sys/cpp/include/MiriInterface.hpp
@@ -0,0 +1,345 @@
+#ifndef GENMC_MIRI_INTERFACE_HPP
+#define GENMC_MIRI_INTERFACE_HPP
+
+// CXX.rs generated headers:
+#include "rust/cxx.h"
+
+// GenMC generated headers:
+#include "config.h"
+
+// Miri `genmc-sys/src_cpp` headers:
+#include "ResultHandling.hpp"
+
+// GenMC headers:
+#include "ExecutionGraph/EventLabel.hpp"
+#include "Static/ModuleID.hpp"
+#include "Support/MemOrdering.hpp"
+#include "Support/RMWOps.hpp"
+#include "Verification/Config.hpp"
+#include "Verification/GenMCDriver.hpp"
+
+// C++ headers:
+#include <cstdint>
+#include <format>
+#include <iomanip>
+#include <memory>
+
+/**** Types available to both Rust and C++ ****/
+
+struct GenmcParams;
+enum class LogLevel : std::uint8_t;
+
+struct GenmcScalar;
+struct SchedulingResult;
+struct EstimationResult;
+struct LoadResult;
+struct StoreResult;
+struct ReadModifyWriteResult;
+struct CompareExchangeResult;
+
+// GenMC uses `int` for its thread IDs.
+using ThreadId = int;
+
+/// Set the log level for GenMC.
+///
+/// # Safety
+///
+/// This function is not thread safe, since it writes to the global, mutable, non-atomic `logLevel`
+/// variable. Any GenMC function may read from `logLevel` unsynchronized.
+/// The safest way to use this function is to set the log level exactly once before first calling
+/// `create_handle`.
+/// Never calling this function is safe, GenMC will fall back to its default log level.
+/* unsafe */ void set_log_level_raw(LogLevel log_level);
+
+struct MiriGenmcShim : private GenMCDriver {
+
+  public:
+    MiriGenmcShim(std::shared_ptr<const Config> conf, Mode mode /* = VerificationMode{} */)
+        : GenMCDriver(std::move(conf), nullptr, mode) {}
+
+    /// Create a new `MiriGenmcShim`, which wraps a `GenMCDriver`.
+    ///
+    /// # Safety
+    ///
+    /// This function is marked as unsafe since the `logLevel` global variable is non-atomic.
+    /// This function should not be called in an unsynchronized way with `set_log_level_raw`,
+    /// since this function and any methods on the returned `MiriGenmcShim` may read the
+    /// `logLevel`, causing a data race. The safest way to use these functions is to call
+    /// `set_log_level_raw` once, and only then start creating handles. There should not be any
+    /// other (safe) way to create a `MiriGenmcShim`.
+    /* unsafe */ static auto create_handle(const GenmcParams& params, bool estimation_mode)
+        -> std::unique_ptr<MiriGenmcShim>;
+
+    virtual ~MiriGenmcShim() {}
+
+    /**** Execution start/end handling ****/
+
+    // This function must be called at the start of any execution, before any events are
+    // reported to GenMC.
+    void handle_execution_start();
+    // This function must be called at the end of any execution, even if an error was found
+    // during the execution.
+    // Returns `null`, or a string containing an error message if an error occured.
+    std::unique_ptr<std::string> handle_execution_end();
+
+    /***** Functions for handling events encountered during program execution. *****/
+
+    /**** Memory access handling ****/
+
+    [[nodiscard]] LoadResult handle_load(
+        ThreadId thread_id,
+        uint64_t address,
+        uint64_t size,
+        MemOrdering ord,
+        GenmcScalar old_val
+    );
+    [[nodiscard]] ReadModifyWriteResult handle_read_modify_write(
+        ThreadId thread_id,
+        uint64_t address,
+        uint64_t size,
+        RMWBinOp rmw_op,
+        MemOrdering ordering,
+        GenmcScalar rhs_value,
+        GenmcScalar old_val
+    );
+    [[nodiscard]] CompareExchangeResult handle_compare_exchange(
+        ThreadId thread_id,
+        uint64_t address,
+        uint64_t size,
+        GenmcScalar expected_value,
+        GenmcScalar new_value,
+        GenmcScalar old_val,
+        MemOrdering success_ordering,
+        MemOrdering fail_load_ordering,
+        bool can_fail_spuriously
+    );
+    [[nodiscard]] StoreResult handle_store(
+        ThreadId thread_id,
+        uint64_t address,
+        uint64_t size,
+        GenmcScalar value,
+        GenmcScalar old_val,
+        MemOrdering ord
+    );
+
+    void handle_fence(ThreadId thread_id, MemOrdering ord);
+
+    /**** Memory (de)allocation ****/
+    auto handle_malloc(ThreadId thread_id, uint64_t size, uint64_t alignment) -> uint64_t;
+    void handle_free(ThreadId thread_id, uint64_t address);
+
+    /**** Thread management ****/
+    void handle_thread_create(ThreadId thread_id, ThreadId parent_id);
+    void handle_thread_join(ThreadId thread_id, ThreadId child_id);
+    void handle_thread_finish(ThreadId thread_id, uint64_t ret_val);
+    void handle_thread_kill(ThreadId thread_id);
+
+    /***** Exploration related functionality *****/
+
+    /** Ask the GenMC scheduler for a new thread to schedule and return whether the execution is
+     * finished, blocked, or can continue.
+     * Updates the next instruction kind for the given thread id. */
+    auto schedule_next(const int curr_thread_id, const ActionKind curr_thread_next_instr_kind)
+        -> SchedulingResult;
+
+    /**
+     * Check whether there are more executions to explore.
+     * If there are more executions, this method prepares for the next execution and returns
+     * `true`. Returns true if there are no more executions to explore. */
+    auto is_exploration_done() -> bool {
+        return GenMCDriver::done();
+    }
+
+    /**** Result querying functionality. ****/
+
+    // NOTE: We don't want to share the `VerificationResult` type with the Rust side, since it
+    // is very large, uses features that CXX.rs doesn't support and may change as GenMC changes.
+    // Instead, we only use the result on the C++ side, and only expose these getter function to
+    // the Rust side.
+
+    // Note that CXX.rs doesn't support returning a C++ string to Rust by value,
+    // it must be behind an indirection like a `unique_ptr` (tested with CXX 1.0.170).
+
+    /// Get the number of blocked executions encountered by GenMC (cast into a fixed with
+    /// integer)
+    auto get_blocked_execution_count() const -> uint64_t {
+        return static_cast<uint64_t>(getResult().exploredBlocked);
+    }
+
+    /// Get the number of executions explored by GenMC (cast into a fixed with integer)
+    auto get_explored_execution_count() const -> uint64_t {
+        return static_cast<uint64_t>(getResult().explored);
+    }
+
+    /// Get all messages that GenMC produced (errors, warnings), combined into one string.
+    auto get_result_message() const -> std::unique_ptr<std::string> {
+        return std::make_unique<std::string>(getResult().message);
+    }
+
+    /// If an error occurred, return a string describing the error, otherwise, return `nullptr`.
+    auto get_error_string() const -> std::unique_ptr<std::string> {
+        const auto& result = GenMCDriver::getResult();
+        if (result.status.has_value())
+            return format_error(result.status.value());
+        return nullptr;
+    }
+
+    /**** Printing and estimation mode functionality. ****/
+
+    /// Get the results of a run in estimation mode.
+    auto get_estimation_results() const -> EstimationResult;
+
+  private:
+    /** Increment the event index in the given thread by 1 and return the new event. */
+    [[nodiscard]] inline auto inc_pos(ThreadId tid) -> Event {
+        ERROR_ON(tid >= threads_action_.size(), "ThreadId out of bounds");
+        return ++threads_action_[tid].event;
+    }
+    /** Decrement the event index in the given thread by 1 and return the new event. */
+    inline auto dec_pos(ThreadId tid) -> Event {
+        ERROR_ON(tid >= threads_action_.size(), "ThreadId out of bounds");
+        return --threads_action_[tid].event;
+    }
+
+    /**
+     * Helper function for loads that need to reset the event counter when no value is returned.
+     * Same syntax as `GenMCDriver::handleLoad`, but this takes a thread id instead of an Event.
+     * Automatically calls `inc_pos` and `dec_pos` where needed for the given thread.
+     */
+    template <EventLabel::EventLabelKind k, typename... Ts>
+    auto handle_load_reset_if_none(ThreadId tid, Ts&&... params) -> HandleResult<SVal> {
+        const auto pos = inc_pos(tid);
+        const auto ret = GenMCDriver::handleLoad<k>(pos, std::forward<Ts>(params)...);
+        // If we didn't get a value, we have to reset the index of the current thread.
+        if (!std::holds_alternative<SVal>(ret)) {
+            dec_pos(tid);
+        }
+        return ret;
+    }
+
+    /**
+     * GenMC uses the term `Action` to refer to a struct of:
+     * - `ActionKind`, storing whether the next instruction in a thread may be a load
+     * - `Event`, storing the most recent event index added for a thread
+     *
+     * Here we store the "action" for each thread. In particular we use this to assign event
+     * indices, since GenMC expects us to do that.
+     */
+    std::vector<Action> threads_action_;
+};
+
+/// Get the bit mask that GenMC expects for global memory allocations.
+/// FIXME(genmc): currently we use `get_global_alloc_static_mask()` to ensure the
+/// `SAddr::staticMask` constant is consistent between Miri and GenMC, but if
+/// https://github.com/dtolnay/cxx/issues/1051 is fixed we could share the constant
+///   directly.
+constexpr auto get_global_alloc_static_mask() -> uint64_t {
+    return SAddr::staticMask;
+}
+
+// CXX.rs generated headers:
+// NOTE: this must be included *after* `MiriGenmcShim` and all the other types we use are defined,
+// otherwise there will be compilation errors due to missing definitions.
+#include "genmc-sys/src/lib.rs.h"
+
+/**** Result handling ****/
+// NOTE: these must come after the cxx_bridge generated code, since that contains the actual
+// definitions of these types.
+
+namespace GenmcScalarExt {
+inline GenmcScalar uninit() {
+    return GenmcScalar {
+        .value = 0,
+        .is_init = false,
+    };
+}
+
+inline GenmcScalar from_sval(SVal sval) {
+    return GenmcScalar {
+        .value = sval.get(),
+        .is_init = true,
+    };
+}
+
+inline SVal to_sval(GenmcScalar scalar) {
+    ERROR_ON(!scalar.is_init, "Cannot convert an uninitialized `GenmcScalar` into an `SVal`\n");
+    return SVal(scalar.value);
+}
+} // namespace GenmcScalarExt
+
+namespace LoadResultExt {
+inline LoadResult no_value() {
+    return LoadResult {
+        .error = std::unique_ptr<std::string>(nullptr),
+        .has_value = false,
+        .read_value = GenmcScalarExt::uninit(),
+    };
+}
+
+inline LoadResult from_value(SVal read_value) {
+    return LoadResult { .error = std::unique_ptr<std::string>(nullptr),
+                        .has_value = true,
+                        .read_value = GenmcScalarExt::from_sval(read_value) };
+}
+
+inline LoadResult from_error(std::unique_ptr<std::string> error) {
+    return LoadResult { .error = std::move(error),
+                        .has_value = false,
+                        .read_value = GenmcScalarExt::uninit() };
+}
+} // namespace LoadResultExt
+
+namespace StoreResultExt {
+inline StoreResult ok(bool is_coherence_order_maximal_write) {
+    return StoreResult { /* error: */ std::unique_ptr<std::string>(nullptr),
+                         is_coherence_order_maximal_write };
+}
+
+inline StoreResult from_error(std::unique_ptr<std::string> error) {
+    return StoreResult { .error = std::move(error), .is_coherence_order_maximal_write = false };
+}
+} // namespace StoreResultExt
+
+namespace ReadModifyWriteResultExt {
+inline ReadModifyWriteResult
+ok(SVal old_value, SVal new_value, bool is_coherence_order_maximal_write) {
+    return ReadModifyWriteResult { .error = std::unique_ptr<std::string>(nullptr),
+                                   .old_value = GenmcScalarExt::from_sval(old_value),
+                                   .new_value = GenmcScalarExt::from_sval(new_value),
+                                   .is_coherence_order_maximal_write =
+                                       is_coherence_order_maximal_write };
+}
+
+inline ReadModifyWriteResult from_error(std::unique_ptr<std::string> error) {
+    return ReadModifyWriteResult { .error = std::move(error),
+                                   .old_value = GenmcScalarExt::uninit(),
+                                   .new_value = GenmcScalarExt::uninit(),
+                                   .is_coherence_order_maximal_write = false };
+}
+} // namespace ReadModifyWriteResultExt
+
+namespace CompareExchangeResultExt {
+inline CompareExchangeResult success(SVal old_value, bool is_coherence_order_maximal_write) {
+    return CompareExchangeResult { .error = nullptr,
+                                   .old_value = GenmcScalarExt::from_sval(old_value),
+                                   .is_success = true,
+                                   .is_coherence_order_maximal_write =
+                                       is_coherence_order_maximal_write };
+}
+
+inline CompareExchangeResult failure(SVal old_value) {
+    return CompareExchangeResult { .error = nullptr,
+                                   .old_value = GenmcScalarExt::from_sval(old_value),
+                                   .is_success = false,
+                                   .is_coherence_order_maximal_write = false };
+}
+
+inline CompareExchangeResult from_error(std::unique_ptr<std::string> error) {
+    return CompareExchangeResult { .error = std::move(error),
+                                   .old_value = GenmcScalarExt::uninit(),
+                                   .is_success = false,
+                                   .is_coherence_order_maximal_write = false };
+}
+} // namespace CompareExchangeResultExt
+
+#endif /* GENMC_MIRI_INTERFACE_HPP */
diff --git a/src/tools/miri/genmc-sys/cpp/include/ResultHandling.hpp b/src/tools/miri/genmc-sys/cpp/include/ResultHandling.hpp
new file mode 100644
index 00000000000..189f32e6f51
--- /dev/null
+++ b/src/tools/miri/genmc-sys/cpp/include/ResultHandling.hpp
@@ -0,0 +1,21 @@
+#ifndef GENMC_RESULT_HANDLING_HPP
+#define GENMC_RESULT_HANDLING_HPP
+
+// CXX.rs generated headers:
+#include "rust/cxx.h"
+
+// GenMC headers:
+#include "Verification/VerificationError.hpp"
+
+#include <string>
+
+/** Information about an error, formatted as a string to avoid having to share an error enum and
+ * printing functionality with the Rust side. */
+static auto format_error(VerificationError err) -> std::unique_ptr<std::string> {
+    auto buf = std::string();
+    auto s = llvm::raw_string_ostream(buf);
+    s << err;
+    return std::make_unique<std::string>(s.str());
+}
+
+#endif /* GENMC_RESULT_HANDLING_HPP */
diff --git a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp
new file mode 100644
index 00000000000..05c82641df9
--- /dev/null
+++ b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/EventHandling.cpp
@@ -0,0 +1,251 @@
+/** This file contains functionality related to handling events encountered
+ * during an execution, such as loads, stores or memory (de)allocation. */
+
+#include "MiriInterface.hpp"
+
+// CXX.rs generated headers:
+#include "genmc-sys/src/lib.rs.h"
+
+// GenMC headers:
+#include "ADT/value_ptr.hpp"
+#include "ExecutionGraph/EventLabel.hpp"
+#include "ExecutionGraph/LoadAnnotation.hpp"
+#include "Runtime/InterpreterEnumAPI.hpp"
+#include "Static/ModuleID.hpp"
+#include "Support/ASize.hpp"
+#include "Support/Error.hpp"
+#include "Support/Logger.hpp"
+#include "Support/MemAccess.hpp"
+#include "Support/RMWOps.hpp"
+#include "Support/SAddr.hpp"
+#include "Support/SVal.hpp"
+#include "Support/ThreadInfo.hpp"
+#include "Support/Verbosity.hpp"
+#include "Verification/GenMCDriver.hpp"
+#include "Verification/MemoryModel.hpp"
+
+// C++ headers:
+#include <cstddef>
+#include <cstdint>
+#include <memory>
+#include <utility>
+
+/**** Memory access handling ****/
+
+[[nodiscard]] auto MiriGenmcShim::handle_load(
+    ThreadId thread_id,
+    uint64_t address,
+    uint64_t size,
+    MemOrdering ord,
+    GenmcScalar old_val
+) -> LoadResult {
+    // `type` is only used for printing.
+    const auto type = AType::Unsigned;
+    const auto ret = handle_load_reset_if_none<EventLabel::EventLabelKind::Read>(
+        thread_id,
+        ord,
+        SAddr(address),
+        ASize(size),
+        type
+    );
+
+    if (const auto* err = std::get_if<VerificationError>(&ret))
+        return LoadResultExt::from_error(format_error(*err));
+    const auto* ret_val = std::get_if<SVal>(&ret);
+    if (ret_val == nullptr)
+        ERROR("Unimplemented: load returned unexpected result.");
+    return LoadResultExt::from_value(*ret_val);
+}
+
+[[nodiscard]] auto MiriGenmcShim::handle_store(
+    ThreadId thread_id,
+    uint64_t address,
+    uint64_t size,
+    GenmcScalar value,
+    GenmcScalar old_val,
+    MemOrdering ord
+) -> StoreResult {
+    const auto pos = inc_pos(thread_id);
+    const auto ret = GenMCDriver::handleStore<EventLabel::EventLabelKind::Write>(
+        pos,
+        ord,
+        SAddr(address),
+        ASize(size),
+        /* type */ AType::Unsigned, // `type` is only used for printing.
+        GenmcScalarExt::to_sval(value),
+        EventDeps()
+    );
+
+    if (const auto* err = std::get_if<VerificationError>(&ret))
+        return StoreResultExt::from_error(format_error(*err));
+    if (!std::holds_alternative<std::monostate>(ret))
+        ERROR("store returned unexpected result");
+
+    // FIXME(genmc,mixed-accesses): Use the value that GenMC returns from handleStore (once
+    // available).
+    const auto& g = getExec().getGraph();
+    return StoreResultExt::ok(
+        /* is_coherence_order_maximal_write */ g.co_max(SAddr(address))->getPos() == pos
+    );
+}
+
+void MiriGenmcShim::handle_fence(ThreadId thread_id, MemOrdering ord) {
+    const auto pos = inc_pos(thread_id);
+    GenMCDriver::handleFence(pos, ord, EventDeps());
+}
+
+[[nodiscard]] auto MiriGenmcShim::handle_read_modify_write(
+    ThreadId thread_id,
+    uint64_t address,
+    uint64_t size,
+    RMWBinOp rmw_op,
+    MemOrdering ordering,
+    GenmcScalar rhs_value,
+    GenmcScalar old_val
+) -> ReadModifyWriteResult {
+    // NOTE: Both the store and load events should get the same `ordering`, it should not be split
+    // into a load and a store component. This means we can have for example `AcqRel` loads and
+    // stores, but this is intended for RMW operations.
+
+    // Somewhat confusingly, the GenMC term for RMW read/write labels is
+    // `FaiRead` and `FaiWrite`.
+    const auto load_ret = handle_load_reset_if_none<EventLabel::EventLabelKind::FaiRead>(
+        thread_id,
+        ordering,
+        SAddr(address),
+        ASize(size),
+        AType::Unsigned, // The type is only used for printing.
+        rmw_op,
+        GenmcScalarExt::to_sval(rhs_value),
+        EventDeps()
+    );
+    if (const auto* err = std::get_if<VerificationError>(&load_ret))
+        return ReadModifyWriteResultExt::from_error(format_error(*err));
+
+    const auto* ret_val = std::get_if<SVal>(&load_ret);
+    if (nullptr == ret_val) {
+        ERROR("Unimplemented: read-modify-write returned unexpected result.");
+    }
+    const auto read_old_val = *ret_val;
+    const auto new_value =
+        executeRMWBinOp(read_old_val, GenmcScalarExt::to_sval(rhs_value), size, rmw_op);
+
+    const auto storePos = inc_pos(thread_id);
+    const auto store_ret = GenMCDriver::handleStore<EventLabel::EventLabelKind::FaiWrite>(
+        storePos,
+        ordering,
+        SAddr(address),
+        ASize(size),
+        AType::Unsigned, // The type is only used for printing.
+        new_value
+    );
+    if (const auto* err = std::get_if<VerificationError>(&store_ret))
+        return ReadModifyWriteResultExt::from_error(format_error(*err));
+
+    const auto* store_ret_val = std::get_if<std::monostate>(&store_ret);
+    ERROR_ON(nullptr == store_ret_val, "Unimplemented: RMW store returned unexpected result.");
+
+    // FIXME(genmc,mixed-accesses): Use the value that GenMC returns from handleStore (once
+    // available).
+    const auto& g = getExec().getGraph();
+    return ReadModifyWriteResultExt::ok(
+        /* old_value: */ read_old_val,
+        new_value,
+        /* is_coherence_order_maximal_write */ g.co_max(SAddr(address))->getPos() == storePos
+    );
+}
+
+[[nodiscard]] auto MiriGenmcShim::handle_compare_exchange(
+    ThreadId thread_id,
+    uint64_t address,
+    uint64_t size,
+    GenmcScalar expected_value,
+    GenmcScalar new_value,
+    GenmcScalar old_val,
+    MemOrdering success_ordering,
+    MemOrdering fail_load_ordering,
+    bool can_fail_spuriously
+) -> CompareExchangeResult {
+    // NOTE: Both the store and load events should get the same `ordering`, it should not be split
+    // into a load and a store component. This means we can have for example `AcqRel` loads and
+    // stores, but this is intended for CAS operations.
+
+    // FIXME(GenMC): properly handle failure memory ordering.
+
+    auto expectedVal = GenmcScalarExt::to_sval(expected_value);
+    auto new_val = GenmcScalarExt::to_sval(new_value);
+
+    const auto load_ret = handle_load_reset_if_none<EventLabel::EventLabelKind::CasRead>(
+        thread_id,
+        success_ordering,
+        SAddr(address),
+        ASize(size),
+        AType::Unsigned, // The type is only used for printing.
+        expectedVal,
+        new_val
+    );
+    if (const auto* err = std::get_if<VerificationError>(&load_ret))
+        return CompareExchangeResultExt::from_error(format_error(*err));
+    const auto* ret_val = std::get_if<SVal>(&load_ret);
+    ERROR_ON(nullptr == ret_val, "Unimplemented: load returned unexpected result.");
+    const auto read_old_val = *ret_val;
+    if (read_old_val != expectedVal)
+        return CompareExchangeResultExt::failure(read_old_val);
+
+    // FIXME(GenMC): Add support for modelling spurious failures.
+
+    const auto storePos = inc_pos(thread_id);
+    const auto store_ret = GenMCDriver::handleStore<EventLabel::EventLabelKind::CasWrite>(
+        storePos,
+        success_ordering,
+        SAddr(address),
+        ASize(size),
+        AType::Unsigned, // The type is only used for printing.
+        new_val
+    );
+    if (const auto* err = std::get_if<VerificationError>(&store_ret))
+        return CompareExchangeResultExt::from_error(format_error(*err));
+    const auto* store_ret_val = std::get_if<std::monostate>(&store_ret);
+    ERROR_ON(
+        nullptr == store_ret_val,
+        "Unimplemented: compare-exchange store returned unexpected result."
+    );
+
+    // FIXME(genmc,mixed-accesses): Use the value that GenMC returns from handleStore (once
+    // available).
+    const auto& g = getExec().getGraph();
+    return CompareExchangeResultExt::success(
+        read_old_val,
+        /* is_coherence_order_maximal_write */ g.co_max(SAddr(address))->getPos() == storePos
+    );
+}
+
+/**** Memory (de)allocation ****/
+
+auto MiriGenmcShim::handle_malloc(ThreadId thread_id, uint64_t size, uint64_t alignment)
+    -> uint64_t {
+    const auto pos = inc_pos(thread_id);
+
+    // These are only used for printing and features Miri-GenMC doesn't support (yet).
+    const auto storage_duration = StorageDuration::SD_Heap;
+    // Volatile, as opposed to "persistent" (i.e., non-volatile memory that persists over reboots)
+    const auto storage_type = StorageType::ST_Volatile;
+    const auto address_space = AddressSpace::AS_User;
+
+    const SVal ret_val = GenMCDriver::handleMalloc(
+        pos,
+        size,
+        alignment,
+        storage_duration,
+        storage_type,
+        address_space,
+        EventDeps()
+    );
+    return ret_val.get();
+}
+
+void MiriGenmcShim::handle_free(ThreadId thread_id, uint64_t address) {
+    const auto pos = inc_pos(thread_id);
+    GenMCDriver::handleFree(pos, SAddr(address), EventDeps());
+    // FIXME(genmc): add error handling once GenMC returns errors from `handleFree`
+}
diff --git a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Exploration.cpp b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Exploration.cpp
new file mode 100644
index 00000000000..5e7188f17e0
--- /dev/null
+++ b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Exploration.cpp
@@ -0,0 +1,56 @@
+/** This file contains functionality related to exploration, such as scheduling.  */
+
+#include "MiriInterface.hpp"
+
+// CXX.rs generated headers:
+#include "genmc-sys/src/lib.rs.h"
+
+// GenMC headers:
+#include "Support/Error.hpp"
+#include "Support/Verbosity.hpp"
+
+// C++ headers:
+#include <cmath>
+#include <cstdint>
+#include <limits>
+
+auto MiriGenmcShim::schedule_next(
+    const int curr_thread_id,
+    const ActionKind curr_thread_next_instr_kind
+) -> SchedulingResult {
+    // The current thread is the only one where the `kind` could have changed since we last made
+    // a scheduling decision.
+    threads_action_[curr_thread_id].kind = curr_thread_next_instr_kind;
+
+    if (const auto result = GenMCDriver::scheduleNext(threads_action_))
+        return SchedulingResult { ExecutionState::Ok, static_cast<int32_t>(result.value()) };
+    if (GenMCDriver::isExecutionBlocked())
+        return SchedulingResult { ExecutionState::Blocked, 0 };
+    return SchedulingResult { ExecutionState::Finished, 0 };
+}
+
+/**** Execution start/end handling ****/
+
+void MiriGenmcShim::handle_execution_start() {
+    threads_action_.clear();
+    threads_action_.push_back(Action(ActionKind::Load, Event::getInit()));
+    GenMCDriver::handleExecutionStart();
+}
+
+auto MiriGenmcShim::handle_execution_end() -> std::unique_ptr<std::string> {
+    // FIXME(genmc): add error handling once GenMC returns an error here.
+    GenMCDriver::handleExecutionEnd();
+    return {};
+}
+
+/**** Estimation mode result ****/
+
+auto MiriGenmcShim::get_estimation_results() const -> EstimationResult {
+    const auto& res = getResult();
+    return EstimationResult {
+        .mean = static_cast<double>(res.estimationMean),
+        .sd = static_cast<double>(std::sqrt(res.estimationVariance)),
+        .explored_execs = static_cast<uint64_t>(res.explored),
+        .blocked_execs = static_cast<uint64_t>(res.exploredBlocked),
+    };
+}
diff --git a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp
new file mode 100644
index 00000000000..af13f0d0774
--- /dev/null
+++ b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/Setup.cpp
@@ -0,0 +1,197 @@
+/** This file contains functionality related to creation of the GenMCDriver,
+ * including translating settings set by Miri.  */
+
+#include "MiriInterface.hpp"
+
+// CXX.rs generated headers:
+#include "genmc-sys/src/lib.rs.h"
+
+// GenMC headers:
+#include "Support/Error.hpp"
+#include "Support/Verbosity.hpp"
+#include "Verification/InterpreterCallbacks.hpp"
+
+// C++ headers:
+#include <cstdint>
+
+/**
+ * Translate the Miri-GenMC `LogLevel` to the GenMC `VerbosityLevel`.
+ * Downgrade any debug options to `Tip` if `ENABLE_GENMC_DEBUG` is not enabled.
+ */
+static auto to_genmc_verbosity_level(const LogLevel log_level) -> VerbosityLevel {
+    switch (log_level) {
+        case LogLevel::Quiet:
+            return VerbosityLevel::Quiet;
+        case LogLevel::Error:
+            return VerbosityLevel::Error;
+        case LogLevel::Warning:
+            return VerbosityLevel::Warning;
+        case LogLevel::Tip:
+            return VerbosityLevel::Tip;
+#ifdef ENABLE_GENMC_DEBUG
+        case LogLevel::Debug1Revisits:
+            return VerbosityLevel::Debug1;
+        case LogLevel::Debug2MemoryAccesses:
+            return VerbosityLevel::Debug2;
+        case LogLevel::Debug3ReadsFrom:
+            return VerbosityLevel::Debug3;
+#else
+        // Downgrade to `Tip` if the debug levels are not available.
+        case LogLevel::Debug1Revisits:
+        case LogLevel::Debug2MemoryAccesses:
+        case LogLevel::Debug3ReadsFrom:
+            return VerbosityLevel::Tip;
+#endif
+        default:
+            WARN_ONCE(
+                "unknown-log-level",
+                "Unknown `LogLevel`, defaulting to `VerbosityLevel::Tip`."
+            );
+            return VerbosityLevel::Tip;
+    }
+}
+
+/* unsafe */ void set_log_level_raw(LogLevel log_level) {
+    // The `logLevel` is a static, non-atomic variable.
+    // It should never be changed if `MiriGenmcShim` still exists, since any of its methods may read
+    // the `logLevel`, otherwise it may cause data races.
+    logLevel = to_genmc_verbosity_level(log_level);
+}
+
+/* unsafe */ auto MiriGenmcShim::create_handle(const GenmcParams& params, bool estimation_mode)
+    -> std::unique_ptr<MiriGenmcShim> {
+    auto conf = std::make_shared<Config>();
+
+    // Set whether GenMC should print execution graphs after every explored/blocked execution.
+    conf->printExecGraphs =
+        (params.print_execution_graphs == ExecutiongraphPrinting::Explored ||
+         params.print_execution_graphs == ExecutiongraphPrinting::ExploredAndBlocked);
+    conf->printBlockedExecs =
+        (params.print_execution_graphs == ExecutiongraphPrinting::Blocked ||
+         params.print_execution_graphs == ExecutiongraphPrinting::ExploredAndBlocked);
+
+    // `1024` is the default value that GenMC uses.
+    // If any thread has at least this many events, a warning/tip will be printed.
+    //
+    // Miri produces a lot more events than GenMC, so the graph size warning triggers on almost
+    // all programs. The current value is large enough so the warning is not be triggered by any
+    // reasonable programs.
+    // FIXME(genmc): The emitted warning mentions features not supported by Miri ('--unroll'
+    // parameter).
+    // FIXME(genmc): A more appropriate limit should be chosen once the warning is useful for
+    // Miri.
+    conf->warnOnGraphSize = 1024 * 1024;
+
+    // We only support the `RC11` memory model for Rust, and `SC` when weak memory emulation is
+    // disabled.
+    conf->model = params.disable_weak_memory_emulation ? ModelType::SC : ModelType::RC11;
+
+    // This prints the seed that GenMC picks for randomized scheduling during estimation mode.
+    conf->printRandomScheduleSeed = params.print_random_schedule_seed;
+
+    // FIXME(genmc): supporting IPR requires annotations for `assume` and `spinloops`.
+    conf->ipr = false;
+    // FIXME(genmc): supporting BAM requires `Barrier` support + detecting whether return value
+    // of barriers are used.
+    conf->disableBAM = true;
+
+    // Instruction caching could help speed up verification by filling the graph from cache, if
+    // the list of values read by all load events in a thread have been seen before. Combined
+    // with not replaying completed threads, this can also reducing the amount of Mir
+    // interpretation required by Miri. With the current setup, this would be incorrect, since
+    // Miri doesn't give GenMC the actual values read by non-atomic reads.
+    conf->instructionCaching = false;
+    // Many of Miri's checks work under the assumption that threads are only executed in an
+    // order that could actually happen during a normal execution. Formally, this means that
+    // replaying an execution needs to respect the po-rf-relation of the executiongraph (po ==
+    // program-order, rf == reads-from). This means, any event in the graph, when replayed, must
+    // happen after any events that happen before it in the same graph according to the program
+    // code, and all (non-atomic) reads must happen after the write event they read from.
+    //
+    // Not replaying completed threads means any read event from that thread never happens in
+    // Miri's memory, so this would only work if there are never any non-atomic reads from any
+    // value written by the skipped thread.
+    conf->replayCompletedThreads = true;
+
+    // FIXME(genmc): implement symmetry reduction.
+    ERROR_ON(
+        params.do_symmetry_reduction,
+        "Symmetry reduction is currently unsupported in GenMC mode."
+    );
+    conf->symmetryReduction = params.do_symmetry_reduction;
+
+    // Set the scheduling policy. GenMC uses `WFR` for estimation mode.
+    // For normal verification, `WF` has the best performance and is the GenMC default.
+    // Other scheduling policies are used by GenMC for testing and for modes currently
+    // unsupported with Miri such as bounding, which uses LTR.
+    conf->schedulePolicy = estimation_mode ? SchedulePolicy::WFR : SchedulePolicy::WF;
+
+    // Set the min and max number of executions tested in estimation mode.
+    conf->estimationMin = 10; // default taken from GenMC
+    conf->estimationMax = params.estimation_max;
+    // Deviation threshold % under which estimation is deemed good enough.
+    conf->sdThreshold = 10; // default taken from GenMC
+    // Set the mode used for this driver, either estimation or verification.
+    const auto mode = estimation_mode ? GenMCDriver::Mode(GenMCDriver::EstimationMode {})
+                                      : GenMCDriver::Mode(GenMCDriver::VerificationMode {});
+
+    // Running Miri-GenMC without race detection is not supported.
+    // Disabling this option also changes the behavior of the replay scheduler to only schedule
+    // at atomic operations, which is required with Miri. This happens because Miri can generate
+    // multiple GenMC events for a single MIR terminator. Without this option, the scheduler
+    // might incorrectly schedule an atomic MIR terminator because the first event it creates is
+    // a non-atomic (e.g., `StorageLive`).
+    conf->disableRaceDetection = false;
+
+    // Miri can already check for unfreed memory. Also, GenMC cannot distinguish between memory
+    // that is allowed to leak and memory that is not.
+    conf->warnUnfreedMemory = false;
+
+    // FIXME(genmc,error handling): This function currently exits on error, but will return an
+    // error value in the future. The return value should be checked once this change is made.
+    checkConfig(*conf);
+
+    // Create the actual driver and Miri-GenMC communication shim.
+    auto driver = std::make_unique<MiriGenmcShim>(std::move(conf), mode);
+
+    // FIXME(genmc,HACK): Until a proper solution is implemented in GenMC, these callbacks will
+    // allow Miri to return information about global allocations and override uninitialized memory
+    // checks for non-atomic loads (Miri handles those without GenMC, so the error would be wrong).
+    auto interpreter_callbacks = InterpreterCallbacks {
+        // Miri already ensures that memory accesses are valid, so this check doesn't matter.
+        // We check that the address is static, but skip checking if it is part of an actual
+        // allocation.
+        .isStaticallyAllocated = [](SAddr addr) { return addr.isStatic(); },
+        // FIXME(genmc,error reporting): Once a proper a proper API for passing such information is
+        // implemented in GenMC, Miri should use it to improve the produced error messages.
+        .getStaticName = [](SAddr addr) { return "[UNKNOWN STATIC]"; },
+        // This function is called to get the initial value stored at the given address.
+        //
+        // From a Miri perspective, this API doesn't work very well: most memory starts out
+        // "uninitialized";
+        // only statics have an initial value. And their initial value is just a sequence of bytes,
+        // but GenMC
+        // expect this to be already split into separate atomic variables. So we return a dummy
+        // value.
+        // This value should never be visible to the interpreted program.
+        // GenMC does not understand uninitialized memory the same way Miri does, which may cause
+        // this function to be called. The returned value can be visible to Miri or the user:
+        // - Printing the execution graph may contain this value in place of uninitialized values.
+        //   FIXME(genmc): NOTE: printing the execution graph is not yet implemented.
+        // - Non-atomic loads may return this value, but Miri ignores values of non-atomic loads.
+        // - Atomic loads will *not* see this value once mixed atomic-non-atomic support is added.
+        //   Currently, atomic loads can see this value, unless initialized by an *atomic* store.
+        //   FIXME(genmc): update this comment once mixed atomic-non-atomic support is added.
+        //
+        // FIXME(genmc): implement proper support for uninitialized memory in GenMC. Ideally, the
+        // initial value getter would return an `optional<SVal>`, since the memory location may be
+        // uninitialized.
+        .initValGetter = [](const AAccess& a) { return SVal(0xDEAD); },
+        // Miri serves non-atomic loads from its own memory and these GenMC checks are wrong in
+        // that case. This should no longer be required with proper mixed-size access support.
+        .skipUninitLoadChecks = [](MemOrdering ord) { return ord == MemOrdering::NotAtomic; },
+    };
+    driver->setInterpCallbacks(std::move(interpreter_callbacks));
+
+    return driver;
+}
diff --git a/src/tools/miri/genmc-sys/cpp/src/MiriInterface/ThreadManagement.cpp b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/ThreadManagement.cpp
new file mode 100644
index 00000000000..352d27adc3e
--- /dev/null
+++ b/src/tools/miri/genmc-sys/cpp/src/MiriInterface/ThreadManagement.cpp
@@ -0,0 +1,54 @@
+
+/** This file contains functionality related thread management (creation, finishing, join, etc.)  */
+
+#include "MiriInterface.hpp"
+
+// CXX.rs generated headers:
+#include "genmc-sys/src/lib.rs.h"
+
+// GenMC headers:
+#include "Support/Error.hpp"
+#include "Support/Verbosity.hpp"
+
+// C++ headers:
+#include <cstdint>
+
+void MiriGenmcShim::handle_thread_create(ThreadId thread_id, ThreadId parent_id) {
+    // NOTE: The threadCreate event happens in the parent:
+    const auto pos = inc_pos(parent_id);
+    // FIXME(genmc): for supporting symmetry reduction, these will need to be properly set:
+    const unsigned fun_id = 0;
+    const SVal arg = SVal(0);
+    const ThreadInfo child_info = ThreadInfo { thread_id, parent_id, fun_id, arg };
+
+    // NOTE: Default memory ordering (`Release`) used here.
+    const auto child_tid = GenMCDriver::handleThreadCreate(pos, child_info, EventDeps());
+    // Sanity check the thread id, which is the index in the `threads_action_` array.
+    BUG_ON(child_tid != thread_id || child_tid <= 0 || child_tid != threads_action_.size());
+    threads_action_.push_back(Action(ActionKind::Load, Event(child_tid, 0)));
+}
+
+void MiriGenmcShim::handle_thread_join(ThreadId thread_id, ThreadId child_id) {
+    // The thread join event happens in the parent.
+    const auto pos = inc_pos(thread_id);
+
+    // NOTE: Default memory ordering (`Acquire`) used here.
+    const auto ret = GenMCDriver::handleThreadJoin(pos, child_id, EventDeps());
+    // If the join failed, decrease the event index again:
+    if (!std::holds_alternative<SVal>(ret)) {
+        dec_pos(thread_id);
+    }
+
+    // NOTE: Thread return value is ignored, since Miri doesn't need it.
+}
+
+void MiriGenmcShim::handle_thread_finish(ThreadId thread_id, uint64_t ret_val) {
+    const auto pos = inc_pos(thread_id);
+    // NOTE: Default memory ordering (`Release`) used here.
+    GenMCDriver::handleThreadFinish(pos, SVal(ret_val));
+}
+
+void MiriGenmcShim::handle_thread_kill(ThreadId thread_id) {
+    const auto pos = inc_pos(thread_id);
+    GenMCDriver::handleThreadKill(pos);
+}
diff --git a/src/tools/miri/genmc-sys/src/lib.rs b/src/tools/miri/genmc-sys/src/lib.rs
index ab46d729ea1..733b3d780b1 100644
--- a/src/tools/miri/genmc-sys/src/lib.rs
+++ b/src/tools/miri/genmc-sys/src/lib.rs
@@ -1,30 +1,456 @@
+use std::str::FromStr;
+use std::sync::OnceLock;
+
+pub use cxx::UniquePtr;
+
 pub use self::ffi::*;
 
+/// Defined in "genmc/src/Support/SAddr.hpp".
+/// The first bit of all global addresses must be set to `1`.
+/// This means the mask, interpreted as an address, is the lower bound of where the global address space starts.
+///
+/// FIXME(genmc): rework this if non-64bit support is added to GenMC (the current allocation scheme only allows for 64bit addresses).
+/// FIXME(genmc): currently we use `get_global_alloc_static_mask()` to ensure the constant is consistent between Miri and GenMC,
+///   but if https://github.com/dtolnay/cxx/issues/1051 is fixed we could share the constant directly.
+pub const GENMC_GLOBAL_ADDRESSES_MASK: u64 = 1 << 63;
+
+/// GenMC thread ids are C++ type `int`, which is equivalent to Rust's `i32` on most platforms.
+/// The main thread always has thread id 0.
+pub const GENMC_MAIN_THREAD_ID: i32 = 0;
+
+/// Changing GenMC's log level is not thread safe, so we limit it to only be set once to prevent any data races.
+/// This value will be initialized when the first `MiriGenmcShim` is created.
+static GENMC_LOG_LEVEL: OnceLock<LogLevel> = OnceLock::new();
+
+// Create a new handle to the GenMC model checker.
+// The first call to this function determines the log level of GenMC, any future call with a different log level will panic.
+pub fn create_genmc_driver_handle(
+    params: &GenmcParams,
+    genmc_log_level: LogLevel,
+    do_estimation: bool,
+) -> UniquePtr<MiriGenmcShim> {
+    // SAFETY: Only setting the GenMC log level once is guaranteed by the `OnceLock`.
+    // No other place calls `set_log_level_raw`, so the `logLevel` value in GenMC will not change once we initialize it once.
+    // All functions that use GenMC's `logLevel` can only be accessed in safe Rust through a `MiriGenmcShim`.
+    // There is no way to get `MiriGenmcShim` other than through `create_handle`, and we only call it *after* setting the log level, preventing any possible data races.
+    assert_eq!(
+        &genmc_log_level,
+        GENMC_LOG_LEVEL.get_or_init(|| {
+            unsafe { set_log_level_raw(genmc_log_level) };
+            genmc_log_level
+        }),
+        "Attempt to change the GenMC log level after it was already set"
+    );
+    unsafe { MiriGenmcShim::create_handle(params, do_estimation) }
+}
+
+impl GenmcScalar {
+    pub const UNINIT: Self = Self { value: 0, is_init: false };
+
+    pub const fn from_u64(value: u64) -> Self {
+        Self { value, is_init: true }
+    }
+}
+
 impl Default for GenmcParams {
     fn default() -> Self {
         Self {
+            estimation_max: 1000, // default taken from GenMC
             print_random_schedule_seed: false,
             do_symmetry_reduction: false,
-            // FIXME(GenMC): Add defaults for remaining parameters
+            // GenMC graphs can be quite large since Miri produces a lot of (non-atomic) events.
+            print_execution_graphs: ExecutiongraphPrinting::None,
+            disable_weak_memory_emulation: false,
         }
     }
 }
 
+impl Default for LogLevel {
+    fn default() -> Self {
+        // FIXME(genmc): set `Tip` by default once the GenMC tips are relevant to Miri.
+        Self::Warning
+    }
+}
+
+impl FromStr for SchedulePolicy {
+    type Err = &'static str;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        Ok(match s {
+            "wf" => SchedulePolicy::WF,
+            "wfr" => SchedulePolicy::WFR,
+            "arbitrary" | "random" => SchedulePolicy::Arbitrary,
+            "ltr" => SchedulePolicy::LTR,
+            _ => return Err("invalid scheduling policy"),
+        })
+    }
+}
+
+impl FromStr for LogLevel {
+    type Err = &'static str;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        Ok(match s {
+            "quiet" => LogLevel::Quiet,
+            "error" => LogLevel::Error,
+            "warning" => LogLevel::Warning,
+            "tip" => LogLevel::Tip,
+            "debug1" => LogLevel::Debug1Revisits,
+            "debug2" => LogLevel::Debug2MemoryAccesses,
+            "debug3" => LogLevel::Debug3ReadsFrom,
+            _ => return Err("invalid log level"),
+        })
+    }
+}
+
 #[cxx::bridge]
 mod ffi {
+    /**** Types shared between Miri/Rust and Miri/C++ through cxx_bridge: ****/
+
     /// Parameters that will be given to GenMC for setting up the model checker.
-    /// (The fields of this struct are visible to both Rust and C++)
+    /// The fields of this struct are visible to both Rust and C++.
+    /// Note that this struct is #[repr(C)], so the order of fields matters.
     #[derive(Clone, Debug)]
     struct GenmcParams {
+        /// Maximum number of executions explored in estimation mode.
+        pub estimation_max: u32,
         pub print_random_schedule_seed: bool,
         pub do_symmetry_reduction: bool,
-        // FIXME(GenMC): Add remaining parameters.
+        pub print_execution_graphs: ExecutiongraphPrinting,
+        /// Enabling this will set the memory model used by GenMC to "Sequential Consistency" (SC).
+        /// This will disable any weak memory effects, which reduces the number of program executions that will be explored.
+        pub disable_weak_memory_emulation: bool,
+    }
+
+    /// This is mostly equivalent to GenMC `VerbosityLevel`, but the debug log levels are always present (not conditionally compiled based on `ENABLE_GENMC_DEBUG`).
+    /// We add this intermediate type to prevent changes to the GenMC log-level from breaking the Miri
+    /// build, and to have a stable type for the C++-Rust interface, independent of `ENABLE_GENMC_DEBUG`.
+    #[derive(Debug)]
+    enum LogLevel {
+        /// Disable *all* logging (including error messages on a crash).
+        Quiet,
+        /// Log errors.
+        Error,
+        /// Log errors and warnings.
+        Warning,
+        /// Log errors, warnings and tips.
+        Tip,
+        /// Debug print considered revisits.
+        /// Downgraded to `Tip` if `GENMC_DEBUG` is not enabled.
+        Debug1Revisits,
+        /// Print the execution graph after every memory access.
+        /// Also includes the previous debug log level.
+        /// Downgraded to `Tip` if `GENMC_DEBUG` is not enabled.
+        Debug2MemoryAccesses,
+        /// Print reads-from values considered by GenMC.
+        /// Also includes the previous debug log level.
+        /// Downgraded to `Tip` if `GENMC_DEBUG` is not enabled.
+        Debug3ReadsFrom,
     }
+
+    #[derive(Debug)]
+    /// Setting to control which execution graphs GenMC prints after every execution.
+    enum ExecutiongraphPrinting {
+        /// Print no graphs.
+        None,
+        /// Print graphs of all fully explored executions.
+        Explored,
+        /// Print graphs of all blocked executions.
+        Blocked,
+        /// Print graphs of all executions.
+        ExploredAndBlocked,
+    }
+
+    /// This type corresponds to `Option<SVal>` (or `std::optional<SVal>`), where `SVal` is the type that GenMC uses for storing values.
+    /// CXX doesn't support `std::optional` currently, so we need to use an extra `bool` to define whether this value is initialized or not.
+    #[derive(Debug, Clone, Copy)]
+    struct GenmcScalar {
+        value: u64,
+        is_init: bool,
+    }
+
+    #[must_use]
+    #[derive(Debug, Clone, Copy)]
+    enum ExecutionState {
+        Ok,
+        Blocked,
+        Finished,
+    }
+
+    #[must_use]
+    #[derive(Debug)]
+    struct SchedulingResult {
+        exec_state: ExecutionState,
+        next_thread: i32,
+    }
+
+    #[must_use]
+    #[derive(Debug)]
+    struct EstimationResult {
+        /// Expected number of total executions.
+        mean: f64,
+        /// Standard deviation of the total executions estimate.
+        sd: f64,
+        /// Number of explored executions during the estimation.
+        explored_execs: u64,
+        /// Number of encounteded blocked executions during the estimation.
+        blocked_execs: u64,
+    }
+
+    #[must_use]
+    #[derive(Debug)]
+    struct LoadResult {
+        /// If not null, contains the error encountered during the handling of the load.
+        error: UniquePtr<CxxString>,
+        /// Indicates whether a value was read or not.
+        has_value: bool,
+        /// The value that was read. Should not be used if `has_value` is `false`.
+        read_value: GenmcScalar,
+    }
+
+    #[must_use]
+    #[derive(Debug)]
+    struct StoreResult {
+        /// If not null, contains the error encountered during the handling of the store.
+        error: UniquePtr<CxxString>,
+        /// `true` if the write should also be reflected in Miri's memory representation.
+        is_coherence_order_maximal_write: bool,
+    }
+
+    #[must_use]
+    #[derive(Debug)]
+    struct ReadModifyWriteResult {
+        /// If there was an error, it will be stored in `error`, otherwise it is `None`.
+        error: UniquePtr<CxxString>,
+        /// The value that was read by the RMW operation as the left operand.
+        old_value: GenmcScalar,
+        /// The value that was produced by the RMW operation.
+        new_value: GenmcScalar,
+        /// `true` if the write should also be reflected in Miri's memory representation.
+        is_coherence_order_maximal_write: bool,
+    }
+
+    #[must_use]
+    #[derive(Debug)]
+    struct CompareExchangeResult {
+        /// If there was an error, it will be stored in `error`, otherwise it is `None`.
+        error: UniquePtr<CxxString>,
+        /// The value that was read by the compare-exchange.
+        old_value: GenmcScalar,
+        /// `true` if compare_exchange op was successful.
+        is_success: bool,
+        /// `true` if the write should also be reflected in Miri's memory representation.
+        is_coherence_order_maximal_write: bool,
+    }
+
+    /**** These are GenMC types that we have to copy-paste here since cxx does not support
+    "importing" externally defined C++ types. ****/
+
+    #[derive(Clone, Copy, Debug)]
+    enum SchedulePolicy {
+        LTR,
+        WF,
+        WFR,
+        Arbitrary,
+    }
+
+    #[derive(Debug)]
+    /// Corresponds to GenMC's type with the same name.
+    /// Should only be modified if changed by GenMC.
+    enum ActionKind {
+        /// Any Mir terminator that's atomic and has load semantics.
+        Load,
+        /// Anything that's not a `Load`.
+        NonLoad,
+    }
+
+    #[derive(Debug)]
+    /// Corresponds to GenMC's type with the same name.
+    /// Should only be modified if changed by GenMC.
+    enum MemOrdering {
+        NotAtomic = 0,
+        Relaxed = 1,
+        // We skip 2 in case we support consume.
+        Acquire = 3,
+        Release = 4,
+        AcquireRelease = 5,
+        SequentiallyConsistent = 6,
+    }
+
+    #[derive(Debug)]
+    enum RMWBinOp {
+        Xchg = 0,
+        Add = 1,
+        Sub = 2,
+        And = 3,
+        Nand = 4,
+        Or = 5,
+        Xor = 6,
+        Max = 7,
+        Min = 8,
+        UMax = 9,
+        UMin = 10,
+    }
+
+    // # Safety
+    //
+    // This block is unsafe to allow defining safe methods inside.
+    //
+    // `get_global_alloc_static_mask` is safe since it just returns a constant.
+    // All methods on `MiriGenmcShim` are safe by the correct usage of the two unsafe functions
+    // `set_log_level_raw` and `MiriGenmcShim::create_handle`.
+    // See the doc comment on those two functions for their safety requirements.
     unsafe extern "C++" {
         include!("MiriInterface.hpp");
 
-        type MiriGenMCShim;
+        /**** Types shared between Miri/Rust and Miri/C++: ****/
+        type MiriGenmcShim;
+
+        /**** Types shared between Miri/Rust and GenMC/C++:
+        (This tells cxx that the enums defined above are already defined on the C++ side;
+        it will emit assertions to ensure that the two definitions agree.) ****/
+        type ActionKind;
+        type MemOrdering;
+        type RMWBinOp;
+        type SchedulePolicy;
+
+        /// Set the log level for GenMC.
+        ///
+        /// # Safety
+        ///
+        /// This function is not thread safe, since it writes to the global, mutable, non-atomic `logLevel` variable.
+        /// Any GenMC function may read from `logLevel` unsynchronized.
+        /// The safest way to use this function is to set the log level exactly once before first calling `create_handle`.
+        /// Never calling this function is safe, GenMC will fall back to its default log level.
+        unsafe fn set_log_level_raw(log_level: LogLevel);
+
+        /// Create a new `MiriGenmcShim`, which wraps a `GenMCDriver`.
+        ///
+        /// # Safety
+        ///
+        /// This function is marked as unsafe since the `logLevel` global variable is non-atomic.
+        /// This function should not be called in an unsynchronized way with `set_log_level_raw`, since
+        /// this function and any methods on the returned `MiriGenmcShim` may read the `logLevel`,
+        /// causing a data race.
+        /// The safest way to use these functions is to call `set_log_level_raw` once, and only then
+        /// start creating handles.
+        /// There should not be any other (safe) way to create a `MiriGenmcShim`.
+        #[Self = "MiriGenmcShim"]
+        unsafe fn create_handle(
+            params: &GenmcParams,
+            estimation_mode: bool,
+        ) -> UniquePtr<MiriGenmcShim>;
+        /// Get the bit mask that GenMC expects for global memory allocations.
+        fn get_global_alloc_static_mask() -> u64;
+
+        /// This function must be called at the start of any execution, before any events are reported to GenMC.
+        fn handle_execution_start(self: Pin<&mut MiriGenmcShim>);
+        /// This function must be called at the end of any execution, even if an error was found during the execution.
+        /// Returns `null`, or a string containing an error message if an error occured.
+        fn handle_execution_end(self: Pin<&mut MiriGenmcShim>) -> UniquePtr<CxxString>;
+
+        /***** Functions for handling events encountered during program execution. *****/
+
+        /**** Memory access handling ****/
+        fn handle_load(
+            self: Pin<&mut MiriGenmcShim>,
+            thread_id: i32,
+            address: u64,
+            size: u64,
+            memory_ordering: MemOrdering,
+            old_value: GenmcScalar,
+        ) -> LoadResult;
+        fn handle_read_modify_write(
+            self: Pin<&mut MiriGenmcShim>,
+            thread_id: i32,
+            address: u64,
+            size: u64,
+            rmw_op: RMWBinOp,
+            ordering: MemOrdering,
+            rhs_value: GenmcScalar,
+            old_value: GenmcScalar,
+        ) -> ReadModifyWriteResult;
+        fn handle_compare_exchange(
+            self: Pin<&mut MiriGenmcShim>,
+            thread_id: i32,
+            address: u64,
+            size: u64,
+            expected_value: GenmcScalar,
+            new_value: GenmcScalar,
+            old_value: GenmcScalar,
+            success_ordering: MemOrdering,
+            fail_load_ordering: MemOrdering,
+            can_fail_spuriously: bool,
+        ) -> CompareExchangeResult;
+        fn handle_store(
+            self: Pin<&mut MiriGenmcShim>,
+            thread_id: i32,
+            address: u64,
+            size: u64,
+            value: GenmcScalar,
+            old_value: GenmcScalar,
+            memory_ordering: MemOrdering,
+        ) -> StoreResult;
+        fn handle_fence(
+            self: Pin<&mut MiriGenmcShim>,
+            thread_id: i32,
+            memory_ordering: MemOrdering,
+        );
+
+        /**** Memory (de)allocation ****/
+        fn handle_malloc(
+            self: Pin<&mut MiriGenmcShim>,
+            thread_id: i32,
+            size: u64,
+            alignment: u64,
+        ) -> u64;
+        fn handle_free(self: Pin<&mut MiriGenmcShim>, thread_id: i32, address: u64);
+
+        /**** Thread management ****/
+        fn handle_thread_create(self: Pin<&mut MiriGenmcShim>, thread_id: i32, parent_id: i32);
+        fn handle_thread_join(self: Pin<&mut MiriGenmcShim>, thread_id: i32, child_id: i32);
+        fn handle_thread_finish(self: Pin<&mut MiriGenmcShim>, thread_id: i32, ret_val: u64);
+        fn handle_thread_kill(self: Pin<&mut MiriGenmcShim>, thread_id: i32);
+
+        /***** Exploration related functionality *****/
+
+        /// Ask the GenMC scheduler for a new thread to schedule and
+        /// return whether the execution is finished, blocked, or can continue.
+        /// Updates the next instruction kind for the given thread id.
+        fn schedule_next(
+            self: Pin<&mut MiriGenmcShim>,
+            curr_thread_id: i32,
+            curr_thread_next_instr_kind: ActionKind,
+        ) -> SchedulingResult;
+
+        /// Check whether there are more executions to explore.
+        /// If there are more executions, this method prepares for the next execution and returns `true`.
+        fn is_exploration_done(self: Pin<&mut MiriGenmcShim>) -> bool;
+
+        /**** Result querying functionality. ****/
+
+        // NOTE: We don't want to share the `VerificationResult` type with the Rust side, since it
+        // is very large, uses features that CXX.rs doesn't support and may change as GenMC changes.
+        // Instead, we only use the result on the C++ side, and only expose these getter function to
+        // the Rust side.
+        // Each `GenMCDriver` contains one `VerificationResult`, and each `MiriGenmcShim` contains on `GenMCDriver`.
+        // GenMC builds up the content of the `struct VerificationResult` over the course of an exploration,
+        // but it's safe to look at it at any point, since it is only accessible through exactly one `MiriGenmcShim`.
+        // All these functions for querying the result can be safely called repeatedly and at any time,
+        // though the results may be incomplete if called before `handle_execution_end`.
+
+        /// Get the number of blocked executions encountered by GenMC (cast into a fixed with integer)
+        fn get_blocked_execution_count(self: &MiriGenmcShim) -> u64;
+        /// Get the number of executions explored by GenMC (cast into a fixed with integer)
+        fn get_explored_execution_count(self: &MiriGenmcShim) -> u64;
+        /// Get all messages that GenMC produced (errors, warnings), combined into one string.
+        fn get_result_message(self: &MiriGenmcShim) -> UniquePtr<CxxString>;
+        /// If an error occurred, return a string describing the error, otherwise, return `nullptr`.
+        fn get_error_string(self: &MiriGenmcShim) -> UniquePtr<CxxString>;
+
+        /**** Printing functionality. ****/
 
-        fn createGenmcHandle(config: &GenmcParams) -> UniquePtr<MiriGenMCShim>;
+        /// Get the results of a run in estimation mode.
+        fn get_estimation_results(self: &MiriGenmcShim) -> EstimationResult;
     }
 }
diff --git a/src/tools/miri/genmc-sys/src_cpp/MiriInterface.cpp b/src/tools/miri/genmc-sys/src_cpp/MiriInterface.cpp
deleted file mode 100644
index 0827bb3d407..00000000000
--- a/src/tools/miri/genmc-sys/src_cpp/MiriInterface.cpp
+++ /dev/null
@@ -1,50 +0,0 @@
-#include "MiriInterface.hpp"
-
-#include "genmc-sys/src/lib.rs.h"
-
-auto MiriGenMCShim::createHandle(const GenmcParams &config)
-	-> std::unique_ptr<MiriGenMCShim>
-{
-	auto conf = std::make_shared<Config>();
-
-	// Miri needs all threads to be replayed, even fully completed ones.
-	conf->replayCompletedThreads = true;
-
-	// We only support the RC11 memory model for Rust.
-	conf->model = ModelType::RC11;
-
-	conf->printRandomScheduleSeed = config.print_random_schedule_seed;
-
-	// FIXME(genmc): disable any options we don't support currently:
-	conf->ipr = false;
-	conf->disableBAM = true;
-	conf->instructionCaching = false;
-
-	ERROR_ON(config.do_symmetry_reduction, "Symmetry reduction is currently unsupported in GenMC mode.");
-	conf->symmetryReduction = config.do_symmetry_reduction;
-
-	// FIXME(genmc): Should there be a way to change this option from Miri?
-	conf->schedulePolicy = SchedulePolicy::WF;
-
-	// FIXME(genmc): implement estimation mode:
-	conf->estimate = false;
-	conf->estimationMax = 1000;
-	const auto mode = conf->estimate ? GenMCDriver::Mode(GenMCDriver::EstimationMode{})
-									  : GenMCDriver::Mode(GenMCDriver::VerificationMode{});
-
-	// Running Miri-GenMC without race detection is not supported.
-	// Disabling this option also changes the behavior of the replay scheduler to only schedule at atomic operations, which is required with Miri.
-	// This happens because Miri can generate multiple GenMC events for a single MIR terminator. Without this option,
-	// the scheduler might incorrectly schedule an atomic MIR terminator because the first event it creates is a non-atomic (e.g., `StorageLive`).
-	conf->disableRaceDetection = false;
-
-	// Miri can already check for unfreed memory. Also, GenMC cannot distinguish between memory
-	// that is allowed to leak and memory that is not.
-	conf->warnUnfreedMemory = false;
-
-	// FIXME(genmc): check config:
-	// checkConfigOptions(*conf);
-
-	auto driver = std::make_unique<MiriGenMCShim>(std::move(conf), mode);
-	return driver;
-}
diff --git a/src/tools/miri/genmc-sys/src_cpp/MiriInterface.hpp b/src/tools/miri/genmc-sys/src_cpp/MiriInterface.hpp
deleted file mode 100644
index e55522ef418..00000000000
--- a/src/tools/miri/genmc-sys/src_cpp/MiriInterface.hpp
+++ /dev/null
@@ -1,44 +0,0 @@
-#ifndef GENMC_MIRI_INTERFACE_HPP
-#define GENMC_MIRI_INTERFACE_HPP
-
-#include "rust/cxx.h"
-
-#include "config.h"
-
-#include "Config/Config.hpp"
-#include "Verification/GenMCDriver.hpp"
-
-#include <iostream>
-
-/**** Types available to Miri ****/
-
-// Config struct defined on the Rust side and translated to C++ by cxx.rs:
-struct GenmcParams;
-
-struct MiriGenMCShim : private GenMCDriver
-{
-
-public:
-	MiriGenMCShim(std::shared_ptr<const Config> conf, Mode mode /* = VerificationMode{} */)
-		: GenMCDriver(std::move(conf), nullptr, mode)
-	{
-		std::cerr << "C++: GenMC handle created!" << std::endl;
-	}
-
-	virtual ~MiriGenMCShim()
-	{
-		std::cerr << "C++: GenMC handle destroyed!" << std::endl;
-	}
-
-	static std::unique_ptr<MiriGenMCShim> createHandle(const GenmcParams &config);
-};
-
-/**** Functions available to Miri ****/
-
-// NOTE: CXX doesn't support exposing static methods to Rust currently, so we expose this function instead.
-static inline auto createGenmcHandle(const GenmcParams &config) -> std::unique_ptr<MiriGenMCShim>
-{
-	return MiriGenMCShim::createHandle(config);
-}
-
-#endif /* GENMC_MIRI_INTERFACE_HPP */
diff --git a/src/tools/miri/miri-script/src/commands.rs b/src/tools/miri/miri-script/src/commands.rs
index ee09b9b4b73..f1b52293123 100644
--- a/src/tools/miri/miri-script/src/commands.rs
+++ b/src/tools/miri/miri-script/src/commands.rs
@@ -130,15 +130,15 @@ impl Command {
         let new_commit = sh.read_file("rust-version")?.trim().to_owned();
         let current_commit = {
             let rustc_info = cmd!(sh, "rustc +miri --version -v").read();
-            if rustc_info.is_err() {
-                None
-            } else {
-                let metadata = rustc_version::version_meta_for(&rustc_info.unwrap())?;
+            if let Ok(rustc_info) = rustc_info {
+                let metadata = rustc_version::version_meta_for(&rustc_info)?;
                 Some(
                     metadata
                         .commit_hash
                         .ok_or_else(|| anyhow!("rustc metadata did not contain commit hash"))?,
                 )
+            } else {
+                None
             }
         };
         // Check if we already are at that commit.
diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version
index 2178caf6396..388e88fe43e 100644
--- a/src/tools/miri/rust-version
+++ b/src/tools/miri/rust-version
@@ -1 +1 @@
-733dab558992d902d6d17576de1da768094e2cf3
+f6092f224d2b1774b31033f12d0bee626943b02f
diff --git a/src/tools/miri/src/alloc_addresses/address_generator.rs b/src/tools/miri/src/alloc_addresses/address_generator.rs
new file mode 100644
index 00000000000..3764f5dcb82
--- /dev/null
+++ b/src/tools/miri/src/alloc_addresses/address_generator.rs
@@ -0,0 +1,78 @@
+use std::ops::Range;
+
+use rand::Rng;
+use rustc_abi::{Align, Size};
+use rustc_const_eval::interpret::{InterpResult, interp_ok};
+use rustc_middle::{err_exhaust, throw_exhaust};
+
+/// Shifts `addr` to make it aligned with `align` by rounding `addr` to the smallest multiple
+/// of `align` that is larger or equal to `addr`
+fn align_addr(addr: u64, align: u64) -> u64 {
+    match addr % align {
+        0 => addr,
+        rem => addr.strict_add(align) - rem,
+    }
+}
+
+/// This provides the logic to generate addresses for memory allocations in a given address range.
+#[derive(Debug)]
+pub struct AddressGenerator {
+    /// This is used as a memory address when a new pointer is casted to an integer. It
+    /// is always larger than any address that was previously made part of a block.
+    next_base_addr: u64,
+    /// This is the last address that can be allocated.
+    end: u64,
+}
+
+impl AddressGenerator {
+    pub fn new(addr_range: Range<u64>) -> Self {
+        Self { next_base_addr: addr_range.start, end: addr_range.end }
+    }
+
+    /// Get the remaining range where this `AddressGenerator` can still allocate addresses.
+    pub fn get_remaining(&self) -> Range<u64> {
+        self.next_base_addr..self.end
+    }
+
+    /// Generate a new address with the specified size and alignment, using the given Rng to add some randomness.
+    /// The returned allocation is guaranteed not to overlap with any address ranges given out by the generator before.
+    /// Returns an error if the allocation request cannot be fulfilled.
+    pub fn generate<'tcx, R: Rng>(
+        &mut self,
+        size: Size,
+        align: Align,
+        rng: &mut R,
+    ) -> InterpResult<'tcx, u64> {
+        // Leave some space to the previous allocation, to give it some chance to be less aligned.
+        // We ensure that `(self.next_base_addr + slack) % 16` is uniformly distributed.
+        let slack = rng.random_range(0..16);
+        // From next_base_addr + slack, round up to adjust for alignment.
+        let base_addr =
+            self.next_base_addr.checked_add(slack).ok_or_else(|| err_exhaust!(AddressSpaceFull))?;
+        let base_addr = align_addr(base_addr, 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!)
+        self.next_base_addr = base_addr
+            .checked_add(size.bytes().max(1))
+            .ok_or_else(|| err_exhaust!(AddressSpaceFull))?;
+        // Even if `Size` didn't overflow, we might still have filled up the address space.
+        if self.next_base_addr > self.end {
+            throw_exhaust!(AddressSpaceFull);
+        }
+        interp_ok(base_addr)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_align_addr() {
+        assert_eq!(align_addr(37, 4), 40);
+        assert_eq!(align_addr(44, 4), 44);
+    }
+}
diff --git a/src/tools/miri/src/alloc_addresses/mod.rs b/src/tools/miri/src/alloc_addresses/mod.rs
index 334503d2994..f011ee71785 100644
--- a/src/tools/miri/src/alloc_addresses/mod.rs
+++ b/src/tools/miri/src/alloc_addresses/mod.rs
@@ -1,15 +1,16 @@
 //! This module is responsible for managing the absolute addresses that allocations are located at,
 //! and for casting between pointers and integers based on those addresses.
 
+mod address_generator;
 mod reuse_pool;
 
 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_middle::ty::TyCtxt;
 
+pub use self::address_generator::AddressGenerator;
 use self::reuse_pool::ReusePool;
 use crate::concurrency::VClock;
 use crate::*;
@@ -33,6 +34,8 @@ pub struct GlobalStateInner {
     /// sorted by address. We cannot use a `HashMap` since we can be given an address that is offset
     /// from the base address, and we need to find the `AllocId` it belongs to. This is not the
     /// *full* inverse of `base_addr`; dead allocations have been removed.
+    /// Note that in GenMC mode, dead allocations are *not* removed -- and also, addresses are never
+    /// reused. This lets us use the address as a cross-execution-stable identifier for an allocation.
     int_to_ptr_map: Vec<(u64, AllocId)>,
     /// The base address for each allocation.  We cannot put that into
     /// `AllocExtra` because function pointers also have a base address, and
@@ -49,9 +52,8 @@ pub struct GlobalStateInner {
     /// Whether an allocation has been exposed or not. This cannot be put
     /// into `AllocExtra` for the same reason as `base_addr`.
     exposed: FxHashSet<AllocId>,
-    /// This is used as a memory address when a new pointer is casted to an integer. It
-    /// is always larger than any address that was previously made part of a block.
-    next_base_addr: u64,
+    /// The generator for new addresses in a given range.
+    address_generator: AddressGenerator,
     /// The provenance to use for int2ptr casts
     provenance_mode: ProvenanceMode,
 }
@@ -64,7 +66,7 @@ impl VisitProvenance for GlobalStateInner {
             prepared_alloc_bytes: _,
             reuse: _,
             exposed: _,
-            next_base_addr: _,
+            address_generator: _,
             provenance_mode: _,
         } = self;
         // Though base_addr, int_to_ptr_map, and exposed contain AllocIds, we do not want to visit them.
@@ -77,14 +79,14 @@ impl VisitProvenance for GlobalStateInner {
 }
 
 impl GlobalStateInner {
-    pub fn new(config: &MiriConfig, stack_addr: u64) -> Self {
+    pub fn new<'tcx>(config: &MiriConfig, stack_addr: u64, tcx: TyCtxt<'tcx>) -> Self {
         GlobalStateInner {
             int_to_ptr_map: Vec::default(),
             base_addr: FxHashMap::default(),
             prepared_alloc_bytes: FxHashMap::default(),
             reuse: ReusePool::new(config),
             exposed: FxHashSet::default(),
-            next_base_addr: stack_addr,
+            address_generator: AddressGenerator::new(stack_addr..tcx.target_usize_max()),
             provenance_mode: config.provenance_mode,
         }
     }
@@ -96,15 +98,6 @@ impl GlobalStateInner {
     }
 }
 
-/// Shifts `addr` to make it aligned with `align` by rounding `addr` to the smallest multiple
-/// of `align` that is larger or equal to `addr`
-fn align_addr(addr: u64, align: u64) -> u64 {
-    match addr % align {
-        0 => addr,
-        rem => addr.strict_add(align) - rem,
-    }
-}
-
 impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
 trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn addr_from_alloc_id_uncached(
@@ -132,7 +125,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // Miri's address assignment leaks state across thread boundaries, which is incompatible
         // with GenMC execution. So we instead let GenMC assign addresses to allocations.
         if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() {
-            let addr = genmc_ctx.handle_alloc(&this.machine, info.size, info.align, memory_kind)?;
+            let addr =
+                genmc_ctx.handle_alloc(this, alloc_id, info.size, info.align, memory_kind)?;
             return interp_ok(addr);
         }
 
@@ -189,39 +183,22 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
             this.active_thread(),
         ) {
             if let Some(clock) = clock {
-                this.acquire_clock(&clock);
+                this.acquire_clock(&clock)?;
             }
             interp_ok(reuse_addr)
         } else {
             // We have to pick a fresh address.
-            // Leave some space to the previous allocation, to give it some chance to be less aligned.
-            // We ensure that `(global_state.next_base_addr + slack) % 16` is uniformly distributed.
-            let slack = rng.random_range(0..16);
-            // From next_base_addr + slack, round up to adjust for alignment.
-            let base_addr = global_state
-                .next_base_addr
-                .checked_add(slack)
-                .ok_or_else(|| err_exhaust!(AddressSpaceFull))?;
-            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(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 > this.target_usize_max() {
-                throw_exhaust!(AddressSpaceFull);
-            }
+            let new_addr =
+                global_state.address_generator.generate(info.size, info.align, &mut rng)?;
+
             // If we filled up more than half the address space, start aggressively reusing
             // addresses to avoid running out.
-            if global_state.next_base_addr > u64::try_from(this.target_isize_max()).unwrap() {
+            let remaining_range = global_state.address_generator.get_remaining();
+            if remaining_range.start > remaining_range.end / 2 {
                 global_state.reuse.address_space_shortage();
             }
 
-            interp_ok(base_addr)
+            interp_ok(new_addr)
         }
     }
 }
@@ -268,7 +245,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // We only use this provenance if it has been exposed, or if the caller requested also non-exposed allocations
         if !only_exposed_allocations || 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!(this.is_alloc_live(alloc_id));
+            // In GenMC mode, we keep all allocations, so this check doesn't apply there.
+            if this.machine.data_race.as_genmc_ref().is_none() {
+                debug_assert!(this.is_alloc_live(alloc_id));
+            }
             Some(alloc_id)
         } else {
             None
@@ -485,6 +465,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
 impl<'tcx> MiriMachine<'tcx> {
     pub fn free_alloc_id(&mut self, dead_id: AllocId, size: Size, align: Align, kind: MemoryKind) {
+        // In GenMC mode, we can't remove dead allocation info since such pointers can
+        // still be stored in atomics and we need this info to convert GenMC pointers to Miri pointers.
+        // `global_state.reuse` is also unused so we can just skip this entire function.
+        if self.data_race.as_genmc_ref().is_some() {
+            return;
+        }
+
         let global_state = self.alloc_addresses.get_mut();
         let rng = self.rng.get_mut();
 
@@ -511,6 +498,8 @@ impl<'tcx> MiriMachine<'tcx> {
         // Also remember this address for future reuse.
         let thread = self.threads.active_thread();
         global_state.reuse.add_addr(rng, addr, size, align, kind, thread, || {
+            // We already excluded GenMC above. We cannot use `self.release_clock` as
+            // `self.alloc_addresses` is borrowed.
             if let Some(data_race) = self.data_race.as_vclocks_ref() {
                 data_race.release_clock(&self.threads, |clock| clock.clone())
             } else {
@@ -519,14 +508,3 @@ impl<'tcx> MiriMachine<'tcx> {
         })
     }
 }
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-
-    #[test]
-    fn test_align_addr() {
-        assert_eq!(align_addr(37, 4), 40);
-        assert_eq!(align_addr(44, 4), 44);
-    }
-}
diff --git a/src/tools/miri/src/bin/log/mod.rs b/src/tools/miri/src/bin/log/mod.rs
index f3b2fdb5348..22f74dd46b5 100644
--- a/src/tools/miri/src/bin/log/mod.rs
+++ b/src/tools/miri/src/bin/log/mod.rs
@@ -1,2 +1,3 @@
 pub mod setup;
 mod tracing_chrome;
+mod tracing_chrome_instant;
diff --git a/src/tools/miri/src/bin/log/tracing_chrome.rs b/src/tools/miri/src/bin/log/tracing_chrome.rs
index 3379816550c..310887a13a5 100644
--- a/src/tools/miri/src/bin/log/tracing_chrome.rs
+++ b/src/tools/miri/src/bin/log/tracing_chrome.rs
@@ -7,12 +7,15 @@
 //! (`git log -- path/to/tracing_chrome.rs`), but in summary:
 //! - the file attributes were changed and `extern crate` was added at the top
 //! - if a tracing span has a field called "tracing_separate_thread", it will be given a separate
-//! span ID even in [TraceStyle::Threaded] mode, to make it appear on a separate line when viewing
-//! the trace in <https://ui.perfetto.dev>. This is the syntax to trigger this behavior:
+//!   span ID even in [TraceStyle::Threaded] mode, to make it appear on a separate line when viewing
+//!   the trace in <https://ui.perfetto.dev>. This is the syntax to trigger this behavior:
 //!   ```rust
 //!   tracing::info_span!("my_span", tracing_separate_thread = tracing::field::Empty, /* ... */)
 //!   ```
-//! - use i64 instead of u64 for the "id" in [ChromeLayer::get_root_id] to be compatible with Perfetto
+//! - use i64 instead of u64 for the "id" in [ChromeLayer::get_root_id] to be compatible with
+//!   Perfetto
+//! - use [ChromeLayer::with_elapsed_micros_subtracting_tracing] to make time measurements faster on
+//!   Linux x86/x86_64 and to subtract time spent tracing from the timestamps in the trace file
 //!
 //! Depending on the tracing-chrome crate from crates.io is unfortunately not possible, since it
 //! depends on `tracing_core` which conflicts with rustc_private's `tracing_core` (meaning it would
@@ -50,9 +53,22 @@ use std::{
     thread::JoinHandle,
 };
 
+use crate::log::tracing_chrome_instant::TracingChromeInstant;
+
+/// Contains thread-local data for threads that send tracing spans or events.
+struct ThreadData {
+    /// A unique ID for this thread, will populate "tid" field in the output trace file.
+    tid: usize,
+    /// A clone of [ChromeLayer::out] to avoid the expensive operation of accessing a mutex
+    /// every time. This is used to send [Message]s to the thread that saves trace data to file.
+    out: Sender<Message>,
+    /// The instant in time this thread was started. All events happening on this thread will be
+    /// saved to the trace file with a timestamp (the "ts" field) measured relative to this instant.
+    start: TracingChromeInstant,
+}
+
 thread_local! {
-    static OUT: RefCell<Option<Sender<Message>>> = const { RefCell::new(None) };
-    static TID: RefCell<Option<usize>> = const { RefCell::new(None) };
+    static THREAD_DATA: RefCell<Option<ThreadData>> = const { RefCell::new(None) };
 }
 
 type NameFn<S> = Box<dyn Fn(&EventOrSpan<'_, '_, S>) -> String + Send + Sync>;
@@ -64,7 +80,6 @@ where
     S: Subscriber + for<'span> LookupSpan<'span> + Send + Sync,
 {
     out: Arc<Mutex<Sender<Message>>>,
-    start: std::time::Instant,
     max_tid: AtomicUsize,
     include_args: bool,
     include_locations: bool,
@@ -323,7 +338,6 @@ where
 {
     fn new(mut builder: ChromeLayerBuilder<S>) -> (ChromeLayer<S>, FlushGuard) {
         let (tx, rx) = mpsc::channel();
-        OUT.with(|val| val.replace(Some(tx.clone())));
 
         let out_writer = builder
             .out_writer
@@ -443,7 +457,6 @@ where
         };
         let layer = ChromeLayer {
             out: Arc::new(Mutex::new(tx)),
-            start: std::time::Instant::now(),
             max_tid: AtomicUsize::new(0),
             name_fn: builder.name_fn.take(),
             cat_fn: builder.cat_fn.take(),
@@ -456,22 +469,7 @@ where
         (layer, guard)
     }
 
-    fn get_tid(&self) -> (usize, bool) {
-        TID.with(|value| {
-            let tid = *value.borrow();
-            match tid {
-                Some(tid) => (tid, false),
-                None => {
-                    let tid = self.max_tid.fetch_add(1, Ordering::SeqCst);
-                    value.replace(Some(tid));
-                    (tid, true)
-                }
-            }
-        })
-    }
-
-    fn get_callsite(&self, data: EventOrSpan<S>) -> Callsite {
-        let (tid, new_thread) = self.get_tid();
+    fn get_callsite(&self, data: EventOrSpan<S>, tid: usize) -> Callsite {
         let name = self.name_fn.as_ref().map(|name_fn| name_fn(&data));
         let target = self.cat_fn.as_ref().map(|cat_fn| cat_fn(&data));
         let meta = match data {
@@ -502,14 +500,6 @@ where
             (None, None)
         };
 
-        if new_thread {
-            let name = match std::thread::current().name() {
-                Some(name) => name.to_owned(),
-                None => tid.to_string(),
-            };
-            self.send_message(Message::NewThread(tid, name));
-        }
-
         Callsite {
             tid,
             name,
@@ -548,31 +538,55 @@ where
         }
     }
 
-    fn enter_span(&self, span: SpanRef<S>, ts: f64) {
-        let callsite = self.get_callsite(EventOrSpan::Span(&span));
+    fn enter_span(&self, span: SpanRef<S>, ts: f64, tid: usize, out: &Sender<Message>) {
+        let callsite = self.get_callsite(EventOrSpan::Span(&span), tid);
         let root_id = self.get_root_id(span);
-        self.send_message(Message::Enter(ts, callsite, root_id));
+        let _ignored = out.send(Message::Enter(ts, callsite, root_id));
     }
 
-    fn exit_span(&self, span: SpanRef<S>, ts: f64) {
-        let callsite = self.get_callsite(EventOrSpan::Span(&span));
+    fn exit_span(&self, span: SpanRef<S>, ts: f64, tid: usize, out: &Sender<Message>) {
+        let callsite = self.get_callsite(EventOrSpan::Span(&span), tid);
         let root_id = self.get_root_id(span);
-        self.send_message(Message::Exit(ts, callsite, root_id));
+        let _ignored = out.send(Message::Exit(ts, callsite, root_id));
     }
 
-    fn get_ts(&self) -> f64 {
-        self.start.elapsed().as_nanos() as f64 / 1000.0
-    }
+    /// Helper function that measures how much time is spent while executing `f` and accounts for it
+    /// in subsequent calls, with the aim to reduce biases in the data collected by `tracing_chrome`
+    /// by subtracting the time spent inside tracing functions from the timeline. This makes it so
+    /// that the time spent inside the `tracing_chrome` functions does not impact the timestamps
+    /// inside the trace file (i.e. `ts`), even if such functions are slow (e.g. because they need
+    /// to format arguments on the same thread those arguments are collected on, otherwise memory
+    /// safety would be broken).
+    ///
+    /// `f` is called with the microseconds elapsed since the current thread was started (**not**
+    /// since the program start!), with the current thread ID (i.e. `tid`), and with a [Sender] that
+    /// can be used to send a [Message] to the thread that collects [Message]s and saves them to the
+    /// trace file.
+    #[inline(always)]
+    fn with_elapsed_micros_subtracting_tracing(&self, f: impl Fn(f64, usize, &Sender<Message>)) {
+        THREAD_DATA.with(|value| {
+            let mut thread_data = value.borrow_mut();
+            let (ThreadData { tid, out, start }, new_thread) = match thread_data.as_mut() {
+                Some(thread_data) => (thread_data, false),
+                None => {
+                    let tid = self.max_tid.fetch_add(1, Ordering::SeqCst);
+                    let out = self.out.lock().unwrap().clone();
+                    let start = TracingChromeInstant::setup_for_thread_and_start(tid);
+                    *thread_data = Some(ThreadData { tid, out, start });
+                    (thread_data.as_mut().unwrap(), true)
+                }
+            };
 
-    fn send_message(&self, message: Message) {
-        OUT.with(move |val| {
-            if val.borrow().is_some() {
-                let _ignored = val.borrow().as_ref().unwrap().send(message);
-            } else {
-                let out = self.out.lock().unwrap().clone();
-                let _ignored = out.send(message);
-                val.replace(Some(out));
-            }
+            start.with_elapsed_micros_subtracting_tracing(|ts| {
+                if new_thread {
+                    let name = match std::thread::current().name() {
+                        Some(name) => name.to_owned(),
+                        None => tid.to_string(),
+                    };
+                    let _ignored = out.send(Message::NewThread(*tid, name));
+                }
+                f(ts, *tid, out);
+            });
         });
     }
 }
@@ -586,52 +600,58 @@ where
             return;
         }
 
-        let ts = self.get_ts();
-        self.enter_span(ctx.span(id).expect("Span not found."), ts);
+        self.with_elapsed_micros_subtracting_tracing(|ts, tid, out| {
+            self.enter_span(ctx.span(id).expect("Span not found."), ts, tid, out);
+        });
     }
 
     fn on_record(&self, id: &span::Id, values: &span::Record<'_>, ctx: Context<'_, S>) {
         if self.include_args {
-            let span = ctx.span(id).unwrap();
-            let mut exts = span.extensions_mut();
+            self.with_elapsed_micros_subtracting_tracing(|_, _, _| {
+                let span = ctx.span(id).unwrap();
+                let mut exts = span.extensions_mut();
 
-            let args = exts.get_mut::<ArgsWrapper>();
+                let args = exts.get_mut::<ArgsWrapper>();
 
-            if let Some(args) = args {
-                let args = Arc::make_mut(&mut args.args);
-                values.record(&mut JsonVisitor { object: args });
-            }
+                if let Some(args) = args {
+                    let args = Arc::make_mut(&mut args.args);
+                    values.record(&mut JsonVisitor { object: args });
+                }
+            });
         }
     }
 
     fn on_event(&self, event: &Event<'_>, _ctx: Context<'_, S>) {
-        let ts = self.get_ts();
-        let callsite = self.get_callsite(EventOrSpan::Event(event));
-        self.send_message(Message::Event(ts, callsite));
+        self.with_elapsed_micros_subtracting_tracing(|ts, tid, out| {
+            let callsite = self.get_callsite(EventOrSpan::Event(event), tid);
+            let _ignored = out.send(Message::Event(ts, callsite));
+        });
     }
 
     fn on_exit(&self, id: &span::Id, ctx: Context<'_, S>) {
         if let TraceStyle::Async = self.trace_style {
             return;
         }
-        let ts = self.get_ts();
-        self.exit_span(ctx.span(id).expect("Span not found."), ts);
+        self.with_elapsed_micros_subtracting_tracing(|ts, tid, out| {
+            self.exit_span(ctx.span(id).expect("Span not found."), ts, tid, out);
+        });
     }
 
     fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: Context<'_, S>) {
-        if self.include_args {
-            let mut args = Object::new();
-            attrs.record(&mut JsonVisitor { object: &mut args });
-            ctx.span(id).unwrap().extensions_mut().insert(ArgsWrapper {
-                args: Arc::new(args),
-            });
-        }
-        if let TraceStyle::Threaded = self.trace_style {
-            return;
-        }
+        self.with_elapsed_micros_subtracting_tracing(|ts, tid, out| {
+            if self.include_args {
+                let mut args = Object::new();
+                attrs.record(&mut JsonVisitor { object: &mut args });
+                ctx.span(id).unwrap().extensions_mut().insert(ArgsWrapper {
+                    args: Arc::new(args),
+                });
+            }
+            if let TraceStyle::Threaded = self.trace_style {
+                return;
+            }
 
-        let ts = self.get_ts();
-        self.enter_span(ctx.span(id).expect("Span not found."), ts);
+            self.enter_span(ctx.span(id).expect("Span not found."), ts, tid, out);
+        });
     }
 
     fn on_close(&self, id: span::Id, ctx: Context<'_, S>) {
@@ -639,8 +659,9 @@ where
             return;
         }
 
-        let ts = self.get_ts();
-        self.exit_span(ctx.span(&id).expect("Span not found."), ts);
+        self.with_elapsed_micros_subtracting_tracing(|ts, tid, out| {
+            self.exit_span(ctx.span(&id).expect("Span not found."), ts, tid, out);
+        });
     }
 }
 
diff --git a/src/tools/miri/src/bin/log/tracing_chrome_instant.rs b/src/tools/miri/src/bin/log/tracing_chrome_instant.rs
new file mode 100644
index 00000000000..f400bc20a7b
--- /dev/null
+++ b/src/tools/miri/src/bin/log/tracing_chrome_instant.rs
@@ -0,0 +1,183 @@
+//! Code in this class was in part inspired by
+//! <https://github.com/tikv/minstant/blob/27c9ec5ec90b5b67113a748a4defee0d2519518c/src/tsc_now.rs>.
+//! A useful resource is also
+//! <https://www.pingcap.com/blog/how-we-trace-a-kv-database-with-less-than-5-percent-performance-impact/>,
+//! although this file does not implement TSC synchronization but insteads pins threads to CPUs,
+//! since the former is not reliable (i.e. it might lead to non-monotonic time measurements).
+//! Another useful resource for future improvements might be measureme's time measurement utils:
+//! <https://github.com/rust-lang/measureme/blob/master/measureme/src/counters.rs>.
+//! Documentation about how the Linux kernel chooses a clock source can be found here:
+//! <https://btorpey.github.io/blog/2014/02/18/clock-sources-in-linux/>.
+#![cfg(feature = "tracing")]
+
+/// This alternative `TracingChromeInstant` implementation was made entirely to suit the needs of
+/// [crate::log::tracing_chrome], and shouldn't be used for anything else. It featues two functions:
+/// - [TracingChromeInstant::setup_for_thread_and_start], which sets up the current thread to do
+///   proper time tracking and returns a point in time to use as "t=0", and
+/// - [TracingChromeInstant::with_elapsed_micros_subtracting_tracing], which allows
+///   obtaining how much time elapsed since [TracingChromeInstant::setup_for_thread_and_start] was
+///   called while accounting for (and subtracting) the time spent inside tracing-related functions.
+///
+/// This measures time using [std::time::Instant], except for x86/x86_64 Linux machines, where
+/// [std::time::Instant] is too slow (~1.5us) and thus `rdtsc` is used instead (~5ns).
+pub enum TracingChromeInstant {
+    WallTime {
+        /// The time at which this instant was created, shifted forward to account
+        /// for time spent in tracing functions as explained in
+        /// [TracingChromeInstant::with_elapsed_micros_subtracting_tracing]'s comments.
+        start_instant: std::time::Instant,
+    },
+    #[cfg(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64")))]
+    Tsc {
+        /// The value in the TSC counter when this instant was created, shifted forward to account
+        /// for time spent in tracing functions as explained in
+        /// [TracingChromeInstant::with_elapsed_micros_subtracting_tracing]'s comments.
+        start_tsc: u64,
+        /// The period of the TSC counter in microseconds.
+        tsc_to_microseconds: f64,
+    },
+}
+
+impl TracingChromeInstant {
+    /// Can be thought of as the same as [std::time::Instant::now()], but also does some setup to
+    /// make TSC stable in case TSC is available. This is supposed to be called (at most) once per
+    /// thread since the thread setup takes a few milliseconds.
+    ///
+    /// WARNING: If TSC is available, `incremental_thread_id` is used to pick to which CPU to pin
+    /// the current thread. Thread IDs should be assigned contiguously starting from 0. Be aware
+    /// that the current thread will be restricted to one CPU for the rest of the execution!
+    pub fn setup_for_thread_and_start(incremental_thread_id: usize) -> TracingChromeInstant {
+        #[cfg(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64")))]
+        if *tsc::IS_TSC_AVAILABLE.get_or_init(tsc::is_tsc_available) {
+            // We need to lock this thread to a specific CPU, because CPUs' TSC timers might be out
+            // of sync.
+            tsc::set_cpu_affinity(incremental_thread_id);
+
+            // Can only use tsc_to_microseconds() and rdtsc() after having set the CPU affinity!
+            // We compute tsc_to_microseconds anew for every new thread just in case some CPU core
+            // has a different TSC frequency.
+            let tsc_to_microseconds = tsc::tsc_to_microseconds();
+            let start_tsc = tsc::rdtsc();
+            return TracingChromeInstant::Tsc { start_tsc, tsc_to_microseconds };
+        }
+
+        let _ = incremental_thread_id; // otherwise we get a warning when the TSC branch is disabled
+        TracingChromeInstant::WallTime { start_instant: std::time::Instant::now() }
+    }
+
+    /// Calls `f` with the time elapsed in microseconds since this [TracingChromeInstant] was built
+    /// by [TracingChromeInstant::setup_for_thread_and_start], while subtracting all time previously
+    /// spent executing other `f`s passed to this function. This behavior allows subtracting time
+    /// spent in functions that log tracing data (which `f` is supposed to be) from the tracing time
+    /// measurements.
+    ///
+    /// Note: microseconds are used as the time unit since that's what Chrome trace files should
+    /// contain, see the definition of the "ts" field in
+    /// <https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview>.
+    #[inline(always)]
+    pub fn with_elapsed_micros_subtracting_tracing(&mut self, f: impl Fn(f64)) {
+        match self {
+            TracingChromeInstant::WallTime { start_instant } => {
+                // Obtain the current time (before executing `f`).
+                let instant_before_f = std::time::Instant::now();
+
+                // Using the current time (`instant_before_f`) and the `start_instant` stored in
+                // `self`, calculate the elapsed time (in microseconds) since this instant was
+                // instantiated, accounting for any time that was previously spent executing `f`.
+                // The "accounting" part is not computed in this line, but is rather done by
+                // shifting forward the `start_instant` down below.
+                let ts = (instant_before_f - *start_instant).as_nanos() as f64 / 1000.0;
+
+                // Run the function (supposedly a function internal to the tracing infrastructure).
+                f(ts);
+
+                // Measure how much time was spent executing `f` and shift `start_instant` forward
+                // by that amount. This "removes" that time from the trace.
+                *start_instant += std::time::Instant::now() - instant_before_f;
+            }
+
+            #[cfg(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64")))]
+            TracingChromeInstant::Tsc { start_tsc, tsc_to_microseconds } => {
+                // the comments above also apply here, since it's the same logic
+                let tsc_before_f = tsc::rdtsc();
+                let ts = ((tsc_before_f - *start_tsc) as f64) * (*tsc_to_microseconds);
+                f(ts);
+                *start_tsc += tsc::rdtsc() - tsc_before_f;
+            }
+        }
+    }
+}
+
+#[cfg(all(target_os = "linux", any(target_arch = "x86", target_arch = "x86_64")))]
+mod tsc {
+
+    pub static IS_TSC_AVAILABLE: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
+
+    /// Reads the timestamp-counter register. Will give monotonic answers only when called from the
+    /// same thread, because the TSC of different CPUs might be out of sync.
+    #[inline(always)]
+    pub(super) fn rdtsc() -> u64 {
+        #[cfg(target_arch = "x86")]
+        use core::arch::x86::_rdtsc;
+        #[cfg(target_arch = "x86_64")]
+        use core::arch::x86_64::_rdtsc;
+
+        unsafe { _rdtsc() }
+    }
+
+    /// Estimates the frequency of the TSC counter by waiting 10ms in a busy loop and
+    /// looking at how much the TSC increased in the meantime.
+    pub(super) fn tsc_to_microseconds() -> f64 {
+        const BUSY_WAIT: std::time::Duration = std::time::Duration::from_millis(10);
+        let tsc_start = rdtsc();
+        let instant_start = std::time::Instant::now();
+        while instant_start.elapsed() < BUSY_WAIT {
+            // `thread::sleep()` is not very precise at waking up the program at the right time,
+            // so use a busy loop instead.
+            core::hint::spin_loop();
+        }
+        let tsc_end = rdtsc();
+        (BUSY_WAIT.as_nanos() as f64) / 1000.0 / ((tsc_end - tsc_start) as f64)
+    }
+
+    /// Checks whether the TSC counter is available and runs at a constant rate independently
+    /// of CPU frequency even across different power states of the CPU (i.e. checks for the
+    /// `invariant_tsc` CPUID flag).
+    pub(super) fn is_tsc_available() -> bool {
+        #[cfg(target_arch = "x86")]
+        use core::arch::x86::__cpuid;
+        #[cfg(target_arch = "x86_64")]
+        use core::arch::x86_64::__cpuid;
+
+        // implemented like https://docs.rs/raw-cpuid/latest/src/raw_cpuid/extended.rs.html#965-967
+        const LEAF: u32 = 0x80000007; // this is the leaf for "advanced power management info"
+        let cpuid = unsafe { __cpuid(LEAF) };
+        (cpuid.edx & (1 << 8)) != 0 // EDX bit 8 indicates invariant TSC
+    }
+
+    /// Forces the current thread to run on a single CPU, which ensures the TSC counter is monotonic
+    /// (since TSCs of different CPUs might be out-of-sync). `incremental_thread_id` is used to pick
+    /// to which CPU to pin the current thread, and should be an incremental number that starts from
+    /// 0.
+    pub(super) fn set_cpu_affinity(incremental_thread_id: usize) {
+        let cpu_id = match std::thread::available_parallelism() {
+            Ok(available_parallelism) => incremental_thread_id % available_parallelism,
+            _ => panic!("Could not determine CPU count to properly set CPU affinity"),
+        };
+
+        let mut set = unsafe { std::mem::zeroed::<libc::cpu_set_t>() };
+        unsafe { libc::CPU_SET(cpu_id, &mut set) };
+
+        // Set the current thread's core affinity.
+        if unsafe {
+            libc::sched_setaffinity(
+                0, // Defaults to current thread
+                size_of::<libc::cpu_set_t>(),
+                &set as *const _,
+            )
+        } != 0
+        {
+            panic!("Could not set CPU affinity")
+        }
+    }
+}
diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs
index ae1b25f8857..8b15a786347 100644
--- a/src/tools/miri/src/bin/miri.rs
+++ b/src/tools/miri/src/bin/miri.rs
@@ -39,7 +39,7 @@ use std::sync::atomic::{AtomicI32, AtomicU32, Ordering};
 
 use miri::{
     BacktraceStyle, BorrowTrackerMethod, GenmcConfig, GenmcCtx, MiriConfig, MiriEntryFnType,
-    ProvenanceMode, RetagFields, TreeBorrowsParams, ValidationMode,
+    ProvenanceMode, RetagFields, TreeBorrowsParams, ValidationMode, run_genmc_mode,
 };
 use rustc_abi::ExternAbi;
 use rustc_data_structures::sync;
@@ -186,10 +186,18 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
                     optimizations is usually marginal at best.");
         }
 
-        if let Some(_genmc_config) = &config.genmc_config {
-            let _genmc_ctx = Rc::new(GenmcCtx::new(&config));
-
-            todo!("GenMC mode not yet implemented");
+        // Run in GenMC mode if enabled.
+        if config.genmc_config.is_some() {
+            // This is the entry point used in GenMC mode.
+            // This closure will be called multiple times to explore the concurrent execution space of the program.
+            let eval_entry_once = |genmc_ctx: Rc<GenmcCtx>| {
+                miri::eval_entry(tcx, entry_def_id, entry_type, &config, Some(genmc_ctx))
+            };
+            let return_code = run_genmc_mode(&config, eval_entry_once, tcx).unwrap_or_else(|| {
+                tcx.dcx().abort_if_errors();
+                rustc_driver::EXIT_FAILURE
+            });
+            exit(return_code);
         };
 
         if let Some(many_seeds) = self.many_seeds.take() {
@@ -556,7 +564,11 @@ fn main() {
         } else if arg == "-Zmiri-deterministic-floats" {
             miri_config.float_nondet = false;
         } else if arg == "-Zmiri-no-extra-rounding-error" {
-            miri_config.float_rounding_error = false;
+            miri_config.float_rounding_error = miri::FloatRoundingErrorMode::None;
+        } else if arg == "-Zmiri-max-extra-rounding-error" {
+            miri_config.float_rounding_error = miri::FloatRoundingErrorMode::Max;
+        } else if arg == "-Zmiri-no-short-fd-operations" {
+            miri_config.short_fd_operations = false;
         } else if arg == "-Zmiri-strict-provenance" {
             miri_config.provenance_mode = ProvenanceMode::Strict;
         } else if arg == "-Zmiri-permissive-provenance" {
@@ -733,19 +745,11 @@ fn main() {
         many_seeds.map(|seeds| ManySeedsConfig { seeds, keep_going: many_seeds_keep_going });
 
     // Validate settings for data race detection and GenMC mode.
-    if miri_config.genmc_config.is_some() {
-        if !miri_config.data_race_detector {
-            fatal_error!("Cannot disable data race detection in GenMC mode (currently)");
-        } else if !miri_config.weak_memory_emulation {
-            fatal_error!("Cannot disable weak memory emulation in GenMC mode");
-        }
-        if miri_config.borrow_tracker.is_some() {
-            eprintln!(
-                "warning: borrow tracking has been disabled, it is not (yet) supported in GenMC mode."
-            );
-            miri_config.borrow_tracker = None;
-        }
-    } else if miri_config.weak_memory_emulation && !miri_config.data_race_detector {
+    if let Err(err) = GenmcConfig::validate_genmc_mode_settings(&mut miri_config) {
+        fatal_error!("Invalid settings: {err}");
+    }
+
+    if miri_config.weak_memory_emulation && !miri_config.data_race_detector {
         fatal_error!(
             "Weak memory emulation cannot be enabled when the data race detector is disabled"
         );
diff --git a/src/tools/miri/src/borrow_tracker/mod.rs b/src/tools/miri/src/borrow_tracker/mod.rs
index ec6c2c60ca9..89bd93edae1 100644
--- a/src/tools/miri/src/borrow_tracker/mod.rs
+++ b/src/tools/miri/src/borrow_tracker/mod.rs
@@ -260,7 +260,7 @@ impl GlobalStateInner {
         kind: MemoryKind,
         machine: &MiriMachine<'_>,
     ) -> AllocState {
-        let _span = enter_trace_span!(borrow_tracker::new_allocation, ?id, ?alloc_size, ?kind);
+        let _trace = enter_trace_span!(borrow_tracker::new_allocation, ?id, ?alloc_size, ?kind);
         match self.borrow_tracker_method {
             BorrowTrackerMethod::StackedBorrows =>
                 AllocState::StackedBorrows(Box::new(RefCell::new(Stacks::new_allocation(
@@ -281,7 +281,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         kind: RetagKind,
         val: &ImmTy<'tcx>,
     ) -> InterpResult<'tcx, ImmTy<'tcx>> {
-        let _span = enter_trace_span!(borrow_tracker::retag_ptr_value, ?kind, ?val.layout);
+        let _trace = enter_trace_span!(borrow_tracker::retag_ptr_value, ?kind, ?val.layout);
         let this = self.eval_context_mut();
         let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method;
         match method {
@@ -295,7 +295,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         kind: RetagKind,
         place: &PlaceTy<'tcx>,
     ) -> InterpResult<'tcx> {
-        let _span = enter_trace_span!(borrow_tracker::retag_place_contents, ?kind, ?place);
+        let _trace = enter_trace_span!(borrow_tracker::retag_place_contents, ?kind, ?place);
         let this = self.eval_context_mut();
         let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method;
         match method {
@@ -305,7 +305,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     }
 
     fn protect_place(&mut self, place: &MPlaceTy<'tcx>) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
-        let _span = enter_trace_span!(borrow_tracker::protect_place, ?place);
+        let _trace = enter_trace_span!(borrow_tracker::protect_place, ?place);
         let this = self.eval_context_mut();
         let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method;
         match method {
@@ -315,7 +315,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     }
 
     fn expose_tag(&self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> {
-        let _span =
+        let _trace =
             enter_trace_span!(borrow_tracker::expose_tag, alloc_id = alloc_id.0, tag = tag.0);
         let this = self.eval_context_ref();
         let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method;
@@ -360,7 +360,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         &self,
         frame: &Frame<'tcx, Provenance, FrameExtra<'tcx>>,
     ) -> InterpResult<'tcx> {
-        let _span = enter_trace_span!(borrow_tracker::on_stack_pop);
+        let _trace = enter_trace_span!(borrow_tracker::on_stack_pop);
         let this = self.eval_context_ref();
         let borrow_tracker = this.machine.borrow_tracker.as_ref().unwrap();
         // The body of this loop needs `borrow_tracker` immutably
@@ -438,7 +438,7 @@ impl AllocState {
         range: AllocRange,
         machine: &MiriMachine<'tcx>,
     ) -> InterpResult<'tcx> {
-        let _span = enter_trace_span!(borrow_tracker::before_memory_read, alloc_id = alloc_id.0);
+        let _trace = enter_trace_span!(borrow_tracker::before_memory_read, alloc_id = alloc_id.0);
         match self {
             AllocState::StackedBorrows(sb) =>
                 sb.borrow_mut().before_memory_read(alloc_id, prov_extra, range, machine),
@@ -460,7 +460,7 @@ impl AllocState {
         range: AllocRange,
         machine: &MiriMachine<'tcx>,
     ) -> InterpResult<'tcx> {
-        let _span = enter_trace_span!(borrow_tracker::before_memory_write, alloc_id = alloc_id.0);
+        let _trace = enter_trace_span!(borrow_tracker::before_memory_write, alloc_id = alloc_id.0);
         match self {
             AllocState::StackedBorrows(sb) =>
                 sb.get_mut().before_memory_write(alloc_id, prov_extra, range, machine),
@@ -482,7 +482,7 @@ impl AllocState {
         size: Size,
         machine: &MiriMachine<'tcx>,
     ) -> InterpResult<'tcx> {
-        let _span =
+        let _trace =
             enter_trace_span!(borrow_tracker::before_memory_deallocation, alloc_id = alloc_id.0);
         match self {
             AllocState::StackedBorrows(sb) =>
@@ -493,7 +493,7 @@ impl AllocState {
     }
 
     pub fn remove_unreachable_tags(&self, tags: &FxHashSet<BorTag>) {
-        let _span = enter_trace_span!(borrow_tracker::remove_unreachable_tags);
+        let _trace = enter_trace_span!(borrow_tracker::remove_unreachable_tags);
         match self {
             AllocState::StackedBorrows(sb) => sb.borrow_mut().remove_unreachable_tags(tags),
             AllocState::TreeBorrows(tb) => tb.borrow_mut().remove_unreachable_tags(tags),
@@ -508,7 +508,7 @@ impl AllocState {
         tag: BorTag,
         alloc_id: AllocId, // diagnostics
     ) -> InterpResult<'tcx> {
-        let _span = enter_trace_span!(
+        let _trace = enter_trace_span!(
             borrow_tracker::release_protector,
             alloc_id = alloc_id.0,
             tag = tag.0
@@ -523,7 +523,7 @@ impl AllocState {
 
 impl VisitProvenance for AllocState {
     fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
-        let _span = enter_trace_span!(borrow_tracker::visit_provenance);
+        let _trace = enter_trace_span!(borrow_tracker::visit_provenance);
         match self {
             AllocState::StackedBorrows(sb) => sb.visit_provenance(visit),
             AllocState::TreeBorrows(tb) => tb.visit_provenance(visit),
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 2977efaae04..5fe00ab02c4 100644
--- a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs
+++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs
@@ -740,6 +740,7 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> {
                 if let Some(access) = access {
                     assert_eq!(access, AccessKind::Write);
                     // Make sure the data race model also knows about this.
+                    // FIXME(genmc): Ensure this is still done in GenMC mode. Check for other places where GenMC may need to be informed.
                     if let Some(data_race) = alloc_extra.data_race.as_vclocks_mut() {
                         data_race.write(
                             alloc_id,
diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs
index 7b4c533cfae..00f921b0f8a 100644
--- a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs
+++ b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs
@@ -244,8 +244,8 @@ pub(super) enum TransitionError {
     ChildAccessForbidden(Permission),
     /// A protector was triggered due to an invalid transition that loses
     /// too much permissions.
-    /// For example, if a protected tag goes from `Active` to `Disabled` due
-    /// to a foreign write this will produce a `ProtectedDisabled(Active)`.
+    /// For example, if a protected tag goes from `Unique` to `Disabled` due
+    /// to a foreign write this will produce a `ProtectedDisabled(Unique)`.
     /// This kind of error can only occur on foreign accesses.
     ProtectedDisabled(Permission),
     /// Cannot deallocate because some tag in the allocation is strongly protected.
@@ -504,7 +504,7 @@ impl DisplayFmt {
         if let Some(perm) = perm {
             format!(
                 "{ac}{st}",
-                ac = if perm.is_accessed() { self.accessed.yes } else { self.accessed.no },
+                ac = if perm.accessed() { self.accessed.yes } else { self.accessed.no },
                 st = perm.permission().short_name(),
             )
         } else {
diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/foreign_access_skipping.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/foreign_access_skipping.rs
index 928b3e6baef..90df05d36d9 100644
--- a/src/tools/miri/src/borrow_tracker/tree_borrows/foreign_access_skipping.rs
+++ b/src/tools/miri/src/borrow_tracker/tree_borrows/foreign_access_skipping.rs
@@ -24,7 +24,7 @@ use super::tree::AccessRelatedness;
 /// "manually" reset the parent's SIFA to be at least as strong as the new child's. This is accomplished with the `ensure_no_stronger_than` method.
 ///
 /// Note that we derive Ord and PartialOrd, so the order in which variants are listed below matters:
-/// None < Read < Write. Do not change that order. See the `test_order` test.
+/// None < Read < Write (weaker to stronger). Do not change that order. See the `test_order` test.
 #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
 pub enum IdempotentForeignAccess {
     #[default]
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 ad2a67160f4..6e5b5c807aa 100644
--- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs
+++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs
@@ -125,81 +125,64 @@ pub struct NewPermission {
     /// Whether a read access should be performed on the non-frozen
     /// part on a retag.
     nonfreeze_access: bool,
+    /// Permission for memory outside the range.
+    outside_perm: Permission,
     /// Whether this pointer is part of the arguments of a function call.
     /// `protector` is `Some(_)` for all pointers marked `noalias`.
     protector: Option<ProtectorKind>,
 }
 
 impl<'tcx> NewPermission {
-    /// Determine NewPermission of the reference from the type of the pointee.
-    fn from_ref_ty(
+    /// Determine NewPermission of the reference/Box from the type of the pointee.
+    ///
+    /// A `ref_mutability` of `None` indicates a `Box` type.
+    fn new(
         pointee: Ty<'tcx>,
-        mutability: Mutability,
-        kind: RetagKind,
+        ref_mutability: Option<Mutability>,
+        retag_kind: RetagKind,
         cx: &crate::MiriInterpCx<'tcx>,
     ) -> Option<Self> {
         let ty_is_unpin = pointee.is_unpin(*cx.tcx, cx.typing_env());
-        let is_protected = kind == RetagKind::FnEntry;
-        let protector = is_protected.then_some(ProtectorKind::StrongProtector);
-
-        Some(match mutability {
-            Mutability::Mut if ty_is_unpin =>
-                NewPermission {
-                    freeze_perm: Permission::new_reserved(
-                        /* ty_is_freeze */ true,
-                        is_protected,
-                    ),
-                    freeze_access: true,
-                    nonfreeze_perm: Permission::new_reserved(
-                        /* ty_is_freeze */ false,
-                        is_protected,
-                    ),
-                    // If we have a mutable reference, then the non-frozen part will
-                    // have state `ReservedIM` or `Reserved`, which can have an initial read access
-                    // performed on it because you cannot have multiple mutable borrows.
-                    nonfreeze_access: true,
-                    protector,
-                },
-            Mutability::Not =>
-                NewPermission {
-                    freeze_perm: Permission::new_frozen(),
-                    freeze_access: true,
-                    nonfreeze_perm: Permission::new_cell(),
-                    // If it is a shared reference, then the non-frozen
-                    // part will have state `Cell`, which should not have an initial access,
-                    // as this can cause data races when using thread-safe data types like
-                    // `Mutex<T>`.
-                    nonfreeze_access: false,
-                    protector,
-                },
-            _ => return None,
-        })
-    }
+        let ty_is_freeze = pointee.is_freeze(*cx.tcx, cx.typing_env());
+        let is_protected = retag_kind == RetagKind::FnEntry;
 
-    /// Compute permission for `Box`-like type (`Box` always, and also `Unique` if enabled).
-    /// These pointers allow deallocation so need a different kind of protector not handled
-    /// by `from_ref_ty`.
-    fn from_unique_ty(
-        ty: Ty<'tcx>,
-        kind: RetagKind,
-        cx: &crate::MiriInterpCx<'tcx>,
-    ) -> Option<Self> {
-        let pointee = ty.builtin_deref(true).unwrap();
-        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 is_protected = kind == RetagKind::FnEntry;
-            let protector = is_protected.then_some(ProtectorKind::WeakProtector);
-            NewPermission {
-                freeze_perm: Permission::new_reserved(/* ty_is_freeze */ true, is_protected),
-                freeze_access: true,
-                nonfreeze_perm: Permission::new_reserved(
-                    /* ty_is_freeze */ false,
-                    is_protected,
-                ),
-                nonfreeze_access: true,
-                protector,
-            }
+        if matches!(ref_mutability, Some(Mutability::Mut) | None if !ty_is_unpin) {
+            // Mutable reference / Box to pinning type: retagging is a NOP.
+            // FIXME: with `UnsafePinned`, this should do proper per-byte tracking.
+            return None;
+        }
+
+        let freeze_perm = match ref_mutability {
+            // Shared references are frozen.
+            Some(Mutability::Not) => Permission::new_frozen(),
+            // Mutable references and Boxes are reserved.
+            _ => Permission::new_reserved_frz(),
+        };
+        let nonfreeze_perm = match ref_mutability {
+            // Shared references are "transparent".
+            Some(Mutability::Not) => Permission::new_cell(),
+            // *Protected* mutable references and boxes are reserved without regarding for interior mutability.
+            _ if is_protected => Permission::new_reserved_frz(),
+            // Unprotected mutable references and boxes start in `ReservedIm`.
+            _ => Permission::new_reserved_im(),
+        };
+
+        // Everything except for `Cell` gets an initial access.
+        let initial_access = |perm: &Permission| !perm.is_cell();
+
+        Some(NewPermission {
+            freeze_perm,
+            freeze_access: initial_access(&freeze_perm),
+            nonfreeze_perm,
+            nonfreeze_access: initial_access(&nonfreeze_perm),
+            outside_perm: if ty_is_freeze { freeze_perm } else { nonfreeze_perm },
+            protector: is_protected.then_some(if ref_mutability.is_some() {
+                // Strong protector for references
+                ProtectorKind::StrongProtector
+            } else {
+                // Weak protector for boxes
+                ProtectorKind::WeakProtector
+            }),
         })
     }
 }
@@ -311,34 +294,6 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             return interp_ok(Some(Provenance::Concrete { alloc_id, tag: new_tag }));
         }
 
-        let span = this.machine.current_span();
-
-        // Store initial permissions and their corresponding range.
-        let mut perms_map: DedupRangeMap<LocationState> = DedupRangeMap::new(
-            ptr_size,
-            LocationState::new_accessed(Permission::new_disabled(), IdempotentForeignAccess::None), // this will be overwritten
-        );
-        // Keep track of whether the node has any part that allows for interior mutability.
-        // FIXME: This misses `PhantomData<UnsafeCell<T>>` which could be considered a marker
-        // for requesting interior mutability.
-        let mut has_unsafe_cell = false;
-
-        // When adding a new node, the SIFA of its parents needs to be updated, potentially across
-        // the entire memory range. For the parts that are being accessed below, the access itself
-        // trivially takes care of that. However, we have to do some more work to also deal with
-        // the parts that are not being accessed. Specifically what we do is that we
-        // call `update_last_accessed_after_retag` on the SIFA of the permission set for the part of
-        // memory outside `perm_map` -- so that part is definitely taken care of. The remaining concern
-        // is the part of memory that is in the range of `perms_map`, but not accessed below.
-        // There we have two cases:
-        // * If we do have an `UnsafeCell` (`has_unsafe_cell` becomes true), then the non-accessed part
-        //   uses `nonfreeze_perm`, so the `nonfreeze_perm` initialized parts are also fine. We enforce
-        //   the `freeze_perm` parts to be accessed, and thus everything is taken care of.
-        // * If there is no `UnsafeCell`, then `freeze_perm` is used everywhere (both inside and outside the initial range),
-        //   and we update everything to have the `freeze_perm`'s SIFA, so there are no issues. (And this assert below is not
-        //   actually needed in this case).
-        assert!(new_perm.freeze_access);
-
         let protected = new_perm.protector.is_some();
         let precise_interior_mut = this
             .machine
@@ -350,67 +305,49 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             .get_tree_borrows_params()
             .precise_interior_mut;
 
-        let default_perm = if !precise_interior_mut {
-            // NOTE: Using `ty_is_freeze` doesn't give the same result as going through the range
-            // and computing `has_unsafe_cell`.  This is because of zero-sized `UnsafeCell`, for which
-            // `has_unsafe_cell` is false, but `!ty_is_freeze` is true.
-            let ty_is_freeze = place.layout.ty.is_freeze(*this.tcx, this.typing_env());
-            let (perm, access) = if ty_is_freeze {
+        // Compute initial "inside" permissions.
+        let loc_state = |frozen: bool| -> LocationState {
+            let (perm, access) = if frozen {
                 (new_perm.freeze_perm, new_perm.freeze_access)
             } else {
                 (new_perm.nonfreeze_perm, new_perm.nonfreeze_access)
             };
             let sifa = perm.strongest_idempotent_foreign_access(protected);
-            let new_loc = if access {
+            if access {
                 LocationState::new_accessed(perm, sifa)
             } else {
                 LocationState::new_non_accessed(perm, sifa)
-            };
-
-            for (_loc_range, loc) in perms_map.iter_mut_all() {
-                *loc = new_loc;
             }
-
-            perm
+        };
+        let inside_perms = if !precise_interior_mut {
+            // For `!Freeze` types, just pretend the entire thing is an `UnsafeCell`.
+            let ty_is_freeze = place.layout.ty.is_freeze(*this.tcx, this.typing_env());
+            let state = loc_state(ty_is_freeze);
+            DedupRangeMap::new(ptr_size, state)
         } else {
+            // The initial state will be overwritten by the visitor below.
+            let mut perms_map: DedupRangeMap<LocationState> = DedupRangeMap::new(
+                ptr_size,
+                LocationState::new_accessed(
+                    Permission::new_disabled(),
+                    IdempotentForeignAccess::None,
+                ),
+            );
             this.visit_freeze_sensitive(place, ptr_size, |range, frozen| {
-                has_unsafe_cell = has_unsafe_cell || !frozen;
-
-                // We are only ever `Frozen` inside the frozen bits.
-                let (perm, access) = if frozen {
-                    (new_perm.freeze_perm, new_perm.freeze_access)
-                } else {
-                    (new_perm.nonfreeze_perm, new_perm.nonfreeze_access)
-                };
-                let sifa = perm.strongest_idempotent_foreign_access(protected);
-                // NOTE: Currently, `access` is false if and only if `perm` is Cell, so this `if`
-                // doesn't not change whether any code is UB or not. We could just always use
-                // `new_accessed` and everything would stay the same. But that seems conceptually
-                // odd, so we keep the initial "accessed" bit of the `LocationState` in sync with whether
-                // a read access is performed below.
-                let new_loc = if access {
-                    LocationState::new_accessed(perm, sifa)
-                } else {
-                    LocationState::new_non_accessed(perm, sifa)
-                };
-
-                // Store initial permissions.
+                let state = loc_state(frozen);
                 for (_loc_range, loc) in perms_map.iter_mut(range.start, range.size) {
-                    *loc = new_loc;
+                    *loc = state;
                 }
-
                 interp_ok(())
             })?;
-
-            // Allow lazily writing to surrounding data if we found an `UnsafeCell`.
-            if has_unsafe_cell { new_perm.nonfreeze_perm } else { new_perm.freeze_perm }
+            perms_map
         };
 
         let alloc_extra = this.get_alloc_extra(alloc_id)?;
         let mut tree_borrows = alloc_extra.borrow_tracker_tb().borrow_mut();
 
-        for (perm_range, perm) in perms_map.iter_mut_all() {
-            if perm.is_accessed() {
+        for (perm_range, perm) in inside_perms.iter_all() {
+            if perm.accessed() {
                 // Some reborrows incur a read access to the parent.
                 // Adjust range to be relative to allocation start (rather than to `place`).
                 let range_in_alloc = AllocRange {
@@ -446,10 +383,10 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             base_offset,
             orig_tag,
             new_tag,
-            perms_map,
-            default_perm,
+            inside_perms,
+            new_perm.outside_perm,
             protected,
-            span,
+            this.machine.current_span(),
         )?;
         drop(tree_borrows);
 
@@ -514,7 +451,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let this = self.eval_context_mut();
         let new_perm = match val.layout.ty.kind() {
             &ty::Ref(_, pointee, mutability) =>
-                NewPermission::from_ref_ty(pointee, mutability, kind, this),
+                NewPermission::new(pointee, Some(mutability), kind, this),
             _ => None,
         };
         if let Some(new_perm) = new_perm {
@@ -571,8 +508,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             fn visit_box(&mut self, box_ty: Ty<'tcx>, place: &PlaceTy<'tcx>) -> InterpResult<'tcx> {
                 // Only boxes for the global allocator get any special treatment.
                 if box_ty.is_box_global(*self.ecx.tcx) {
+                    let pointee = place.layout.ty.builtin_deref(true).unwrap();
                     let new_perm =
-                        NewPermission::from_unique_ty(place.layout.ty, self.kind, self.ecx);
+                        NewPermission::new(pointee, /* not a ref */ None, self.kind, self.ecx);
                     self.retag_ptr_inplace(place, new_perm)?;
                 }
                 interp_ok(())
@@ -591,7 +529,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 match place.layout.ty.kind() {
                     &ty::Ref(_, pointee, mutability) => {
                         let new_perm =
-                            NewPermission::from_ref_ty(pointee, mutability, self.kind, self.ecx);
+                            NewPermission::new(pointee, Some(mutability), self.kind, self.ecx);
                         self.retag_ptr_inplace(place, new_perm)?;
                     }
                     ty::RawPtr(_, _) => {
@@ -643,14 +581,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // never be ReservedIM, the value of the `ty_is_freeze`
             // argument doesn't matter
             // (`ty_is_freeze || true` in `new_reserved` will always be `true`).
-            freeze_perm: Permission::new_reserved(
-                /* ty_is_freeze */ true, /* protected */ true,
-            ),
+            freeze_perm: Permission::new_reserved_frz(),
             freeze_access: true,
-            nonfreeze_perm: Permission::new_reserved(
-                /* ty_is_freeze */ false, /* protected */ true,
-            ),
+            nonfreeze_perm: Permission::new_reserved_frz(),
             nonfreeze_access: true,
+            outside_perm: Permission::new_reserved_frz(),
             protector: Some(ProtectorKind::StrongProtector),
         };
         this.tb_retag_place(place, new_perm)
diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs
index 38863ca0734..e21775c9f23 100644
--- a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs
+++ b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs
@@ -14,7 +14,7 @@ enum PermissionPriv {
     Cell,
     /// represents: a local mutable reference that has not yet been written to;
     /// allows: child reads, foreign reads;
-    /// affected by: child writes (becomes Active),
+    /// affected by: child writes (becomes Unique),
     /// rejects: foreign writes (Disabled).
     ///
     /// `ReservedFrz` is mostly for types that are `Freeze` (no interior mutability).
@@ -31,17 +31,17 @@ enum PermissionPriv {
     /// This is so that the behavior of `Reserved` adheres to the rules of `noalias`:
     /// - foreign-read then child-write is UB due to `conflicted`,
     /// - child-write then foreign-read is UB since child-write will activate and then
-    ///   foreign-read disables a protected `Active`, which is UB.
+    ///   foreign-read disables a protected `Unique`, which is UB.
     ReservedFrz { conflicted: bool },
     /// Alternative version of `ReservedFrz` made for types with interior mutability.
     /// allows: child reads, foreign reads, foreign writes (extra);
-    /// affected by: child writes (becomes Active);
+    /// affected by: child writes (becomes Unique);
     /// rejects: nothing.
     ReservedIM,
     /// represents: a unique pointer;
     /// allows: child reads, child writes;
     /// rejects: foreign reads (Frozen), foreign writes (Disabled).
-    Active,
+    Unique,
     /// represents: a shared pointer;
     /// allows: all read accesses;
     /// rejects child writes (UB), foreign writes (Disabled).
@@ -56,7 +56,7 @@ use super::foreign_access_skipping::IdempotentForeignAccess;
 
 impl PartialOrd for PermissionPriv {
     /// PermissionPriv is ordered by the reflexive transitive closure of
-    /// `Reserved(conflicted=false) < Reserved(conflicted=true) < Active < Frozen < Disabled`.
+    /// `Reserved(conflicted=false) < Reserved(conflicted=true) < Unique < Frozen < Disabled`.
     /// `Reserved` that have incompatible `ty_is_freeze` are incomparable to each other.
     /// This ordering matches the reachability by transitions, as asserted by the exhaustive test
     /// `permissionpriv_partialord_is_reachability`.
@@ -76,8 +76,8 @@ impl PartialOrd for PermissionPriv {
             (_, Disabled) => Less,
             (Frozen, _) => Greater,
             (_, Frozen) => Less,
-            (Active, _) => Greater,
-            (_, Active) => Less,
+            (Unique, _) => Greater,
+            (_, Unique) => Less,
             (ReservedIM, ReservedIM) => Equal,
             (ReservedFrz { conflicted: c1 }, ReservedFrz { conflicted: c2 }) => {
                 // `bool` is ordered such that `false <= true`, so this works as intended.
@@ -115,8 +115,8 @@ impl PermissionPriv {
             // Famously, ReservedIM survives foreign writes. It is never protected.
             ReservedIM if prot => unreachable!("Protected ReservedIM should not exist!"),
             ReservedIM => IdempotentForeignAccess::Write,
-            // Active changes on any foreign access (becomes Frozen/Disabled).
-            Active => IdempotentForeignAccess::None,
+            // Unique changes on any foreign access (becomes Frozen/Disabled).
+            Unique => IdempotentForeignAccess::None,
             // Frozen survives foreign reads, but not writes.
             Frozen => IdempotentForeignAccess::Read,
             // Disabled survives foreign reads and writes. It survives them
@@ -139,12 +139,12 @@ mod transition {
             Disabled => return None,
             // The inner data `ty_is_freeze` of `Reserved` is always irrelevant for Read
             // accesses, since the data is not being mutated. Hence the `{ .. }`.
-            readable @ (Cell | ReservedFrz { .. } | ReservedIM | Active | Frozen) => readable,
+            readable @ (Cell | ReservedFrz { .. } | ReservedIM | Unique | Frozen) => readable,
         })
     }
 
     /// A non-child node was read-accessed: keep `Reserved` but mark it as `conflicted` if it
-    /// is protected; invalidate `Active`.
+    /// is protected; invalidate `Unique`.
     fn foreign_read(state: PermissionPriv, protected: bool) -> Option<PermissionPriv> {
         Some(match state {
             // Cell ignores foreign reads.
@@ -167,10 +167,10 @@ mod transition {
                 assert!(!protected);
                 res
             }
-            Active =>
+            Unique =>
                 if protected {
                     // We wrote, someone else reads -- that's bad.
-                    // (Since Active is always initialized, this move-to-protected will mean insta-UB.)
+                    // (Since Unique is always initialized, this move-to-protected will mean insta-UB.)
                     Disabled
                 } else {
                     // We don't want to disable here to allow read-read reordering: it is crucial
@@ -180,7 +180,7 @@ mod transition {
         })
     }
 
-    /// A child node was write-accessed: `Reserved` must become `Active` to obtain
+    /// A child node was write-accessed: `Reserved` must become `Unique` to obtain
     /// write permissions, `Frozen` and `Disabled` cannot obtain such permissions and produce UB.
     fn child_write(state: PermissionPriv, protected: bool) -> Option<PermissionPriv> {
         Some(match state {
@@ -192,7 +192,7 @@ mod transition {
             ReservedFrz { conflicted: true } if protected => return None,
             // A write always activates the 2-phase borrow, even with interior
             // mutability
-            ReservedFrz { .. } | ReservedIM | Active => Active,
+            ReservedFrz { .. } | ReservedIM | Unique => Unique,
             Frozen | Disabled => return None,
         })
     }
@@ -266,34 +266,21 @@ impl Permission {
 
     /// Default initial permission of the root of a new tree at inbounds positions.
     /// Must *only* be used for the root, this is not in general an "initial" permission!
-    pub fn new_active() -> Self {
-        Self { inner: Active }
+    pub fn new_unique() -> Self {
+        Self { inner: Unique }
     }
 
     /// Default initial permission of a reborrowed mutable reference that is either
     /// protected or not interior mutable.
-    fn new_reserved_frz() -> Self {
+    pub fn new_reserved_frz() -> Self {
         Self { inner: ReservedFrz { conflicted: false } }
     }
 
     /// Default initial permission of an unprotected interior mutable reference.
-    fn new_reserved_im() -> Self {
+    pub fn new_reserved_im() -> Self {
         Self { inner: ReservedIM }
     }
 
-    /// Wrapper around `new_reserved_frz` and `new_reserved_im` that decides
-    /// which to call based on the interior mutability and the retag kind (whether there
-    /// is a protector is relevant because being protected takes priority over being
-    /// interior mutable)
-    pub fn new_reserved(ty_is_freeze: bool, protected: bool) -> Self {
-        // As demonstrated by `tests/fail/tree_borrows/reservedim_spurious_write.rs`,
-        // interior mutability and protectors interact poorly.
-        // To eliminate the case of Protected Reserved IM we override interior mutability
-        // in the case of a protected reference: protected references are always considered
-        // "freeze" in their reservation phase.
-        if ty_is_freeze || protected { Self::new_reserved_frz() } else { Self::new_reserved_im() }
-    }
-
     /// Default initial permission of a reborrowed shared reference.
     pub fn new_frozen() -> Self {
         Self { inner: Frozen }
@@ -322,7 +309,7 @@ impl Permission {
             // Do not do perform access if it is a `Cell`, as this
             // can cause data races when using thread-safe data types.
             Cell => None,
-            Active => Some(AccessKind::Write),
+            Unique => Some(AccessKind::Write),
             _ => Some(AccessKind::Read),
         }
     }
@@ -357,7 +344,7 @@ impl Permission {
             (_, Cell) => false,
             // ReservedIM can be replaced by anything besides Cell.
             // ReservedIM allows all transitions, but unlike Cell, a local write
-            // to ReservedIM transitions to Active, while it is a no-op for Cell.
+            // to ReservedIM transitions to Unique, while it is a no-op for Cell.
             (ReservedIM, _) => true,
             (_, ReservedIM) => false,
             // Reserved (as parent, where conflictedness does not matter)
@@ -365,12 +352,12 @@ impl Permission {
             // since ReservedIM and Cell alone would survive foreign writes
             (ReservedFrz { .. }, _) => true,
             (_, ReservedFrz { .. }) => false,
-            // Active can not be replaced by something surviving
+            // Unique can not be replaced by something surviving
             // foreign reads and then remaining writable (i.e., Reserved*).
             // Replacing a state by itself is always okay, even if the child state is protected.
-            // Active can be replaced by Frozen, since it is not protected.
-            (Active, Active | Frozen | Disabled) => true,
-            (_, Active) => false,
+            // Unique can be replaced by Frozen, since it is not protected.
+            (Unique, Unique | Frozen | Disabled) => true,
+            (_, Unique) => false,
             // Frozen can only be replaced by Disabled (and itself).
             (Frozen, Frozen | Disabled) => true,
             (_, Frozen) => false,
@@ -423,7 +410,7 @@ pub mod diagnostics {
                     ReservedFrz { conflicted: false } => "Reserved",
                     ReservedFrz { conflicted: true } => "Reserved (conflicted)",
                     ReservedIM => "Reserved (interior mutable)",
-                    Active => "Active",
+                    Unique => "Unique",
                     Frozen => "Frozen",
                     Disabled => "Disabled",
                 }
@@ -454,7 +441,7 @@ pub mod diagnostics {
                 ReservedFrz { conflicted: false } => "Res ",
                 ReservedFrz { conflicted: true } => "ResC",
                 ReservedIM => "ReIM",
-                Active => "Act ",
+                Unique => "Act ",
                 Frozen => "Frz ",
                 Disabled => "Dis ",
             }
@@ -468,7 +455,7 @@ pub mod diagnostics {
             assert!(self.is_possible());
             assert!(!self.is_noop());
             match (self.from, self.to) {
-                (_, Active) => "the first write to a 2-phase borrowed mutable reference",
+                (_, Unique) => "the first write to a 2-phase borrowed mutable reference",
                 (_, Frozen) => "a loss of write permissions",
                 (ReservedFrz { conflicted: false }, ReservedFrz { conflicted: true }) =>
                     "a temporary loss of write permissions until function exit",
@@ -485,8 +472,8 @@ pub mod diagnostics {
         ///
         /// Irrelevant events:
         /// - modifications of write permissions when the error is related to read permissions
-        ///   (on failed reads and protected `Frozen -> Disabled`, ignore `Reserved -> Active`,
-        ///   `Reserved(conflicted=false) -> Reserved(conflicted=true)`, and `Active -> Frozen`)
+        ///   (on failed reads and protected `Frozen -> Disabled`, ignore `Reserved -> Unique`,
+        ///   `Reserved(conflicted=false) -> Reserved(conflicted=true)`, and `Unique -> Frozen`)
         /// - all transitions for attempts to deallocate strongly protected tags
         ///
         /// # Panics
@@ -494,10 +481,10 @@ pub mod diagnostics {
         /// This function assumes that its arguments apply to the same location
         /// and that they were obtained during a normal execution. It will panic otherwise.
         /// - all transitions involved in `self` and `err` should be increasing
-        ///   (Reserved < Active < Frozen < Disabled);
+        ///   (Reserved < Unique < Frozen < Disabled);
         /// - between `self` and `err` the permission should also be increasing,
         ///   so all permissions inside `err` should be greater than `self.1`;
-        /// - `Active`, `Reserved(conflicted=false)`, and `Cell` cannot cause an error
+        /// - `Unique`, `Reserved(conflicted=false)`, and `Cell` cannot cause an error
         ///   due to insufficient permissions, so `err` cannot be a `ChildAccessForbidden(_)`
         ///   of either of them;
         /// - `err` should not be `ProtectedDisabled(Disabled)`, because the protected
@@ -513,11 +500,11 @@ pub mod diagnostics {
                 TransitionError::ChildAccessForbidden(insufficient) => {
                     // Show where the permission was gained then lost,
                     // but ignore unrelated permissions.
-                    // This eliminates transitions like `Active -> Frozen`
+                    // This eliminates transitions like `Unique -> Frozen`
                     // when the error is a failed `Read`.
                     match (self.to, insufficient.inner) {
                         (Frozen, Frozen) => true,
-                        (Active, Frozen) => true,
+                        (Unique, Frozen) => true,
                         (Disabled, Disabled) => true,
                         (
                             ReservedFrz { conflicted: true, .. },
@@ -525,14 +512,14 @@ pub mod diagnostics {
                         ) => true,
                         // A pointer being `Disabled` is a strictly stronger source of
                         // errors than it being `Frozen`. If we try to access a `Disabled`,
-                        // then where it became `Frozen` (or `Active` or `Reserved`) is the least
+                        // then where it became `Frozen` (or `Unique` or `Reserved`) is the least
                         // of our concerns for now.
-                        (ReservedFrz { conflicted: true } | Active | Frozen, Disabled) => false,
+                        (ReservedFrz { conflicted: true } | Unique | Frozen, Disabled) => false,
                         (ReservedFrz { conflicted: true }, Frozen) => false,
 
-                        // `Active`, `Reserved`, and `Cell` have all permissions, so a
-                        // `ChildAccessForbidden(Reserved | Active)` can never exist.
-                        (_, Active) | (_, ReservedFrz { conflicted: false }) | (_, Cell) =>
+                        // `Unique`, `Reserved`, and `Cell` have all permissions, so a
+                        // `ChildAccessForbidden(Reserved | Unique)` can never exist.
+                        (_, Unique) | (_, ReservedFrz { conflicted: false }) | (_, Cell) =>
                             unreachable!("this permission cannot cause an error"),
                         // No transition has `Reserved { conflicted: false }` or `ReservedIM`
                         // as its `.to` unless it's a noop. `Cell` cannot be in its `.to`
@@ -540,11 +527,11 @@ pub mod diagnostics {
                         (ReservedFrz { conflicted: false } | ReservedIM | Cell, _) =>
                             unreachable!("self is a noop transition"),
                         // All transitions produced in normal executions (using `apply_access`)
-                        // change permissions in the order `Reserved -> Active -> Frozen -> Disabled`.
+                        // change permissions in the order `Reserved -> Unique -> Frozen -> Disabled`.
                         // We assume that the error was triggered on the same location that
                         // the transition `self` applies to, so permissions found must be increasing
                         // in the order `self.from < self.to <= insufficient.inner`
-                        (Active | Frozen | Disabled, ReservedFrz { .. } | ReservedIM)
+                        (Unique | Frozen | Disabled, ReservedFrz { .. } | ReservedIM)
                         | (Disabled, Frozen)
                         | (ReservedFrz { .. }, ReservedIM) =>
                             unreachable!("permissions between self and err must be increasing"),
@@ -553,29 +540,29 @@ pub mod diagnostics {
                 TransitionError::ProtectedDisabled(before_disabled) => {
                     // Show how we got to the starting point of the forbidden transition,
                     // but ignore what came before.
-                    // This eliminates transitions like `Reserved -> Active`
+                    // This eliminates transitions like `Reserved -> Unique`
                     // when the error is a `Frozen -> Disabled`.
                     match (self.to, before_disabled.inner) {
                         // We absolutely want to know where it was activated/frozen/marked
                         // conflicted.
-                        (Active, Active) => true,
+                        (Unique, Unique) => true,
                         (Frozen, Frozen) => true,
                         (
                             ReservedFrz { conflicted: true, .. },
                             ReservedFrz { conflicted: true, .. },
                         ) => true,
                         // If the error is a transition `Frozen -> Disabled`, then we don't really
-                        // care whether before that was `Reserved -> Active -> Frozen` or
+                        // care whether before that was `Reserved -> Unique -> Frozen` or
                         // `Frozen` directly.
                         // The error will only show either
                         // - created as Reserved { conflicted: false },
                         //   then Reserved { .. } -> Disabled is forbidden
                         // - created as Reserved { conflicted: false },
-                        //   then Active -> Disabled is forbidden
+                        //   then Unique -> Disabled is forbidden
                         // A potential `Reserved { conflicted: false }
                         //   -> Reserved { conflicted: true }` is inexistant or irrelevant,
-                        // and so is the `Reserved { conflicted: false } -> Active`
-                        (Active, Frozen) => false,
+                        // and so is the `Reserved { conflicted: false } -> Unique`
+                        (Unique, Frozen) => false,
                         (ReservedFrz { conflicted: true }, _) => false,
 
                         (_, Disabled) =>
@@ -588,12 +575,12 @@ pub mod diagnostics {
                         (ReservedFrz { conflicted: false } | ReservedIM | Cell, _) =>
                             unreachable!("self is a noop transition"),
 
-                        // Permissions only evolve in the order `Reserved -> Active -> Frozen -> Disabled`,
+                        // Permissions only evolve in the order `Reserved -> Unique -> Frozen -> Disabled`,
                         // so permissions found must be increasing in the order
                         // `self.from < self.to <= forbidden.from < forbidden.to`.
-                        (Disabled, Cell | ReservedFrz { .. } | ReservedIM | Active | Frozen)
-                        | (Frozen, Cell | ReservedFrz { .. } | ReservedIM | Active)
-                        | (Active, Cell | ReservedFrz { .. } | ReservedIM) =>
+                        (Disabled, Cell | ReservedFrz { .. } | ReservedIM | Unique | Frozen)
+                        | (Frozen, Cell | ReservedFrz { .. } | ReservedIM | Unique)
+                        | (Unique, Cell | ReservedFrz { .. } | ReservedIM) =>
                             unreachable!("permissions between self and err must be increasing"),
                     }
                 }
@@ -630,7 +617,7 @@ mod propagation_optimization_checks {
     impl Exhaustive for PermissionPriv {
         fn exhaustive() -> Box<dyn Iterator<Item = Self>> {
             Box::new(
-                vec![Active, Frozen, Disabled, ReservedIM, Cell]
+                vec![Unique, Frozen, Disabled, ReservedIM, Cell]
                     .into_iter()
                     .chain(<bool>::exhaustive().map(|conflicted| ReservedFrz { conflicted })),
             )
@@ -743,7 +730,7 @@ mod propagation_optimization_checks {
 
     #[test]
     // Check that all transitions are consistent with the order on PermissionPriv,
-    // i.e. Reserved -> Active -> Frozen -> Disabled
+    // i.e. Reserved -> Unique -> Frozen -> Disabled
     fn permissionpriv_partialord_is_reachability() {
         let reach = {
             let mut reach = rustc_data_structures::fx::FxHashSet::default();
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 1f29bcfc2b0..c4345c63289 100644
--- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs
+++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs
@@ -11,7 +11,7 @@
 //! - idempotency properties asserted in `perms.rs` (for optimizations)
 
 use std::ops::Range;
-use std::{fmt, mem};
+use std::{cmp, fmt, mem};
 
 use rustc_abi::Size;
 use rustc_data_structures::fx::FxHashSet;
@@ -57,7 +57,7 @@ pub(super) struct LocationState {
 impl LocationState {
     /// Constructs a new initial state. It has neither been accessed, nor been subjected
     /// to any foreign access yet.
-    /// The permission is not allowed to be `Active`.
+    /// The permission is not allowed to be `Unique`.
     /// `sifa` is the (strongest) idempotent foreign access, see `foreign_access_skipping.rs`
     pub fn new_non_accessed(permission: Permission, sifa: IdempotentForeignAccess) -> Self {
         assert!(permission.is_initial() || permission.is_disabled());
@@ -73,23 +73,10 @@ impl LocationState {
 
     /// Check if the location has been accessed, i.e. if it has
     /// ever been accessed through a child pointer.
-    pub fn is_accessed(&self) -> bool {
+    pub fn accessed(&self) -> bool {
         self.accessed
     }
 
-    /// Check if the state can exist as the initial permission of a pointer.
-    ///
-    /// Do not confuse with `is_accessed`, the two are almost orthogonal
-    /// as apart from `Active` which is not initial and must be accessed,
-    /// any other permission can have an arbitrary combination of being
-    /// initial/accessed.
-    /// FIXME: when the corresponding `assert` in `tree_borrows/mod.rs` finally
-    /// passes and can be uncommented, remove this `#[allow(dead_code)]`.
-    #[cfg_attr(not(test), allow(dead_code))]
-    pub fn is_initial(&self) -> bool {
-        self.permission.is_initial()
-    }
-
     pub fn permission(&self) -> Permission {
         self.permission
     }
@@ -170,7 +157,7 @@ impl LocationState {
             }
             if self.permission.is_frozen() && access_kind == AccessKind::Read {
                 // A foreign read to a `Frozen` tag will have almost no observable effect.
-                // It's a theorem that `Frozen` nodes have no active children, so all children
+                // It's a theorem that `Frozen` nodes have no `Unique` children, so all children
                 // already survive foreign reads. Foreign reads in general have almost no
                 // effect, the only further thing they could do is make protected `Reserved`
                 // nodes become conflicted, i.e. make them reject child writes for the further
@@ -265,7 +252,7 @@ pub(super) struct Node {
     pub children: SmallVec<[UniIndex; 4]>,
     /// Either `Reserved`,  `Frozen`, or `Disabled`, it is the permission this tag will
     /// lazily be initialized to on the first access.
-    /// It is only ever `Disabled` for a tree root, since the root is initialized to `Active` by
+    /// It is only ever `Disabled` for a tree root, since the root is initialized to `Unique` by
     /// its own separate mechanism.
     default_initial_perm: Permission,
     /// The default initial (strongest) idempotent foreign access.
@@ -598,14 +585,14 @@ impl Tree {
         };
         let rperms = {
             let mut perms = UniValMap::default();
-            // We manually set it to `Active` on all in-bounds positions.
-            // We also ensure that it is accessed, so that no `Active` but
+            // We manually set it to `Unique` on all in-bounds positions.
+            // We also ensure that it is accessed, so that no `Unique` but
             // not yet accessed nodes exist. Essentially, we pretend there
-            // was a write that initialized these to `Active`.
+            // was a write that initialized these to `Unique`.
             perms.insert(
                 root_idx,
                 LocationState::new_accessed(
-                    Permission::new_active(),
+                    Permission::new_unique(),
                     IdempotentForeignAccess::None,
                 ),
             );
@@ -618,30 +605,26 @@ impl Tree {
 impl<'tcx> Tree {
     /// Insert a new tag in the tree.
     ///
-    /// `initial_perms` defines the initial permissions for the part of memory
-    /// that is already considered "initialized" immediately. The ranges in this
-    /// map are relative to `base_offset`.
-    /// `default_perm` defines the initial permission for the rest of the allocation.
-    ///
-    /// For all non-accessed locations in the RangeMap (those that haven't had an
-    /// implicit read), their SIFA must be weaker than or as weak as the SIFA of
-    /// `default_perm`.
+    /// `inside_perm` defines the initial permissions for a block of memory starting at
+    /// `base_offset`. These may nor may not be already marked as "accessed".
+    /// `outside_perm` defines the initial permission for the rest of the allocation.
+    /// These are definitely not "accessed".
     pub(super) fn new_child(
         &mut self,
         base_offset: Size,
         parent_tag: BorTag,
         new_tag: BorTag,
-        initial_perms: DedupRangeMap<LocationState>,
-        default_perm: Permission,
+        inside_perms: DedupRangeMap<LocationState>,
+        outside_perm: Permission,
         protected: bool,
         span: Span,
     ) -> InterpResult<'tcx> {
         let idx = self.tag_mapping.insert(new_tag);
         let parent_idx = self.tag_mapping.get(&parent_tag).unwrap();
-        assert!(default_perm.is_initial());
+        assert!(outside_perm.is_initial());
 
         let default_strongest_idempotent =
-            default_perm.strongest_idempotent_foreign_access(protected);
+            outside_perm.strongest_idempotent_foreign_access(protected);
         // Create the node
         self.nodes.insert(
             idx,
@@ -649,47 +632,57 @@ impl<'tcx> Tree {
                 tag: new_tag,
                 parent: Some(parent_idx),
                 children: SmallVec::default(),
-                default_initial_perm: default_perm,
+                default_initial_perm: outside_perm,
                 default_initial_idempotent_foreign_access: default_strongest_idempotent,
-                debug_info: NodeDebugInfo::new(new_tag, default_perm, span),
+                debug_info: NodeDebugInfo::new(new_tag, outside_perm, span),
             },
         );
         // Register new_tag as a child of parent_tag
         self.nodes.get_mut(parent_idx).unwrap().children.push(idx);
 
+        // We need to know the weakest SIFA for `update_idempotent_foreign_access_after_retag`.
+        let mut min_sifa = default_strongest_idempotent;
         for (Range { start, end }, &perm) in
-            initial_perms.iter(Size::from_bytes(0), initial_perms.size())
+            inside_perms.iter(Size::from_bytes(0), inside_perms.size())
         {
-            assert!(perm.is_initial());
+            assert!(perm.permission.is_initial());
+            assert_eq!(
+                perm.idempotent_foreign_access,
+                perm.permission.strongest_idempotent_foreign_access(protected)
+            );
+
+            min_sifa = cmp::min(min_sifa, perm.idempotent_foreign_access);
             for (_perms_range, perms) in self
                 .rperms
                 .iter_mut(Size::from_bytes(start) + base_offset, Size::from_bytes(end - start))
             {
-                assert!(
-                    default_strongest_idempotent
-                        >= perm.permission.strongest_idempotent_foreign_access(protected)
-                );
                 perms.insert(idx, perm);
             }
         }
 
-        // Inserting the new perms might have broken the SIFA invariant (see `foreign_access_skipping.rs`).
-        // We now weaken the recorded SIFA for our parents, until the invariant is restored.
-        // We could weaken them all to `LocalAccess`, but it is more efficient to compute the SIFA
-        // for the new permission statically, and use that.
-        // See the comment in `tb_reborrow` for why it is correct to use the SIFA of `default_uninit_perm`.
-        self.update_last_accessed_after_retag(parent_idx, default_strongest_idempotent);
+        // Inserting the new perms might have broken the SIFA invariant (see
+        // `foreign_access_skipping.rs`) if the SIFA we inserted is weaker than that of some parent.
+        // We now weaken the recorded SIFA for our parents, until the invariant is restored. We
+        // could weaken them all to `None`, but it is more efficient to compute the SIFA for the new
+        // permission statically, and use that. For this we need the *minimum* SIFA (`None` needs
+        // more fixup than `Write`).
+        self.update_idempotent_foreign_access_after_retag(parent_idx, min_sifa);
 
         interp_ok(())
     }
 
-    /// Restores the SIFA "children are stronger" invariant after a retag.
-    /// See `foreign_access_skipping` and `new_child`.
-    fn update_last_accessed_after_retag(
+    /// Restores the SIFA "children are stronger"/"parents are weaker" invariant after a retag:
+    /// reduce the SIFA of `current` and its parents to be no stronger than `strongest_allowed`.
+    /// See `foreign_access_skipping.rs` and [`Tree::new_child`].
+    fn update_idempotent_foreign_access_after_retag(
         &mut self,
         mut current: UniIndex,
         strongest_allowed: IdempotentForeignAccess,
     ) {
+        if strongest_allowed == IdempotentForeignAccess::Write {
+            // Nothing is stronger than `Write`.
+            return;
+        }
         // We walk the tree upwards, until the invariant is restored
         loop {
             let current_node = self.nodes.get_mut(current).unwrap();
@@ -755,7 +748,9 @@ impl<'tcx> Tree {
                             == Some(&ProtectorKind::StrongProtector)
                             // Don't check for protector if it is a Cell (see `unsafe_cell_deallocate` in `interior_mutability.rs`).
                             // Related to https://github.com/rust-lang/rust/issues/55005.
-                            && !perm.permission().is_cell()
+                            && !perm.permission.is_cell()
+                            // Only trigger UB if the accessed bit is set, i.e. if the protector is actually protecting this offset. See #4579.
+                            && perm.accessed
                         {
                             Err(TransitionError::ProtectedDealloc)
                         } else {
@@ -788,7 +783,7 @@ impl<'tcx> Tree {
     /// - the access will be applied only to accessed locations of the allocation,
     /// - it will not be visible to children,
     /// - it will be recorded as a `FnExit` diagnostic access
-    /// - and it will be a read except if the location is `Active`, i.e. has been written to,
+    /// - and it will be a read except if the location is `Unique`, i.e. has been written to,
     ///   in which case it will be a write.
     ///
     /// `LocationState::perform_access` will take care of raising transition
diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs
index bb3fc2d80b3..189e48eca72 100644
--- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs
+++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs
@@ -106,7 +106,7 @@ fn tree_compacting_is_sound() {
                         as_foreign_or_child(rel),
                         kind,
                         parent.permission(),
-                        as_lazy_or_accessed(child.is_accessed()),
+                        as_lazy_or_accessed(child.accessed()),
                         child.permission(),
                         as_protected(child_protected),
                         np.permission(),
@@ -122,7 +122,7 @@ fn tree_compacting_is_sound() {
                         as_foreign_or_child(rel),
                         kind,
                         parent.permission(),
-                        as_lazy_or_accessed(child.is_accessed()),
+                        as_lazy_or_accessed(child.accessed()),
                         child.permission(),
                         as_protected(child_protected),
                         nc.permission()
@@ -375,7 +375,7 @@ mod spurious_read {
 
     impl LocStateProt {
         fn is_initial(&self) -> bool {
-            self.state.is_initial()
+            self.state.permission().is_initial()
         }
 
         fn perform_access(&self, kind: AccessKind, rel: AccessRelatedness) -> Result<Self, ()> {
@@ -420,7 +420,7 @@ mod spurious_read {
     /// `(LocStateProt, LocStateProt)` where the two states are not guaranteed
     /// to be updated at the same time.
     /// Some `LocStateProtPair` may be unreachable through normal means
-    /// such as `x: Active, y: Active` in the case of mutually foreign pointers.
+    /// such as `x: Unique, y: Unique` in the case of mutually foreign pointers.
     struct LocStateProtPair {
         xy_rel: RelPosXY,
         x: LocStateProt,
@@ -610,7 +610,7 @@ mod spurious_read {
             },
             y: LocStateProt {
                 state: LocationState::new_non_accessed(
-                    Permission::new_reserved(/* freeze */ true, /* protected */ true),
+                    Permission::new_reserved_frz(),
                     IdempotentForeignAccess::default(),
                 ),
                 prot: true,
@@ -709,7 +709,7 @@ mod spurious_read {
         let mut err = 0;
         for pat in Pattern::exhaustive() {
             let Ok(initial_source) = pat.initial_state() else {
-                // Failed to retag `x` in the source (e.g. `y` was protected Active)
+                // Failed to retag `x` in the source (e.g. `y` was protected Unique)
                 continue;
             };
             // `x` must stay protected, but the function protecting `y` might return here
diff --git a/src/tools/miri/src/clock.rs b/src/tools/miri/src/clock.rs
index 34465e9cac6..dbbe741a071 100644
--- a/src/tools/miri/src/clock.rs
+++ b/src/tools/miri/src/clock.rs
@@ -46,14 +46,21 @@ impl Instant {
                 InstantKind::Virtual { nanoseconds: earlier },
             ) => {
                 let duration = nanoseconds.saturating_sub(earlier);
-                // `Duration` does not provide a nice constructor from a `u128` of nanoseconds,
-                // so we have to implement this ourselves.
-                // It is possible for second to overflow because u64::MAX < (u128::MAX / 1e9).
-                // It will be saturated to u64::MAX seconds if the value after division exceeds u64::MAX.
-                let seconds = u64::try_from(duration / 1_000_000_000).unwrap_or(u64::MAX);
-                // It is impossible for nanosecond to overflow because u32::MAX > 1e9.
-                let nanosecond = u32::try_from(duration.wrapping_rem(1_000_000_000)).unwrap();
-                Duration::new(seconds, nanosecond)
+                cfg_select! {
+                    bootstrap => {
+                         // `Duration` does not provide a nice constructor from a `u128` of nanoseconds,
+                        // so we have to implement this ourselves.
+                        // It is possible for second to overflow because u64::MAX < (u128::MAX / 1e9).
+                        // It will be saturated to u64::MAX seconds if the value after division exceeds u64::MAX.
+                        let seconds = u64::try_from(duration / 1_000_000_000).unwrap_or(u64::MAX);
+                        // It is impossible for nanosecond to overflow because u32::MAX > 1e9.
+                        let nanosecond = u32::try_from(duration.wrapping_rem(1_000_000_000)).unwrap();
+                        Duration::new(seconds, nanosecond)
+                    }
+                    _ => {
+                        Duration::from_nanos_u128(duration)
+                    }
+                }
             }
             _ => panic!("all `Instant` must be of the same kind"),
         }
diff --git a/src/tools/miri/src/concurrency/data_race.rs b/src/tools/miri/src/concurrency/data_race.rs
index 38d76f5cf73..1ad9ace1b5d 100644
--- a/src/tools/miri/src/concurrency/data_race.rs
+++ b/src/tools/miri/src/concurrency/data_race.rs
@@ -56,6 +56,7 @@ use super::vector_clock::{VClock, VTimestamp, VectorIdx};
 use super::weak_memory::EvalContextExt as _;
 use crate::concurrency::GlobalDataRaceHandler;
 use crate::diagnostics::RacingOp;
+use crate::intrinsics::AtomicRmwOp;
 use crate::*;
 
 pub type AllocState = VClockAlloc;
@@ -182,6 +183,9 @@ struct AtomicMemoryCellClocks {
     /// contains the vector of timestamps that will
     /// happen-before a thread if an acquire-load is
     /// performed on the data.
+    ///
+    /// With weak memory emulation, this is the clock of the most recent write. It is then only used
+    /// for release sequences, to integrate the most recent clock into the next one for RMWs.
     sync_vector: VClock,
 
     /// The size of accesses to this atomic location.
@@ -275,7 +279,7 @@ struct MemoryCellClocks {
     /// zero on each write operation.
     read: VClock,
 
-    /// Atomic access, acquire, release sequence tracking clocks.
+    /// Atomic access tracking clocks.
     /// For non-atomic memory this value is set to None.
     /// For atomic memory, each byte carries this information.
     atomic_ops: Option<Box<AtomicMemoryCellClocks>>,
@@ -503,10 +507,11 @@ impl MemoryCellClocks {
         thread_clocks: &mut ThreadClockSet,
         index: VectorIdx,
         access_size: Size,
+        sync_clock: Option<&VClock>,
     ) -> Result<(), DataRace> {
         self.atomic_read_detect(thread_clocks, index, access_size)?;
-        if let Some(atomic) = self.atomic() {
-            thread_clocks.clock.join(&atomic.sync_vector);
+        if let Some(sync_clock) = sync_clock.or_else(|| self.atomic().map(|a| &a.sync_vector)) {
+            thread_clocks.clock.join(sync_clock);
         }
         Ok(())
     }
@@ -519,10 +524,11 @@ impl MemoryCellClocks {
         thread_clocks: &mut ThreadClockSet,
         index: VectorIdx,
         access_size: Size,
+        sync_clock: Option<&VClock>,
     ) -> Result<(), DataRace> {
         self.atomic_read_detect(thread_clocks, index, access_size)?;
-        if let Some(atomic) = self.atomic() {
-            thread_clocks.fence_acquire.join(&atomic.sync_vector);
+        if let Some(sync_clock) = sync_clock.or_else(|| self.atomic().map(|a| &a.sync_vector)) {
+            thread_clocks.fence_acquire.join(sync_clock);
         }
         Ok(())
     }
@@ -554,7 +560,8 @@ impl MemoryCellClocks {
         // The handling of release sequences was changed in C++20 and so
         // the code here is different to the paper since now all relaxed
         // stores block release sequences. The exception for same-thread
-        // relaxed stores has been removed.
+        // relaxed stores has been removed. We always overwrite the `sync_vector`,
+        // meaning the previous release sequence is broken.
         let atomic = self.atomic_mut_unwrap();
         atomic.sync_vector.clone_from(&thread_clocks.fence_release);
         Ok(())
@@ -570,6 +577,8 @@ impl MemoryCellClocks {
     ) -> Result<(), DataRace> {
         self.atomic_write_detect(thread_clocks, index, access_size)?;
         let atomic = self.atomic_mut_unwrap();
+        // This *joining* of `sync_vector` implements release sequences: future
+        // reads of this location will acquire our clock *and* what was here before.
         atomic.sync_vector.join(&thread_clocks.clock);
         Ok(())
     }
@@ -584,6 +593,8 @@ impl MemoryCellClocks {
     ) -> Result<(), DataRace> {
         self.atomic_write_detect(thread_clocks, index, access_size)?;
         let atomic = self.atomic_mut_unwrap();
+        // This *joining* of `sync_vector` implements release sequences: future
+        // reads of this location will acquire our fence clock *and* what was here before.
         atomic.sync_vector.join(&thread_clocks.fence_release);
         Ok(())
     }
@@ -719,8 +730,7 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> {
         // Only metadata on the location itself is used.
 
         if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() {
-            // FIXME(GenMC): Inform GenMC what a non-atomic read here would return, to support mixed atomics/non-atomics
-            let old_val = None;
+            let old_val = this.run_for_validation_ref(|this| this.read_scalar(place)).discard_err();
             return genmc_ctx.atomic_load(
                 this,
                 place.ptr().addr(),
@@ -731,8 +741,8 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> {
         }
 
         let scalar = this.allow_data_races_ref(move |this| this.read_scalar(place))?;
-        let buffered_scalar = this.buffered_atomic_read(place, atomic, scalar, || {
-            this.validate_atomic_load(place, atomic)
+        let buffered_scalar = this.buffered_atomic_read(place, atomic, scalar, |sync_clock| {
+            this.validate_atomic_load(place, atomic, sync_clock)
         })?;
         interp_ok(buffered_scalar.ok_or_else(|| err_ub!(InvalidUninitBytes(None)))?)
     }
@@ -751,11 +761,22 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> {
         // The program didn't actually do a read, so suppress the memory access hooks.
         // This is also a very special exception where we just ignore an error -- if this read
         // was UB e.g. because the memory is uninitialized, we don't want to know!
-        let old_val = this.run_for_validation_mut(|this| this.read_scalar(dest)).discard_err();
+        let old_val = this.run_for_validation_ref(|this| this.read_scalar(dest)).discard_err();
+
         // Inform GenMC about the atomic store.
         if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() {
-            // FIXME(GenMC): Inform GenMC what a non-atomic read here would return, to support mixed atomics/non-atomics
-            genmc_ctx.atomic_store(this, dest.ptr().addr(), dest.layout.size, val, atomic)?;
+            if genmc_ctx.atomic_store(
+                this,
+                dest.ptr().addr(),
+                dest.layout.size,
+                val,
+                old_val,
+                atomic,
+            )? {
+                // The store might be the latest store in coherence order (determined by GenMC).
+                // If it is, we need to update the value in Miri's memory:
+                this.allow_data_races_mut(|this| this.write_scalar(val, dest))?;
+            }
             return interp_ok(());
         }
         this.allow_data_races_mut(move |this| this.write_scalar(val, dest))?;
@@ -768,9 +789,8 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> {
         &mut self,
         place: &MPlaceTy<'tcx>,
         rhs: &ImmTy<'tcx>,
-        op: mir::BinOp,
-        not: bool,
-        atomic: AtomicRwOrd,
+        atomic_op: AtomicRmwOp,
+        ord: AtomicRwOrd,
     ) -> InterpResult<'tcx, ImmTy<'tcx>> {
         let this = self.eval_context_mut();
         this.atomic_access_check(place, AtomicAccessType::Rmw)?;
@@ -779,26 +799,42 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> {
 
         // Inform GenMC about the atomic rmw operation.
         if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() {
-            // FIXME(GenMC): Inform GenMC what a non-atomic read here would return, to support mixed atomics/non-atomics
             let (old_val, new_val) = genmc_ctx.atomic_rmw_op(
                 this,
                 place.ptr().addr(),
                 place.layout.size,
-                atomic,
-                (op, not),
+                atomic_op,
+                place.layout.backend_repr.is_signed(),
+                ord,
                 rhs.to_scalar(),
+                old.to_scalar(),
             )?;
-            this.allow_data_races_mut(|this| this.write_scalar(new_val, place))?;
+            if let Some(new_val) = new_val {
+                this.allow_data_races_mut(|this| this.write_scalar(new_val, place))?;
+            }
             return interp_ok(ImmTy::from_scalar(old_val, old.layout));
         }
 
-        let val = this.binary_op(op, &old, rhs)?;
-        let val = if not { this.unary_op(mir::UnOp::Not, &val)? } else { val };
+        let val = match atomic_op {
+            AtomicRmwOp::MirOp { op, neg } => {
+                let val = this.binary_op(op, &old, rhs)?;
+                if neg { this.unary_op(mir::UnOp::Not, &val)? } else { val }
+            }
+            AtomicRmwOp::Max => {
+                let lt = this.binary_op(mir::BinOp::Lt, &old, rhs)?.to_scalar().to_bool()?;
+                if lt { rhs } else { &old }.clone()
+            }
+            AtomicRmwOp::Min => {
+                let lt = this.binary_op(mir::BinOp::Lt, &old, rhs)?.to_scalar().to_bool()?;
+                if lt { &old } else { rhs }.clone()
+            }
+        };
+
         this.allow_data_races_mut(|this| this.write_immediate(*val, place))?;
 
-        this.validate_atomic_rmw(place, atomic)?;
+        this.validate_atomic_rmw(place, ord)?;
 
-        this.buffered_atomic_rmw(val.to_scalar(), place, atomic, old.to_scalar())?;
+        this.buffered_atomic_rmw(val.to_scalar(), place, ord, old.to_scalar())?;
         interp_ok(old)
     }
 
@@ -818,14 +854,19 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> {
 
         // Inform GenMC about the atomic atomic exchange.
         if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() {
-            // FIXME(GenMC): Inform GenMC what a non-atomic read here would return, to support mixed atomics/non-atomics
-            let (old_val, _is_success) = genmc_ctx.atomic_exchange(
+            let (old_val, new_val) = genmc_ctx.atomic_exchange(
                 this,
                 place.ptr().addr(),
                 place.layout.size,
                 new,
                 atomic,
+                old,
             )?;
+            // The store might be the latest store in coherence order (determined by GenMC).
+            // If it is, we need to update the value in Miri's memory:
+            if let Some(new_val) = new_val {
+                this.allow_data_races_mut(|this| this.write_scalar(new_val, place))?;
+            }
             return interp_ok(old_val);
         }
 
@@ -835,55 +876,6 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> {
         interp_ok(old)
     }
 
-    /// Perform an conditional atomic exchange with a memory place and a new
-    /// scalar value, the old value is returned.
-    fn atomic_min_max_scalar(
-        &mut self,
-        place: &MPlaceTy<'tcx>,
-        rhs: ImmTy<'tcx>,
-        min: bool,
-        atomic: AtomicRwOrd,
-    ) -> InterpResult<'tcx, ImmTy<'tcx>> {
-        let this = self.eval_context_mut();
-        this.atomic_access_check(place, AtomicAccessType::Rmw)?;
-
-        let old = this.allow_data_races_mut(|this| this.read_immediate(place))?;
-
-        // Inform GenMC about the atomic min/max operation.
-        if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() {
-            // FIXME(GenMC): Inform GenMC what a non-atomic read here would return, to support mixed atomics/non-atomics
-            let (old_val, new_val) = genmc_ctx.atomic_min_max_op(
-                this,
-                place.ptr().addr(),
-                place.layout.size,
-                atomic,
-                min,
-                old.layout.backend_repr.is_signed(),
-                rhs.to_scalar(),
-            )?;
-            this.allow_data_races_mut(|this| this.write_scalar(new_val, place))?;
-            return interp_ok(ImmTy::from_scalar(old_val, old.layout));
-        }
-
-        let lt = this.binary_op(mir::BinOp::Lt, &old, &rhs)?.to_scalar().to_bool()?;
-
-        #[rustfmt::skip] // rustfmt makes this unreadable
-        let new_val = if min {
-            if lt { &old } else { &rhs }
-        } else {
-            if lt { &rhs } else { &old }
-        };
-
-        this.allow_data_races_mut(|this| this.write_immediate(**new_val, place))?;
-
-        this.validate_atomic_rmw(place, atomic)?;
-
-        this.buffered_atomic_rmw(new_val.to_scalar(), place, atomic, old.to_scalar())?;
-
-        // Return the old value.
-        interp_ok(old)
-    }
-
     /// Perform an atomic compare and exchange at a given memory location.
     /// On success an atomic RMW operation is performed and on failure
     /// only an atomic read occurs. If `can_fail_spuriously` is true,
@@ -903,15 +895,12 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> {
         let this = self.eval_context_mut();
         this.atomic_access_check(place, AtomicAccessType::Rmw)?;
 
-        // Failure ordering cannot be stronger than success ordering, therefore first attempt
-        // to read with the failure ordering and if successful then try again with the success
-        // read ordering and write in the success case.
         // Read as immediate for the sake of `binary_op()`
         let old = this.allow_data_races_mut(|this| this.read_immediate(place))?;
 
         // Inform GenMC about the atomic atomic compare exchange.
         if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() {
-            let (old, cmpxchg_success) = genmc_ctx.atomic_compare_exchange(
+            let (old_value, new_value, cmpxchg_success) = genmc_ctx.atomic_compare_exchange(
                 this,
                 place.ptr().addr(),
                 place.layout.size,
@@ -920,11 +909,14 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> {
                 success,
                 fail,
                 can_fail_spuriously,
+                old.to_scalar(),
             )?;
-            if cmpxchg_success {
-                this.allow_data_races_mut(|this| this.write_scalar(new, place))?;
+            // The store might be the latest store in coherence order (determined by GenMC).
+            // If it is, we need to update the value in Miri's memory:
+            if let Some(new_value) = new_value {
+                this.allow_data_races_mut(|this| this.write_scalar(new_value, place))?;
             }
-            return interp_ok(Immediate::ScalarPair(old, Scalar::from_bool(cmpxchg_success)));
+            return interp_ok(Immediate::ScalarPair(old_value, Scalar::from_bool(cmpxchg_success)));
         }
 
         // `binary_op` will bail if either of them is not a scalar.
@@ -948,7 +940,7 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> {
             this.validate_atomic_rmw(place, success)?;
             this.buffered_atomic_rmw(new, place, success, old.to_scalar())?;
         } else {
-            this.validate_atomic_load(place, fail)?;
+            this.validate_atomic_load(place, fail, /* can use latest sync clock */ None)?;
             // A failed compare exchange is equivalent to a load, reading from the latest store
             // in the modification order.
             // Since `old` is only a value and not the store element, we need to separately
@@ -976,20 +968,36 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> {
     /// with this program point.
     ///
     /// The closure will only be invoked if data race handling is on.
-    fn release_clock<R>(&self, callback: impl FnOnce(&VClock) -> R) -> Option<R> {
+    fn release_clock<R>(
+        &self,
+        callback: impl FnOnce(&VClock) -> R,
+    ) -> InterpResult<'tcx, Option<R>> {
         let this = self.eval_context_ref();
-        Some(
-            this.machine.data_race.as_vclocks_ref()?.release_clock(&this.machine.threads, callback),
-        )
+        interp_ok(match &this.machine.data_race {
+            GlobalDataRaceHandler::None => None,
+            GlobalDataRaceHandler::Genmc(_genmc_ctx) =>
+                throw_unsup_format!(
+                    "this operation performs synchronization that is not supported in GenMC mode"
+                ),
+            GlobalDataRaceHandler::Vclocks(data_race) =>
+                Some(data_race.release_clock(&this.machine.threads, callback)),
+        })
     }
 
     /// Acquire the given clock into the current thread, establishing synchronization with
     /// the moment when that clock snapshot was taken via `release_clock`.
-    fn acquire_clock(&self, clock: &VClock) {
+    fn acquire_clock(&self, clock: &VClock) -> InterpResult<'tcx> {
         let this = self.eval_context_ref();
-        if let Some(data_race) = this.machine.data_race.as_vclocks_ref() {
-            data_race.acquire_clock(clock, &this.machine.threads);
+        match &this.machine.data_race {
+            GlobalDataRaceHandler::None => {}
+            GlobalDataRaceHandler::Genmc(_genmc_ctx) =>
+                throw_unsup_format!(
+                    "this operation performs synchronization that is not supported in GenMC mode"
+                ),
+            GlobalDataRaceHandler::Vclocks(data_race) =>
+                data_race.acquire_clock(clock, &this.machine.threads),
         }
+        interp_ok(())
     }
 }
 
@@ -1174,6 +1182,18 @@ impl VClockAlloc {
         }))?
     }
 
+    /// Return the release/acquire synchronization clock for the given memory range.
+    pub(super) fn sync_clock(&self, access_range: AllocRange) -> VClock {
+        let alloc_ranges = self.alloc_ranges.borrow();
+        let mut clock = VClock::default();
+        for (_, mem_clocks) in alloc_ranges.iter(access_range.start, access_range.size) {
+            if let Some(atomic) = mem_clocks.atomic() {
+                clock.join(&atomic.sync_vector);
+            }
+        }
+        clock
+    }
+
     /// Detect data-races for an unsynchronized read operation. It will not perform
     /// data-race detection if `race_detecting()` is false, either due to no threads
     /// being created or if it is temporarily disabled during a racy read or write
@@ -1450,6 +1470,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
         &self,
         place: &MPlaceTy<'tcx>,
         atomic: AtomicReadOrd,
+        sync_clock: Option<&VClock>,
     ) -> InterpResult<'tcx> {
         let this = self.eval_context_ref();
         this.validate_atomic_op(
@@ -1458,9 +1479,9 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
             AccessType::AtomicLoad,
             move |memory, clocks, index, atomic| {
                 if atomic == AtomicReadOrd::Relaxed {
-                    memory.load_relaxed(&mut *clocks, index, place.layout.size)
+                    memory.load_relaxed(&mut *clocks, index, place.layout.size, sync_clock)
                 } else {
-                    memory.load_acquire(&mut *clocks, index, place.layout.size)
+                    memory.load_acquire(&mut *clocks, index, place.layout.size, sync_clock)
                 }
             },
         )
@@ -1505,9 +1526,9 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
             AccessType::AtomicRmw,
             move |memory, clocks, index, _| {
                 if acquire {
-                    memory.load_acquire(clocks, index, place.layout.size)?;
+                    memory.load_acquire(clocks, index, place.layout.size, None)?;
                 } else {
-                    memory.load_relaxed(clocks, index, place.layout.size)?;
+                    memory.load_relaxed(clocks, index, place.layout.size, None)?;
                 }
                 if release {
                     memory.rmw_release(clocks, index, place.layout.size)
diff --git a/src/tools/miri/src/concurrency/genmc/config.rs b/src/tools/miri/src/concurrency/genmc/config.rs
index c56adab90fe..c7cfa6012b8 100644
--- a/src/tools/miri/src/concurrency/genmc/config.rs
+++ b/src/tools/miri/src/concurrency/genmc/config.rs
@@ -1,13 +1,24 @@
+use genmc_sys::LogLevel;
+
 use super::GenmcParams;
+use crate::{IsolatedOp, MiriConfig, RejectOpWith};
 
 /// Configuration for GenMC mode.
 /// The `params` field is shared with the C++ side.
 /// The remaining options are kept on the Rust side.
 #[derive(Debug, Default, Clone)]
 pub struct GenmcConfig {
+    /// Parameters sent to the C++ side to create a new handle to the GenMC model checker.
     pub(super) params: GenmcParams,
-    do_estimation: bool,
-    // FIXME(GenMC): add remaining options.
+    pub(super) do_estimation: bool,
+    /// Print the output message that GenMC generates when an error occurs.
+    /// This error message is currently hard to use, since there is no clear mapping between the events that GenMC sees and the Rust code location where this event was produced.
+    pub(super) print_genmc_output: bool,
+    /// The log level for GenMC.
+    pub(super) log_level: LogLevel,
+    /// Enable more verbose output, such as number of executions estimate
+    /// and time to completion of verification step.
+    pub(super) verbose_output: bool,
 }
 
 impl GenmcConfig {
@@ -29,7 +40,80 @@ impl GenmcConfig {
         if trimmed_arg.is_empty() {
             return Ok(()); // this corresponds to "-Zmiri-genmc"
         }
-        // FIXME(GenMC): implement remaining parameters.
-        todo!();
+        let genmc_config = genmc_config.as_mut().unwrap();
+        let Some(trimmed_arg) = trimmed_arg.strip_prefix("-") else {
+            return Err(format!("Invalid GenMC argument \"-Zmiri-genmc{trimmed_arg}\""));
+        };
+        if let Some(log_level) = trimmed_arg.strip_prefix("log=") {
+            genmc_config.log_level = log_level.parse()?;
+        } else if let Some(trimmed_arg) = trimmed_arg.strip_prefix("print-exec-graphs") {
+            use genmc_sys::ExecutiongraphPrinting;
+            genmc_config.params.print_execution_graphs = match trimmed_arg {
+                "=none" => ExecutiongraphPrinting::None,
+                // Make GenMC print explored executions.
+                "" | "=explored" => ExecutiongraphPrinting::Explored,
+                // Make GenMC print blocked executions.
+                "=blocked" => ExecutiongraphPrinting::Blocked,
+                // Make GenMC print all executions.
+                "=all" => ExecutiongraphPrinting::ExploredAndBlocked,
+                _ =>
+                    return Err(format!(
+                        "Invalid suffix to GenMC argument '-Zmiri-genmc-print-exec-graphs', expected '', '=none', '=explored', '=blocked' or '=all'"
+                    )),
+            }
+        } else if trimmed_arg == "estimate" {
+            // FIXME(genmc): should this be on by default (like for GenMC)?
+            // Enable estimating the execution space and require time before running the actual verification.
+            genmc_config.do_estimation = true;
+        } else if let Some(estimation_max_str) = trimmed_arg.strip_prefix("estimation-max=") {
+            // Set the maximum number of executions to explore during estimation.
+            genmc_config.params.estimation_max = estimation_max_str.parse().ok().filter(|estimation_max| *estimation_max > 0).ok_or_else(|| {
+                format!(
+                    "'-Zmiri-genmc-estimation-max=...' expects a positive integer argument, but got '{estimation_max_str}'"
+                )
+            })?;
+        } else if trimmed_arg == "print-genmc-output" {
+            genmc_config.print_genmc_output = true;
+        } else if trimmed_arg == "verbose" {
+            genmc_config.verbose_output = true;
+        } else {
+            return Err(format!("Invalid GenMC argument: \"-Zmiri-genmc-{trimmed_arg}\""));
+        }
+        Ok(())
+    }
+
+    /// Validate settings for GenMC mode (NOP if GenMC mode disabled).
+    ///
+    /// Unsupported configurations return an error.
+    /// Adjusts Miri settings where required, printing a warnings if the change might be unexpected for the user.
+    pub fn validate_genmc_mode_settings(miri_config: &mut MiriConfig) -> Result<(), &'static str> {
+        let Some(genmc_config) = miri_config.genmc_config.as_mut() else {
+            return Ok(());
+        };
+
+        // Check for disallowed configurations.
+        if !miri_config.data_race_detector {
+            return Err("Cannot disable data race detection in GenMC mode");
+        } else if !miri_config.native_lib.is_empty() {
+            return Err("native-lib not supported in GenMC mode.");
+        } else if miri_config.isolated_op != IsolatedOp::Reject(RejectOpWith::Abort) {
+            return Err("Cannot disable isolation in GenMC mode");
+        }
+
+        // Adjust settings where needed.
+        if !miri_config.weak_memory_emulation {
+            genmc_config.params.disable_weak_memory_emulation = true;
+        }
+        if miri_config.borrow_tracker.is_some() {
+            eprintln!(
+                "warning: borrow tracking has been disabled, it is not (yet) supported in GenMC mode."
+            );
+            miri_config.borrow_tracker = None;
+        }
+        // We enable fixed scheduling so Miri doesn't randomly yield before a terminator, which anyway
+        // would be a NOP in GenMC mode.
+        miri_config.fixed_scheduling = true;
+
+        Ok(())
     }
 }
diff --git a/src/tools/miri/src/concurrency/genmc/dummy.rs b/src/tools/miri/src/concurrency/genmc/dummy.rs
index 79d27c4be15..c28984cef35 100644
--- a/src/tools/miri/src/concurrency/genmc/dummy.rs
+++ b/src/tools/miri/src/concurrency/genmc/dummy.rs
@@ -1,49 +1,45 @@
-#![allow(unused)]
-
 use rustc_abi::{Align, Size};
-use rustc_const_eval::interpret::{InterpCx, InterpResult};
-use rustc_middle::mir;
+use rustc_const_eval::interpret::{AllocId, InterpCx, InterpResult};
 
+pub use self::run::run_genmc_mode;
+use crate::intrinsics::AtomicRmwOp;
 use crate::{
-    AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, MemoryKind, MiriConfig,
-    MiriMachine, Scalar, ThreadId, ThreadManager, VisitProvenance, VisitWith,
+    AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, MemoryKind, MiriMachine, Scalar,
+    ThreadId, ThreadManager, VisitProvenance, VisitWith,
 };
 
+#[derive(Clone, Copy, Debug)]
+pub enum ExitType {
+    MainThreadFinish,
+    ExitCalled,
+}
+
 #[derive(Debug)]
 pub struct GenmcCtx {}
 
 #[derive(Debug, Default, Clone)]
 pub struct GenmcConfig {}
 
-impl GenmcCtx {
-    pub fn new(_miri_config: &MiriConfig) -> Self {
-        unreachable!()
-    }
+mod run {
+    use std::rc::Rc;
 
-    pub fn get_stuck_execution_count(&self) -> usize {
-        unreachable!()
-    }
+    use rustc_middle::ty::TyCtxt;
 
-    pub fn print_genmc_graph(&self) {
-        unreachable!()
-    }
+    use crate::{GenmcCtx, MiriConfig};
 
-    pub fn is_exploration_done(&self) -> bool {
-        unreachable!()
+    pub fn run_genmc_mode<'tcx>(
+        _config: &MiriConfig,
+        _eval_entry: impl Fn(Rc<GenmcCtx>) -> Option<i32>,
+        _tcx: TyCtxt<'tcx>,
+    ) -> Option<i32> {
+        unreachable!();
     }
+}
 
-    /**** Memory access handling ****/
-
-    pub(crate) fn handle_execution_start(&self) {
-        unreachable!()
-    }
+impl GenmcCtx {
+    // We don't provide the `new` function in the dummy module.
 
-    pub(crate) fn handle_execution_end<'tcx>(
-        &self,
-        _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>,
-    ) -> Result<(), String> {
-        unreachable!()
-    }
+    /**** Memory access handling ****/
 
     pub(super) fn set_ongoing_action_data_race_free(&self, _enable: bool) {
         unreachable!()
@@ -67,8 +63,9 @@ impl GenmcCtx {
         _address: Size,
         _size: Size,
         _value: Scalar,
+        _old_value: Option<Scalar>,
         _ordering: AtomicWriteOrd,
-    ) -> InterpResult<'tcx, ()> {
+    ) -> InterpResult<'tcx, bool> {
         unreachable!()
     }
 
@@ -76,7 +73,7 @@ impl GenmcCtx {
         &self,
         _machine: &MiriMachine<'tcx>,
         _ordering: AtomicFenceOrd,
-    ) -> InterpResult<'tcx, ()> {
+    ) -> InterpResult<'tcx> {
         unreachable!()
     }
 
@@ -85,23 +82,12 @@ impl GenmcCtx {
         _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>,
         _address: Size,
         _size: Size,
+        _atomic_op: AtomicRmwOp,
+        _is_signed: bool,
         _ordering: AtomicRwOrd,
-        (rmw_op, not): (mir::BinOp, bool),
         _rhs_scalar: Scalar,
-    ) -> InterpResult<'tcx, (Scalar, Scalar)> {
-        unreachable!()
-    }
-
-    pub(crate) fn atomic_min_max_op<'tcx>(
-        &self,
-        ecx: &InterpCx<'tcx, MiriMachine<'tcx>>,
-        address: Size,
-        size: Size,
-        ordering: AtomicRwOrd,
-        min: bool,
-        is_signed: bool,
-        rhs_scalar: Scalar,
-    ) -> InterpResult<'tcx, (Scalar, Scalar)> {
+        _old_value: Scalar,
+    ) -> InterpResult<'tcx, (Scalar, Option<Scalar>)> {
         unreachable!()
     }
 
@@ -112,7 +98,8 @@ impl GenmcCtx {
         _size: Size,
         _rhs_scalar: Scalar,
         _ordering: AtomicRwOrd,
-    ) -> InterpResult<'tcx, (Scalar, bool)> {
+        _old_value: Scalar,
+    ) -> InterpResult<'tcx, (Scalar, Option<Scalar>)> {
         unreachable!()
     }
 
@@ -126,7 +113,8 @@ impl GenmcCtx {
         _success: AtomicRwOrd,
         _fail: AtomicReadOrd,
         _can_fail_spuriously: bool,
-    ) -> InterpResult<'tcx, (Scalar, bool)> {
+        _old_value: Scalar,
+    ) -> InterpResult<'tcx, (Scalar, Option<Scalar>, bool)> {
         unreachable!()
     }
 
@@ -135,7 +123,7 @@ impl GenmcCtx {
         _machine: &MiriMachine<'tcx>,
         _address: Size,
         _size: Size,
-    ) -> InterpResult<'tcx, ()> {
+    ) -> InterpResult<'tcx> {
         unreachable!()
     }
 
@@ -144,7 +132,7 @@ impl GenmcCtx {
         _machine: &MiriMachine<'tcx>,
         _address: Size,
         _size: Size,
-    ) -> InterpResult<'tcx, ()> {
+    ) -> InterpResult<'tcx> {
         unreachable!()
     }
 
@@ -152,7 +140,8 @@ impl GenmcCtx {
 
     pub(crate) fn handle_alloc<'tcx>(
         &self,
-        _machine: &MiriMachine<'tcx>,
+        _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>,
+        _alloc_id: AllocId,
         _size: Size,
         _alignment: Align,
         _memory_kind: MemoryKind,
@@ -163,11 +152,10 @@ impl GenmcCtx {
     pub(crate) fn handle_dealloc<'tcx>(
         &self,
         _machine: &MiriMachine<'tcx>,
+        _alloc_id: AllocId,
         _address: Size,
-        _size: Size,
-        _align: Align,
         _kind: MemoryKind,
-    ) -> InterpResult<'tcx, ()> {
+    ) -> InterpResult<'tcx> {
         unreachable!()
     }
 
@@ -176,8 +164,10 @@ impl GenmcCtx {
     pub(crate) fn handle_thread_create<'tcx>(
         &self,
         _threads: &ThreadManager<'tcx>,
+        _start_routine: crate::Pointer,
+        _func_arg: &crate::ImmTy<'tcx>,
         _new_thread_id: ThreadId,
-    ) -> InterpResult<'tcx, ()> {
+    ) -> InterpResult<'tcx> {
         unreachable!()
     }
 
@@ -185,24 +175,26 @@ impl GenmcCtx {
         &self,
         _active_thread_id: ThreadId,
         _child_thread_id: ThreadId,
-    ) -> InterpResult<'tcx, ()> {
+    ) -> InterpResult<'tcx> {
         unreachable!()
     }
 
-    pub(crate) fn handle_thread_stack_empty(&self, _thread_id: ThreadId) {
+    pub(crate) fn handle_thread_finish<'tcx>(&self, _threads: &ThreadManager<'tcx>) {
         unreachable!()
     }
 
-    pub(crate) fn handle_thread_finish<'tcx>(
+    pub(crate) fn handle_exit<'tcx>(
         &self,
-        _threads: &ThreadManager<'tcx>,
-    ) -> InterpResult<'tcx, ()> {
+        _thread: ThreadId,
+        _exit_code: i32,
+        _exit_type: ExitType,
+    ) -> InterpResult<'tcx> {
         unreachable!()
     }
 
     /**** Scheduling functionality ****/
 
-    pub(crate) fn schedule_thread<'tcx>(
+    pub fn schedule_thread<'tcx>(
         &self,
         _ecx: &InterpCx<'tcx, MiriMachine<'tcx>>,
     ) -> InterpResult<'tcx, ThreadId> {
@@ -211,6 +203,7 @@ impl GenmcCtx {
 
     /**** Blocking instructions ****/
 
+    #[allow(unused)]
     pub(crate) fn handle_verifier_assume<'tcx>(
         &self,
         _machine: &MiriMachine<'tcx>,
@@ -229,7 +222,7 @@ impl VisitProvenance for GenmcCtx {
 impl GenmcConfig {
     pub fn parse_arg(
         _genmc_config: &mut Option<GenmcConfig>,
-        trimmed_arg: &str,
+        _trimmed_arg: &str,
     ) -> Result<(), String> {
         if cfg!(feature = "genmc") {
             Err(format!("GenMC is disabled in this build of Miri"))
@@ -238,7 +231,9 @@ impl GenmcConfig {
         }
     }
 
-    pub fn should_print_graph(&self, _rep: usize) -> bool {
-        unreachable!()
+    pub fn validate_genmc_mode_settings(
+        _miri_config: &mut crate::MiriConfig,
+    ) -> Result<(), &'static str> {
+        Ok(())
     }
 }
diff --git a/src/tools/miri/src/concurrency/genmc/global_allocations.rs b/src/tools/miri/src/concurrency/genmc/global_allocations.rs
new file mode 100644
index 00000000000..272f8d24840
--- /dev/null
+++ b/src/tools/miri/src/concurrency/genmc/global_allocations.rs
@@ -0,0 +1,118 @@
+use std::collections::hash_map::Entry;
+use std::sync::RwLock;
+
+use genmc_sys::{GENMC_GLOBAL_ADDRESSES_MASK, get_global_alloc_static_mask};
+use rand::SeedableRng;
+use rand::rngs::StdRng;
+use rustc_const_eval::interpret::{AllocId, AllocInfo, InterpResult, interp_ok};
+use rustc_data_structures::fx::FxHashMap;
+use tracing::debug;
+
+use crate::alloc_addresses::AddressGenerator;
+
+#[derive(Debug)]
+struct GlobalStateInner {
+    /// The base address for each *global* allocation.
+    base_addr: FxHashMap<AllocId, u64>,
+    /// We use the same address generator that Miri uses in normal operation.
+    address_generator: AddressGenerator,
+    /// The address generator needs an Rng to randomize the offsets between allocations.
+    /// We don't use the `MiriMachine` Rng since this is global, cross-machine state.
+    rng: StdRng,
+}
+
+/// Allocator for global memory in GenMC mode.
+/// Miri doesn't discover all global allocations statically like LLI does for GenMC.
+/// The existing global memory allocator in GenMC doesn't support this, so we take over these allocations.
+/// Global allocations need to be in a specific address range, with the lower limit given by the `GENMC_GLOBAL_ADDRESSES_MASK` constant.
+///
+/// Every global allocation must have the same addresses across all executions of a single program.
+/// Therefore there is only 1 global allocator, and it syncs new globals across executions, even if they are explored in parallel.
+#[derive(Debug)]
+pub struct GlobalAllocationHandler(RwLock<GlobalStateInner>);
+
+impl GlobalAllocationHandler {
+    /// Create a new global address generator with a given max address `last_addr`
+    /// (corresponding to the highest address available on the target platform, unless another limit exists).
+    /// No addresses higher than this will be allocated.
+    /// Will panic if the given address limit is too small to allocate any addresses.
+    pub fn new(last_addr: u64) -> GlobalAllocationHandler {
+        assert_eq!(GENMC_GLOBAL_ADDRESSES_MASK, get_global_alloc_static_mask());
+        assert_ne!(GENMC_GLOBAL_ADDRESSES_MASK, 0);
+        // FIXME(genmc): Remove if non-64bit targets are supported.
+        assert!(
+            GENMC_GLOBAL_ADDRESSES_MASK < last_addr,
+            "only 64bit platforms are currently supported (highest address {last_addr:#x} <= minimum global address {GENMC_GLOBAL_ADDRESSES_MASK:#x})."
+        );
+        Self(RwLock::new(GlobalStateInner {
+            base_addr: FxHashMap::default(),
+            address_generator: AddressGenerator::new(GENMC_GLOBAL_ADDRESSES_MASK..last_addr),
+            // FIXME(genmc): We could provide a way to changes this seed, to allow for different global addresses.
+            rng: StdRng::seed_from_u64(0),
+        }))
+    }
+}
+
+impl GlobalStateInner {
+    fn global_allocate_addr<'tcx>(
+        &mut self,
+        alloc_id: AllocId,
+        info: AllocInfo,
+    ) -> InterpResult<'tcx, u64> {
+        let entry = match self.base_addr.entry(alloc_id) {
+            Entry::Occupied(occupied_entry) => {
+                // Looks like some other thread allocated this for us
+                // between when we released the read lock and aquired the write lock,
+                // so we just return that value.
+                return interp_ok(*occupied_entry.get());
+            }
+            Entry::Vacant(vacant_entry) => vacant_entry,
+        };
+
+        // This allocation does not have a base address yet, pick or reuse one.
+        // We are not in native lib mode (incompatible with GenMC mode), so we control the addresses ourselves.
+        let new_addr = self.address_generator.generate(info.size, info.align, &mut self.rng)?;
+
+        // Cache the address for future use.
+        entry.insert(new_addr);
+
+        interp_ok(new_addr)
+    }
+}
+
+impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
+pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
+    /// Allocate a new address for the given alloc id, or return the cached address.
+    /// Each alloc id is assigned one unique allocation which will not change if this function is called again with the same alloc id.
+    fn get_global_allocation_address(
+        &self,
+        global_allocation_handler: &GlobalAllocationHandler,
+        alloc_id: AllocId,
+    ) -> InterpResult<'tcx, u64> {
+        let this = self.eval_context_ref();
+        let info = this.get_alloc_info(alloc_id);
+
+        let global_state = global_allocation_handler.0.read().unwrap();
+        if let Some(base_addr) = global_state.base_addr.get(&alloc_id) {
+            debug!(
+                "GenMC: address for global with alloc id {alloc_id:?} was cached: {base_addr} == {base_addr:#x}"
+            );
+            return interp_ok(*base_addr);
+        }
+
+        // We need to upgrade to a write lock. `std::sync::RwLock` doesn't support this, so we drop the guard and lock again
+        // Note that another thread might allocate the address while the `RwLock` is unlocked, but we handle this case in the allocation function.
+        drop(global_state);
+        let mut global_state = global_allocation_handler.0.write().unwrap();
+        // With the write lock, we can safely allocate an address only once per `alloc_id`.
+        let new_addr = global_state.global_allocate_addr(alloc_id, info)?;
+        debug!("GenMC: global with alloc id {alloc_id:?} got address: {new_addr} == {new_addr:#x}");
+        assert_eq!(
+            GENMC_GLOBAL_ADDRESSES_MASK,
+            new_addr & GENMC_GLOBAL_ADDRESSES_MASK,
+            "Global address allocated outside global address space."
+        );
+
+        interp_ok(new_addr)
+    }
+}
diff --git a/src/tools/miri/src/concurrency/genmc/helper.rs b/src/tools/miri/src/concurrency/genmc/helper.rs
new file mode 100644
index 00000000000..48a5ec8bb26
--- /dev/null
+++ b/src/tools/miri/src/concurrency/genmc/helper.rs
@@ -0,0 +1,225 @@
+use std::sync::RwLock;
+
+use genmc_sys::{MemOrdering, RMWBinOp};
+use rustc_abi::Size;
+use rustc_const_eval::interpret::{InterpResult, interp_ok};
+use rustc_data_structures::fx::FxHashSet;
+use rustc_middle::mir;
+use rustc_middle::ty::ScalarInt;
+use rustc_span::Span;
+use tracing::debug;
+
+use super::GenmcScalar;
+use crate::diagnostics::EvalContextExt;
+use crate::intrinsics::AtomicRmwOp;
+use crate::{
+    AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, InterpCx, MiriInterpCx,
+    MiriMachine, NonHaltingDiagnostic, Scalar, throw_unsup_format,
+};
+
+/// Maximum size memory access in bytes that GenMC supports.
+pub(super) const MAX_ACCESS_SIZE: u64 = 8;
+
+/// Type for storing spans for already emitted warnings.
+pub(super) type WarningCache = RwLock<FxHashSet<Span>>;
+
+#[derive(Default)]
+pub(super) struct Warnings {
+    pub(super) compare_exchange_failure_ordering: WarningCache,
+    pub(super) compare_exchange_weak: WarningCache,
+}
+
+/// Emit a warning if it hasn't already been reported for current span.
+pub(super) fn emit_warning<'tcx>(
+    ecx: &InterpCx<'tcx, MiriMachine<'tcx>>,
+    cache: &WarningCache,
+    diagnostic: impl FnOnce() -> NonHaltingDiagnostic,
+) {
+    let span = ecx.machine.current_span();
+    if cache.read().unwrap().contains(&span) {
+        return;
+    }
+    // This span has not yet been reported, so we insert it into the cache and report it.
+    let mut cache = cache.write().unwrap();
+    if cache.insert(span) {
+        // Some other thread may have added this span while we didn't hold the lock, so we only emit it if the insertions succeeded.
+        ecx.emit_diagnostic(diagnostic());
+    }
+}
+
+/// This function is used to split up a large memory access into aligned, non-overlapping chunks of a limited size.
+/// Returns an iterator over the chunks, yielding `(base address, size)` of each chunk, ordered by address.
+pub fn split_access(address: Size, size: Size) -> impl Iterator<Item = (u64, u64)> {
+    let start_address = address.bytes();
+    let end_address = start_address + size.bytes();
+
+    let start_address_aligned = start_address.next_multiple_of(MAX_ACCESS_SIZE);
+    let end_address_aligned = (end_address / MAX_ACCESS_SIZE) * MAX_ACCESS_SIZE; // prev_multiple_of
+
+    debug!(
+        "GenMC: splitting NA memory access into {MAX_ACCESS_SIZE} byte chunks: {}B + {} * {MAX_ACCESS_SIZE}B + {}B = {size:?}",
+        start_address_aligned - start_address,
+        (end_address_aligned - start_address_aligned) / MAX_ACCESS_SIZE,
+        end_address - end_address_aligned,
+    );
+
+    // FIXME(genmc): could make remaining accesses powers-of-2, instead of 1 byte.
+    let start_chunks = (start_address..start_address_aligned).map(|address| (address, 1));
+    let aligned_chunks = (start_address_aligned..end_address_aligned)
+        .step_by(MAX_ACCESS_SIZE.try_into().unwrap())
+        .map(|address| (address, MAX_ACCESS_SIZE));
+    let end_chunks = (end_address_aligned..end_address).map(|address| (address, 1));
+
+    start_chunks.chain(aligned_chunks).chain(end_chunks)
+}
+
+/// Inverse function to `scalar_to_genmc_scalar`.
+///
+/// Convert a Miri `Scalar` to a `GenmcScalar`.
+/// To be able to restore pointer provenance from a `GenmcScalar`, the base address of the allocation of the pointer is also stored in the `GenmcScalar`.
+/// We cannot use the `AllocId` instead of the base address, since Miri has no control over the `AllocId`, and it may change across executions.
+/// Pointers with `Wildcard` provenance are not supported.
+pub fn scalar_to_genmc_scalar<'tcx>(
+    _ecx: &MiriInterpCx<'tcx>,
+    scalar: Scalar,
+) -> InterpResult<'tcx, GenmcScalar> {
+    interp_ok(match scalar {
+        rustc_const_eval::interpret::Scalar::Int(scalar_int) => {
+            // FIXME(genmc): Add u128 support once GenMC supports it.
+            let value: u64 = scalar_int.to_uint(scalar_int.size()).try_into().unwrap();
+            GenmcScalar { value, is_init: true }
+        }
+        rustc_const_eval::interpret::Scalar::Ptr(_pointer, _size) =>
+            throw_unsup_format!(
+                "FIXME(genmc): Implement sending pointers (with provenance) to GenMC."
+            ),
+    })
+}
+
+/// Inverse function to `scalar_to_genmc_scalar`.
+///
+/// Convert a `GenmcScalar` back into a Miri `Scalar`.
+/// For pointers, attempt to convert the stored base address of their allocation back into an `AllocId`.
+pub fn genmc_scalar_to_scalar<'tcx>(
+    _ecx: &MiriInterpCx<'tcx>,
+    scalar: GenmcScalar,
+    size: Size,
+) -> InterpResult<'tcx, Scalar> {
+    // FIXME(genmc): Add GenmcScalar to Miri Pointer conversion.
+
+    // NOTE: GenMC always returns 64 bit values, and the upper bits are not yet truncated.
+    // FIXME(genmc): GenMC should be doing the truncation, not Miri.
+    let (value_scalar_int, _got_truncated) = ScalarInt::truncate_from_uint(scalar.value, size);
+    interp_ok(Scalar::Int(value_scalar_int))
+}
+
+impl AtomicReadOrd {
+    pub(super) fn to_genmc(self) -> MemOrdering {
+        match self {
+            AtomicReadOrd::Relaxed => MemOrdering::Relaxed,
+            AtomicReadOrd::Acquire => MemOrdering::Acquire,
+            AtomicReadOrd::SeqCst => MemOrdering::SequentiallyConsistent,
+        }
+    }
+}
+
+impl AtomicWriteOrd {
+    pub(super) fn to_genmc(self) -> MemOrdering {
+        match self {
+            AtomicWriteOrd::Relaxed => MemOrdering::Relaxed,
+            AtomicWriteOrd::Release => MemOrdering::Release,
+            AtomicWriteOrd::SeqCst => MemOrdering::SequentiallyConsistent,
+        }
+    }
+}
+
+impl AtomicFenceOrd {
+    pub(super) fn to_genmc(self) -> MemOrdering {
+        match self {
+            AtomicFenceOrd::Acquire => MemOrdering::Acquire,
+            AtomicFenceOrd::Release => MemOrdering::Release,
+            AtomicFenceOrd::AcqRel => MemOrdering::AcquireRelease,
+            AtomicFenceOrd::SeqCst => MemOrdering::SequentiallyConsistent,
+        }
+    }
+}
+
+/// Since GenMC ignores the failure memory ordering and Miri should not detect bugs that don't actually exist, we upgrade the success ordering if required.
+/// This means that Miri running in GenMC mode will not explore all possible executions allowed under the RC11 memory model.
+/// FIXME(genmc): remove this once GenMC properly supports the failure memory ordering.
+pub(super) fn maybe_upgrade_compare_exchange_success_orderings(
+    success: AtomicRwOrd,
+    failure: AtomicReadOrd,
+) -> AtomicRwOrd {
+    use AtomicReadOrd::*;
+    let (success_read, success_write) = success.split_memory_orderings();
+    let upgraded_success_read = match (success_read, failure) {
+        (_, SeqCst) | (SeqCst, _) => SeqCst,
+        (Acquire, _) | (_, Acquire) => Acquire,
+        (Relaxed, Relaxed) => Relaxed,
+    };
+    AtomicRwOrd::from_split_memory_orderings(upgraded_success_read, success_write)
+}
+
+impl AtomicRwOrd {
+    /// Split up an atomic read-write memory ordering into a separate read and write ordering.
+    pub(super) fn split_memory_orderings(self) -> (AtomicReadOrd, AtomicWriteOrd) {
+        match self {
+            AtomicRwOrd::Relaxed => (AtomicReadOrd::Relaxed, AtomicWriteOrd::Relaxed),
+            AtomicRwOrd::Acquire => (AtomicReadOrd::Acquire, AtomicWriteOrd::Relaxed),
+            AtomicRwOrd::Release => (AtomicReadOrd::Relaxed, AtomicWriteOrd::Release),
+            AtomicRwOrd::AcqRel => (AtomicReadOrd::Acquire, AtomicWriteOrd::Release),
+            AtomicRwOrd::SeqCst => (AtomicReadOrd::SeqCst, AtomicWriteOrd::SeqCst),
+        }
+    }
+
+    /// Split up an atomic read-write memory ordering into a separate read and write ordering.
+    fn from_split_memory_orderings(
+        read_ordering: AtomicReadOrd,
+        write_ordering: AtomicWriteOrd,
+    ) -> Self {
+        match (read_ordering, write_ordering) {
+            (AtomicReadOrd::Relaxed, AtomicWriteOrd::Relaxed) => AtomicRwOrd::Relaxed,
+            (AtomicReadOrd::Acquire, AtomicWriteOrd::Relaxed) => AtomicRwOrd::Acquire,
+            (AtomicReadOrd::Relaxed, AtomicWriteOrd::Release) => AtomicRwOrd::Release,
+            (AtomicReadOrd::Acquire, AtomicWriteOrd::Release) => AtomicRwOrd::AcqRel,
+            (AtomicReadOrd::SeqCst, AtomicWriteOrd::SeqCst) => AtomicRwOrd::SeqCst,
+            _ =>
+                panic!(
+                    "Unsupported memory ordering combination ({read_ordering:?}, {write_ordering:?})"
+                ),
+        }
+    }
+
+    pub(super) fn to_genmc(self) -> MemOrdering {
+        match self {
+            AtomicRwOrd::Relaxed => MemOrdering::Relaxed,
+            AtomicRwOrd::Acquire => MemOrdering::Acquire,
+            AtomicRwOrd::Release => MemOrdering::Release,
+            AtomicRwOrd::AcqRel => MemOrdering::AcquireRelease,
+            AtomicRwOrd::SeqCst => MemOrdering::SequentiallyConsistent,
+        }
+    }
+}
+
+/// Convert an atomic binary operation to its GenMC counterpart.
+pub(super) fn to_genmc_rmw_op(atomic_op: AtomicRmwOp, is_signed: bool) -> RMWBinOp {
+    match (atomic_op, is_signed) {
+        (AtomicRmwOp::Min, true) => RMWBinOp::Min,
+        (AtomicRmwOp::Max, true) => RMWBinOp::Max,
+        (AtomicRmwOp::Min, false) => RMWBinOp::UMin,
+        (AtomicRmwOp::Max, false) => RMWBinOp::UMax,
+        (AtomicRmwOp::MirOp { op, neg }, _is_signed) =>
+            match (op, neg) {
+                (mir::BinOp::Add, false) => RMWBinOp::Add,
+                (mir::BinOp::Sub, false) => RMWBinOp::Sub,
+                (mir::BinOp::BitXor, false) => RMWBinOp::Xor,
+                (mir::BinOp::BitAnd, false) => RMWBinOp::And,
+                (mir::BinOp::BitAnd, true) => RMWBinOp::Nand,
+                (mir::BinOp::BitOr, false) => RMWBinOp::Or,
+                _ => {
+                    panic!("unsupported atomic operation: bin_op: {op:?}, negate: {neg}");
+                }
+            },
+    }
+}
diff --git a/src/tools/miri/src/concurrency/genmc/mod.rs b/src/tools/miri/src/concurrency/genmc/mod.rs
index 3617775e27e..0086d3f2bf0 100644
--- a/src/tools/miri/src/concurrency/genmc/mod.rs
+++ b/src/tools/miri/src/concurrency/genmc/mod.rs
@@ -1,77 +1,188 @@
-#![allow(unused)] // FIXME(GenMC): remove this
+use std::cell::{Cell, RefCell};
+use std::sync::Arc;
 
-use std::cell::Cell;
-
-use genmc_sys::{GenmcParams, createGenmcHandle};
+use genmc_sys::{
+    EstimationResult, GENMC_GLOBAL_ADDRESSES_MASK, GenmcScalar, MemOrdering, MiriGenmcShim,
+    RMWBinOp, UniquePtr, create_genmc_driver_handle,
+};
 use rustc_abi::{Align, Size};
-use rustc_const_eval::interpret::{InterpCx, InterpResult, interp_ok};
-use rustc_middle::mir;
-
+use rustc_const_eval::interpret::{AllocId, InterpCx, InterpResult, interp_ok};
+use rustc_middle::{throw_machine_stop, throw_ub_format, throw_unsup_format};
+// FIXME(genmc,tracing): Implement some work-around for enabling debug/trace level logging (currently disabled statically in rustc).
+use tracing::{debug, info};
+
+use self::global_allocations::{EvalContextExt as _, GlobalAllocationHandler};
+use self::helper::{
+    MAX_ACCESS_SIZE, Warnings, emit_warning, genmc_scalar_to_scalar,
+    maybe_upgrade_compare_exchange_success_orderings, scalar_to_genmc_scalar, to_genmc_rmw_op,
+};
+use self::run::GenmcMode;
+use self::thread_id_map::ThreadIdMap;
+use crate::concurrency::genmc::helper::split_access;
+use crate::intrinsics::AtomicRmwOp;
 use crate::{
     AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, MemoryKind, MiriConfig,
-    MiriMachine, Scalar, ThreadId, ThreadManager, VisitProvenance, VisitWith,
+    MiriMachine, MiriMemoryKind, NonHaltingDiagnostic, Scalar, TerminationInfo, ThreadId,
+    ThreadManager, VisitProvenance, VisitWith,
 };
 
 mod config;
+mod global_allocations;
+mod helper;
+mod run;
+pub(crate) mod scheduling;
+mod thread_id_map;
+
+pub use genmc_sys::GenmcParams;
 
 pub use self::config::GenmcConfig;
+pub use self::run::run_genmc_mode;
+
+#[derive(Debug)]
+pub enum ExecutionEndResult {
+    /// An error occurred at the end of the execution.
+    Error(String),
+    /// No errors occurred, and there are more executions to explore.
+    Continue,
+    /// No errors occurred and we are finished.
+    Stop,
+}
 
-// FIXME(GenMC): add fields
-pub struct GenmcCtx {
-    /// Some actions Miri does are allowed to cause data races.
-    /// GenMC will not be informed about certain actions (e.g. non-atomic loads) when this flag is set.
-    allow_data_races: Cell<bool>,
+#[derive(Clone, Copy, Debug)]
+pub enum ExitType {
+    MainThreadFinish,
+    ExitCalled,
 }
 
-impl GenmcCtx {
-    /// Create a new `GenmcCtx` from a given config.
-    pub fn new(miri_config: &MiriConfig) -> Self {
-        let genmc_config = miri_config.genmc_config.as_ref().unwrap();
+/// The exit status of a program.
+/// GenMC must store this if a thread exits while any others can still run.
+/// The other threads must also be explored before the program is terminated.
+#[derive(Clone, Copy, Debug)]
+struct ExitStatus {
+    exit_code: i32,
+    exit_type: ExitType,
+}
 
-        let handle = createGenmcHandle(&genmc_config.params);
-        assert!(!handle.is_null());
+impl ExitStatus {
+    fn do_leak_check(self) -> bool {
+        matches!(self.exit_type, ExitType::MainThreadFinish)
+    }
+}
 
-        eprintln!("Miri: GenMC handle creation successful!");
+/// State that is reset at the start of every execution.
+#[derive(Debug, Default)]
+struct PerExecutionState {
+    /// Thread id management, such as mapping between Miri `ThreadId` and GenMC's thread ids, or selecting GenMC thread ids.
+    thread_id_manager: RefCell<ThreadIdMap>,
 
-        drop(handle);
-        eprintln!("Miri: Dropping GenMC handle successful!");
+    /// A flag to indicate that we should not forward non-atomic accesses to genmc, e.g. because we
+    /// are executing an atomic operation.
+    allow_data_races: Cell<bool>,
 
-        // FIXME(GenMC): implement
-        std::process::exit(0);
+    /// The exit status of the program. We keep running other threads even after `exit` to ensure
+    /// we cover all possible executions.
+    /// `None` if no thread has called `exit` and the main thread isn't finished yet.
+    exit_status: Cell<Option<ExitStatus>>,
+}
+
+impl PerExecutionState {
+    fn reset(&self) {
+        self.allow_data_races.replace(false);
+        self.thread_id_manager.borrow_mut().reset();
+        self.exit_status.set(None);
     }
+}
 
-    pub fn get_stuck_execution_count(&self) -> usize {
-        todo!()
+struct GlobalState {
+    /// Keep track of global allocations, to ensure they keep the same address across different executions, even if the order of allocations changes.
+    /// The `AllocId` for globals is stable across executions, so we can use it as an identifier.
+    global_allocations: GlobalAllocationHandler,
+
+    /// Cache for which warnings have already been shown to the user.
+    /// `None` if warnings are disabled.
+    warning_cache: Option<Warnings>,
+}
+
+impl GlobalState {
+    fn new(target_usize_max: u64, print_warnings: bool) -> Self {
+        Self {
+            global_allocations: GlobalAllocationHandler::new(target_usize_max),
+            warning_cache: print_warnings.then(Default::default),
+        }
     }
+}
 
-    pub fn print_genmc_graph(&self) {
-        todo!()
+/// The main interface with GenMC.
+/// Each `GenmcCtx` owns one `MiriGenmcShim`, which owns one `GenMCDriver` (the GenMC model checker).
+/// For each GenMC run (estimation or verification), one or more `GenmcCtx` can be created (one per Miri thread).
+/// However, for now, we only ever have one `GenmcCtx` per run.
+///
+/// In multithreading, each worker thread has its own `GenmcCtx`, which will have their results combined in the end.
+/// FIXME(genmc): implement multithreading.
+///
+/// Some data is shared across all `GenmcCtx` in the same run, namely data for global allocation handling.
+/// Globals must be allocated in a consistent manner, i.e., each global allocation must have the same address in each execution.
+///
+/// Some state is reset between each execution in the same run.
+pub struct GenmcCtx {
+    /// Handle to the GenMC model checker.
+    handle: RefCell<UniquePtr<MiriGenmcShim>>,
+
+    /// State that is reset at the start of every execution.
+    exec_state: PerExecutionState,
+
+    /// State that persists across executions.
+    /// All `GenmcCtx` in one verification step share this state.
+    global_state: Arc<GlobalState>,
+}
+
+/// GenMC Context creation and administrative / query actions
+impl GenmcCtx {
+    /// Create a new `GenmcCtx` from a given config.
+    fn new(miri_config: &MiriConfig, global_state: Arc<GlobalState>, mode: GenmcMode) -> Self {
+        let genmc_config = miri_config.genmc_config.as_ref().unwrap();
+        let handle = RefCell::new(create_genmc_driver_handle(
+            &genmc_config.params,
+            genmc_config.log_level,
+            /* do_estimation: */ mode == GenmcMode::Estimation,
+        ));
+        Self { handle, exec_state: Default::default(), global_state }
     }
 
-    /// This function determines if we should continue exploring executions or if we are done.
-    ///
-    /// In GenMC mode, the input program should be repeatedly executed until this function returns `true` or an error is found.
-    pub fn is_exploration_done(&self) -> bool {
-        todo!()
+    fn get_estimation_results(&self) -> EstimationResult {
+        self.handle.borrow().get_estimation_results()
     }
 
-    /// Inform GenMC that a new program execution has started.
-    /// This function should be called at the start of every execution.
-    pub(crate) fn handle_execution_start(&self) {
-        todo!()
+    /// Get the number of blocked executions encountered by GenMC.
+    fn get_blocked_execution_count(&self) -> u64 {
+        self.handle.borrow().get_blocked_execution_count()
     }
 
-    /// Inform GenMC that the program's execution has ended.
-    ///
-    /// This function must be called even when the execution got stuck (i.e., it returned a `InterpErrorKind::MachineStop` with error kind `TerminationInfo::GenmcStuckExecution`).
-    pub(crate) fn handle_execution_end<'tcx>(
-        &self,
-        ecx: &InterpCx<'tcx, MiriMachine<'tcx>>,
-    ) -> Result<(), String> {
-        todo!()
+    /// Get the number of explored executions encountered by GenMC.
+    fn get_explored_execution_count(&self) -> u64 {
+        self.handle.borrow().get_explored_execution_count()
     }
 
-    /**** Memory access handling ****/
+    /// Check if GenMC encountered an error that wasn't immediately returned during execution.
+    /// Returns a string representation of the error if one occurred.
+    fn try_get_error(&self) -> Option<String> {
+        self.handle
+            .borrow()
+            .get_error_string()
+            .as_ref()
+            .map(|error| error.to_string_lossy().to_string())
+    }
+
+    /// Check if GenMC encountered an error that wasn't immediately returned during execution.
+    /// Returns a string representation of the error if one occurred.
+    fn get_result_message(&self) -> String {
+        self.handle
+            .borrow()
+            .get_result_message()
+            .as_ref()
+            .map(|error| error.to_string_lossy().to_string())
+            .expect("there should always be a message")
+    }
 
     /// Select whether data race free actions should be allowed. This function should be used carefully!
     ///
@@ -83,10 +194,68 @@ impl GenmcCtx {
     /// # Panics
     /// If data race free is attempted to be set more than once (i.e., no nesting allowed).
     pub(super) fn set_ongoing_action_data_race_free(&self, enable: bool) {
-        let old = self.allow_data_races.replace(enable);
+        debug!("GenMC: set_ongoing_action_data_race_free ({enable})");
+        let old = self.exec_state.allow_data_races.replace(enable);
         assert_ne!(old, enable, "cannot nest allow_data_races");
     }
 
+    /// Check whether data races are currently allowed (e.g., for loading values for validation which are not actually loaded by the program).
+    fn get_alloc_data_races(&self) -> bool {
+        self.exec_state.allow_data_races.get()
+    }
+}
+
+/// GenMC event handling. These methods are used to inform GenMC about events happening in the program, and to handle scheduling decisions.
+impl GenmcCtx {
+    /// Prepare for the next execution and inform GenMC about it.
+    /// Must be called before at the start of every execution.
+    fn prepare_next_execution(&self) {
+        // Reset per-execution state.
+        self.exec_state.reset();
+        // Inform GenMC about the new execution.
+        self.handle.borrow_mut().pin_mut().handle_execution_start();
+    }
+
+    /// Inform GenMC that the program's execution has ended.
+    ///
+    /// This function must be called even when the execution is blocked
+    /// (i.e., it returned a `InterpErrorKind::MachineStop` with error kind `TerminationInfo::GenmcBlockedExecution`).
+    /// Don't call this function if an error was found.
+    ///
+    /// GenMC detects certain errors only when the execution ends.
+    /// If an error occured, a string containing a short error description is returned.
+    ///
+    /// GenMC currently doesn't return an error in all cases immediately when one happens.
+    /// This function will also check for those, and return their error description.
+    ///
+    /// To get the all messages (warnings, errors) that GenMC produces, use the `get_result_message` method.
+    fn handle_execution_end(&self) -> ExecutionEndResult {
+        let result = self.handle.borrow_mut().pin_mut().handle_execution_end();
+        if let Some(error) = result.as_ref() {
+            return ExecutionEndResult::Error(error.to_string_lossy().to_string());
+        }
+
+        // GenMC decides if there is more to explore:
+        let exploration_done = self.handle.borrow_mut().pin_mut().is_exploration_done();
+
+        // GenMC currently does not return an error value immediately in all cases.
+        // Both `handle_execution_end` and `is_exploration_done` can produce such errors.
+        // We manually query for any errors here to ensure we don't miss any.
+        if let Some(error) = self.try_get_error() {
+            ExecutionEndResult::Error(error)
+        } else if exploration_done {
+            ExecutionEndResult::Stop
+        } else {
+            ExecutionEndResult::Continue
+        }
+    }
+
+    /**** Memory access handling ****/
+
+    /// Inform GenMC about an atomic load.
+    /// Returns that value that the load should read.
+    ///
+    /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitalized.
     pub(crate) fn atomic_load<'tcx>(
         &self,
         ecx: &InterpCx<'tcx, MiriMachine<'tcx>>,
@@ -95,64 +264,93 @@ impl GenmcCtx {
         ordering: AtomicReadOrd,
         old_val: Option<Scalar>,
     ) -> InterpResult<'tcx, Scalar> {
-        assert!(!self.allow_data_races.get());
-        todo!()
+        assert!(!self.get_alloc_data_races(), "atomic load with data race checking disabled.");
+        let genmc_old_value = if let Some(scalar) = old_val {
+            scalar_to_genmc_scalar(ecx, scalar)?
+        } else {
+            GenmcScalar::UNINIT
+        };
+        let read_value =
+            self.handle_load(&ecx.machine, address, size, ordering.to_genmc(), genmc_old_value)?;
+        genmc_scalar_to_scalar(ecx, read_value, size)
     }
 
+    /// Inform GenMC about an atomic store.
+    /// Returns `true` if the stored value should be reflected in Miri's memory.
+    ///
+    /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitalized.
     pub(crate) fn atomic_store<'tcx>(
         &self,
         ecx: &InterpCx<'tcx, MiriMachine<'tcx>>,
         address: Size,
         size: Size,
         value: Scalar,
+        old_value: Option<Scalar>,
         ordering: AtomicWriteOrd,
-    ) -> InterpResult<'tcx, ()> {
-        assert!(!self.allow_data_races.get());
-        todo!()
+    ) -> InterpResult<'tcx, bool> {
+        assert!(!self.get_alloc_data_races(), "atomic store with data race checking disabled.");
+        let genmc_value = scalar_to_genmc_scalar(ecx, value)?;
+        let genmc_old_value = if let Some(scalar) = old_value {
+            scalar_to_genmc_scalar(ecx, scalar)?
+        } else {
+            GenmcScalar::UNINIT
+        };
+        self.handle_store(
+            &ecx.machine,
+            address,
+            size,
+            genmc_value,
+            genmc_old_value,
+            ordering.to_genmc(),
+        )
     }
 
+    /// Inform GenMC about an atomic fence.
     pub(crate) fn atomic_fence<'tcx>(
         &self,
         machine: &MiriMachine<'tcx>,
         ordering: AtomicFenceOrd,
-    ) -> InterpResult<'tcx, ()> {
-        assert!(!self.allow_data_races.get());
-        todo!()
+    ) -> InterpResult<'tcx> {
+        assert!(!self.get_alloc_data_races(), "atomic fence with data race checking disabled.");
+
+        let thread_infos = self.exec_state.thread_id_manager.borrow();
+        let curr_thread = machine.threads.active_thread();
+        let genmc_tid = thread_infos.get_genmc_tid(curr_thread);
+
+        self.handle.borrow_mut().pin_mut().handle_fence(genmc_tid, ordering.to_genmc());
+        interp_ok(())
     }
 
     /// Inform GenMC about an atomic read-modify-write operation.
     ///
-    /// Returns `(old_val, new_val)`.
+    /// Returns `(old_val, Option<new_val>)`. `new_val` might not be the latest write in coherence order, which is indicated by `None`.
+    ///
+    /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitalized.
     pub(crate) fn atomic_rmw_op<'tcx>(
         &self,
         ecx: &InterpCx<'tcx, MiriMachine<'tcx>>,
         address: Size,
         size: Size,
+        atomic_op: AtomicRmwOp,
+        is_signed: bool,
         ordering: AtomicRwOrd,
-        (rmw_op, not): (mir::BinOp, bool),
         rhs_scalar: Scalar,
-    ) -> InterpResult<'tcx, (Scalar, Scalar)> {
-        assert!(!self.allow_data_races.get());
-        todo!()
+        old_value: Scalar,
+    ) -> InterpResult<'tcx, (Scalar, Option<Scalar>)> {
+        self.handle_atomic_rmw_op(
+            ecx,
+            address,
+            size,
+            ordering,
+            to_genmc_rmw_op(atomic_op, is_signed),
+            scalar_to_genmc_scalar(ecx, rhs_scalar)?,
+            scalar_to_genmc_scalar(ecx, old_value)?,
+        )
     }
 
-    /// Inform GenMC about an atomic `min` or `max` operation.
+    /// Returns `(old_val, Option<new_val>)`. `new_val` might not be the latest write in coherence order, which is indicated by `None`.
     ///
-    /// Returns `(old_val, new_val)`.
-    pub(crate) fn atomic_min_max_op<'tcx>(
-        &self,
-        ecx: &InterpCx<'tcx, MiriMachine<'tcx>>,
-        address: Size,
-        size: Size,
-        ordering: AtomicRwOrd,
-        min: bool,
-        is_signed: bool,
-        rhs_scalar: Scalar,
-    ) -> InterpResult<'tcx, (Scalar, Scalar)> {
-        assert!(!self.allow_data_races.get());
-        todo!()
-    }
-
+    /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitalized.
     pub(crate) fn atomic_exchange<'tcx>(
         &self,
         ecx: &InterpCx<'tcx, MiriMachine<'tcx>>,
@@ -160,11 +358,24 @@ impl GenmcCtx {
         size: Size,
         rhs_scalar: Scalar,
         ordering: AtomicRwOrd,
-    ) -> InterpResult<'tcx, (Scalar, bool)> {
-        assert!(!self.allow_data_races.get());
-        todo!()
+        old_value: Scalar,
+    ) -> InterpResult<'tcx, (Scalar, Option<Scalar>)> {
+        self.handle_atomic_rmw_op(
+            ecx,
+            address,
+            size,
+            ordering,
+            /* genmc_rmw_op */ RMWBinOp::Xchg,
+            scalar_to_genmc_scalar(ecx, rhs_scalar)?,
+            scalar_to_genmc_scalar(ecx, old_value)?,
+        )
     }
 
+    /// Inform GenMC about an atomic compare-exchange operation.
+    ///
+    /// Returns the old value read by the compare exchange, optionally the value that Miri should write back to its memory, and whether the compare-exchange was a success or not.
+    ///
+    /// `old_value` is the value that a non-atomic load would read here, or `None` if the memory is uninitalized.
     pub(crate) fn atomic_compare_exchange<'tcx>(
         &self,
         ecx: &InterpCx<'tcx, MiriMachine<'tcx>>,
@@ -175,9 +386,83 @@ impl GenmcCtx {
         success: AtomicRwOrd,
         fail: AtomicReadOrd,
         can_fail_spuriously: bool,
-    ) -> InterpResult<'tcx, (Scalar, bool)> {
-        assert!(!self.allow_data_races.get());
-        todo!()
+        old_value: Scalar,
+    ) -> InterpResult<'tcx, (Scalar, Option<Scalar>, bool)> {
+        assert!(
+            !self.get_alloc_data_races(),
+            "atomic compare-exchange with data race checking disabled."
+        );
+        assert_ne!(0, size.bytes());
+        assert!(
+            size.bytes() <= MAX_ACCESS_SIZE,
+            "GenMC currently does not support atomic accesses larger than {} bytes (got {} bytes)",
+            MAX_ACCESS_SIZE,
+            size.bytes()
+        );
+
+        // Upgrade the success memory ordering to equal the failure ordering, since GenMC currently ignores the failure ordering.
+        // FIXME(genmc): remove this once GenMC properly supports the failure memory ordering.
+        let upgraded_success_ordering =
+            maybe_upgrade_compare_exchange_success_orderings(success, fail);
+
+        if let Some(warning_cache) = &self.global_state.warning_cache {
+            // FIXME(genmc): remove once GenMC supports failure memory ordering in `compare_exchange`.
+            let (effective_failure_ordering, _) =
+                upgraded_success_ordering.split_memory_orderings();
+            // Return a warning if the actual orderings don't match the upgraded ones.
+            if success != upgraded_success_ordering || effective_failure_ordering != fail {
+                emit_warning(ecx, &warning_cache.compare_exchange_failure_ordering, || {
+                    NonHaltingDiagnostic::GenmcCompareExchangeOrderingMismatch {
+                        success_ordering: success,
+                        upgraded_success_ordering,
+                        failure_ordering: fail,
+                        effective_failure_ordering,
+                    }
+                });
+            }
+            // FIXME(genmc): remove once GenMC implements spurious failures for `compare_exchange_weak`.
+            if can_fail_spuriously {
+                emit_warning(ecx, &warning_cache.compare_exchange_weak, || {
+                    NonHaltingDiagnostic::GenmcCompareExchangeWeak
+                });
+            }
+        }
+
+        debug!(
+            "GenMC: atomic_compare_exchange, address: {address:?}, size: {size:?} (expect: {expected_old_value:?}, new: {new_value:?}, old_value: {old_value:?}, {success:?}, orderings: {fail:?}), can fail spuriously: {can_fail_spuriously}"
+        );
+
+        let thread_infos = self.exec_state.thread_id_manager.borrow();
+        let genmc_tid = thread_infos.get_genmc_tid(ecx.machine.threads.active_thread());
+
+        let cas_result = self.handle.borrow_mut().pin_mut().handle_compare_exchange(
+            genmc_tid,
+            address.bytes(),
+            size.bytes(),
+            scalar_to_genmc_scalar(ecx, expected_old_value)?,
+            scalar_to_genmc_scalar(ecx, new_value)?,
+            scalar_to_genmc_scalar(ecx, old_value)?,
+            upgraded_success_ordering.to_genmc(),
+            fail.to_genmc(),
+            can_fail_spuriously,
+        );
+
+        if let Some(error) = cas_result.error.as_ref() {
+            // FIXME(genmc): error handling
+            throw_ub_format!("{}", error.to_string_lossy());
+        }
+
+        let return_scalar = genmc_scalar_to_scalar(ecx, cas_result.old_value, size)?;
+        debug!(
+            "GenMC: atomic_compare_exchange: result: {cas_result:?}, returning scalar: {return_scalar:?}"
+        );
+        // The write can only be a co-maximal write if the CAS succeeded.
+        assert!(cas_result.is_success || !cas_result.is_coherence_order_maximal_write);
+        interp_ok((
+            return_scalar,
+            cas_result.is_coherence_order_maximal_write.then_some(new_value),
+            cas_result.is_success,
+        ))
     }
 
     /// Inform GenMC about a non-atomic memory load
@@ -188,40 +473,178 @@ impl GenmcCtx {
         machine: &MiriMachine<'tcx>,
         address: Size,
         size: Size,
-    ) -> InterpResult<'tcx, ()> {
-        todo!()
+    ) -> InterpResult<'tcx> {
+        debug!(
+            "GenMC: received memory_load (non-atomic): address: {:#x}, size: {}",
+            address.bytes(),
+            size.bytes()
+        );
+        if self.get_alloc_data_races() {
+            debug!("GenMC: data race checking disabled, ignoring non-atomic load.");
+            return interp_ok(());
+        }
+        // GenMC doesn't like ZSTs, and they can't have any data races, so we skip them
+        if size.bytes() == 0 {
+            return interp_ok(());
+        }
+
+        let handle_load = |address, size| {
+            // NOTE: Values loaded non-atomically are still handled by Miri, so we discard whatever we get from GenMC
+            let _read_value = self.handle_load(
+                machine,
+                address,
+                size,
+                MemOrdering::NotAtomic,
+                // This value is used to update the co-maximal store event to the same location.
+                // We don't need to update that store, since if it is ever read by any atomic loads, the value will be updated then.
+                // We use uninit for lack of a better value, since we don't know whether the location we currently load from is initialized or not.
+                GenmcScalar::UNINIT,
+            )?;
+            interp_ok(())
+        };
+
+        // This load is small enough so GenMC can handle it.
+        if size.bytes() <= MAX_ACCESS_SIZE {
+            return handle_load(address, size);
+        }
+
+        // This load is too big to be a single GenMC access, we have to split it.
+        // FIXME(genmc): This will misbehave if there are non-64bit-atomics in there.
+        // Needs proper support on the GenMC side for large and mixed atomic accesses.
+        for (address, size) in split_access(address, size) {
+            handle_load(Size::from_bytes(address), Size::from_bytes(size))?;
+        }
+        interp_ok(())
     }
 
+    /// Inform GenMC about a non-atomic memory store
+    ///
+    /// NOTE: Unlike for *atomic* stores, we don't provide the actual stored values to GenMC here.
     pub(crate) fn memory_store<'tcx>(
         &self,
         machine: &MiriMachine<'tcx>,
         address: Size,
         size: Size,
-    ) -> InterpResult<'tcx, ()> {
-        todo!()
+    ) -> InterpResult<'tcx> {
+        debug!(
+            "GenMC: received memory_store (non-atomic): address: {:#x}, size: {}",
+            address.bytes(),
+            size.bytes()
+        );
+        if self.get_alloc_data_races() {
+            debug!("GenMC: data race checking disabled, ignoring non-atomic store.");
+            return interp_ok(());
+        }
+        // GenMC doesn't like ZSTs, and they can't have any data races, so we skip them
+        if size.bytes() == 0 {
+            return interp_ok(());
+        }
+
+        let handle_store = |address, size| {
+            // We always write the the stored values to Miri's memory, whether GenMC says the write is co-maximal or not.
+            // The GenMC scheduler ensures that replaying an execution happens in porf-respecting order (po := program order, rf: reads-from order).
+            // This means that for any non-atomic read Miri performs, the corresponding write has already been replayed.
+            let _is_co_max_write = self.handle_store(
+                machine,
+                address,
+                size,
+                // We don't know the value that this store will write, but GenMC expects that we give it an actual value.
+                // Unfortunately, there are situations where this value can actually become visible
+                // to the program: when there is an atomic load reading from a non-atomic store.
+                // FIXME(genmc): update once mixed atomic-non-atomic support is added. Afterwards, this value should never be readable.
+                GenmcScalar::from_u64(0xDEADBEEF),
+                // This value is used to update the co-maximal store event to the same location.
+                // This old value cannot be read anymore by any future loads, since we are doing another non-atomic store to the same location.
+                // Any future load will either see the store we are adding now, or we have a data race (there can only be one possible non-atomic value to read from at any time).
+                // We use uninit for lack of a better value, since we don't know whether the location we currently write to is initialized or not.
+                GenmcScalar::UNINIT,
+                MemOrdering::NotAtomic,
+            )?;
+            interp_ok(())
+        };
+
+        // This store is small enough so GenMC can handle it.
+        if size.bytes() <= MAX_ACCESS_SIZE {
+            return handle_store(address, size);
+        }
+
+        // This store is too big to be a single GenMC access, we have to split it.
+        // FIXME(genmc): This will misbehave if there are non-64bit-atomics in there.
+        // Needs proper support on the GenMC side for large and mixed atomic accesses.
+        for (address, size) in split_access(address, size) {
+            handle_store(Size::from_bytes(address), Size::from_bytes(size))?;
+        }
+        interp_ok(())
     }
 
     /**** Memory (de)allocation ****/
 
+    /// This is also responsible for determining the address of the new allocation.
     pub(crate) fn handle_alloc<'tcx>(
         &self,
-        machine: &MiriMachine<'tcx>,
+        ecx: &InterpCx<'tcx, MiriMachine<'tcx>>,
+        alloc_id: AllocId,
         size: Size,
         alignment: Align,
         memory_kind: MemoryKind,
     ) -> InterpResult<'tcx, u64> {
-        todo!()
+        assert!(
+            !self.get_alloc_data_races(),
+            "memory allocation with data race checking disabled."
+        );
+        let machine = &ecx.machine;
+        if memory_kind == MiriMemoryKind::Global.into() {
+            return ecx
+                .get_global_allocation_address(&self.global_state.global_allocations, alloc_id);
+        }
+        let thread_infos = self.exec_state.thread_id_manager.borrow();
+        let curr_thread = machine.threads.active_thread();
+        let genmc_tid = thread_infos.get_genmc_tid(curr_thread);
+        // GenMC doesn't support ZSTs, so we set the minimum size to 1 byte
+        let genmc_size = size.bytes().max(1);
+
+        let chosen_address = self.handle.borrow_mut().pin_mut().handle_malloc(
+            genmc_tid,
+            genmc_size,
+            alignment.bytes(),
+        );
+
+        // Non-global addresses should not be in the global address space or null.
+        assert_ne!(0, chosen_address, "GenMC malloc returned nullptr.");
+        assert_eq!(0, chosen_address & GENMC_GLOBAL_ADDRESSES_MASK);
+        // Sanity check the address alignment:
+        assert!(
+            chosen_address.is_multiple_of(alignment.bytes()),
+            "GenMC returned address {chosen_address:#x} with lower alignment than requested ({}).",
+            alignment.bytes()
+        );
+
+        interp_ok(chosen_address)
     }
 
     pub(crate) fn handle_dealloc<'tcx>(
         &self,
         machine: &MiriMachine<'tcx>,
+        alloc_id: AllocId,
         address: Size,
-        size: Size,
-        align: Align,
         kind: MemoryKind,
-    ) -> InterpResult<'tcx, ()> {
-        todo!()
+    ) -> InterpResult<'tcx> {
+        assert_ne!(
+            kind,
+            MiriMemoryKind::Global.into(),
+            "we probably shouldn't try to deallocate global allocations (alloc_id: {alloc_id:?})"
+        );
+        assert!(
+            !self.get_alloc_data_races(),
+            "memory deallocation with data race checking disabled."
+        );
+        let thread_infos = self.exec_state.thread_id_manager.borrow();
+        let curr_thread = machine.threads.active_thread();
+        let genmc_tid = thread_infos.get_genmc_tid(curr_thread);
+
+        self.handle.borrow_mut().pin_mut().handle_free(genmc_tid, address.bytes());
+
+        interp_ok(())
     }
 
     /**** Thread management ****/
@@ -229,50 +652,90 @@ impl GenmcCtx {
     pub(crate) fn handle_thread_create<'tcx>(
         &self,
         threads: &ThreadManager<'tcx>,
+        // FIXME(genmc,symmetry reduction): pass info to GenMC
+        _start_routine: crate::Pointer,
+        _func_arg: &crate::ImmTy<'tcx>,
         new_thread_id: ThreadId,
-    ) -> InterpResult<'tcx, ()> {
-        assert!(!self.allow_data_races.get());
-        todo!()
+    ) -> InterpResult<'tcx> {
+        assert!(!self.get_alloc_data_races(), "thread creation with data race checking disabled.");
+        let mut thread_infos = self.exec_state.thread_id_manager.borrow_mut();
+
+        let curr_thread_id = threads.active_thread();
+        let genmc_parent_tid = thread_infos.get_genmc_tid(curr_thread_id);
+        let genmc_new_tid = thread_infos.add_thread(new_thread_id);
+
+        self.handle.borrow_mut().pin_mut().handle_thread_create(genmc_new_tid, genmc_parent_tid);
+        interp_ok(())
     }
 
     pub(crate) fn handle_thread_join<'tcx>(
         &self,
         active_thread_id: ThreadId,
         child_thread_id: ThreadId,
-    ) -> InterpResult<'tcx, ()> {
-        assert!(!self.allow_data_races.get());
-        todo!()
-    }
+    ) -> InterpResult<'tcx> {
+        assert!(!self.get_alloc_data_races(), "thread join with data race checking disabled.");
+        let thread_infos = self.exec_state.thread_id_manager.borrow();
 
-    pub(crate) fn handle_thread_stack_empty(&self, thread_id: ThreadId) {
-        todo!()
-    }
+        let genmc_curr_tid = thread_infos.get_genmc_tid(active_thread_id);
+        let genmc_child_tid = thread_infos.get_genmc_tid(child_thread_id);
 
-    pub(crate) fn handle_thread_finish<'tcx>(
-        &self,
-        threads: &ThreadManager<'tcx>,
-    ) -> InterpResult<'tcx, ()> {
-        assert!(!self.allow_data_races.get());
-        todo!()
+        self.handle.borrow_mut().pin_mut().handle_thread_join(genmc_curr_tid, genmc_child_tid);
+
+        interp_ok(())
     }
 
-    /**** Scheduling functionality ****/
+    pub(crate) fn handle_thread_finish<'tcx>(&self, threads: &ThreadManager<'tcx>) {
+        assert!(!self.get_alloc_data_races(), "thread finish with data race checking disabled.");
+        let curr_thread_id = threads.active_thread();
 
-    /// Ask for a scheduling decision. This should be called before every MIR instruction.
-    ///
-    /// GenMC may realize that the execution got stuck, then this function will return a `InterpErrorKind::MachineStop` with error kind `TerminationInfo::GenmcStuckExecution`).
-    ///
-    /// This is **not** an error by iself! Treat this as if the program ended normally: `handle_execution_end` should be called next, which will determine if were are any actual errors.
-    pub(crate) fn schedule_thread<'tcx>(
+        let thread_infos = self.exec_state.thread_id_manager.borrow();
+        let genmc_tid = thread_infos.get_genmc_tid(curr_thread_id);
+
+        debug!("GenMC: thread {curr_thread_id:?} ({genmc_tid:?}) finished.");
+        // NOTE: Miri doesn't support return values for threads, but GenMC expects one, so we return 0
+        self.handle.borrow_mut().pin_mut().handle_thread_finish(genmc_tid, /* ret_val */ 0);
+    }
+
+    /// Handle a call to `libc::exit` or the exit of the main thread.
+    /// Unless an error is returned, the program should continue executing (in a different thread, chosen by the next scheduling call).
+    pub(crate) fn handle_exit<'tcx>(
         &self,
-        ecx: &InterpCx<'tcx, MiriMachine<'tcx>>,
-    ) -> InterpResult<'tcx, ThreadId> {
-        assert!(!self.allow_data_races.get());
-        todo!()
+        thread: ThreadId,
+        exit_code: i32,
+        exit_type: ExitType,
+    ) -> InterpResult<'tcx> {
+        // Calling `libc::exit` doesn't do cleanup, so we skip the leak check in that case.
+        let exit_status = ExitStatus { exit_code, exit_type };
+
+        if let Some(old_exit_status) = self.exec_state.exit_status.get() {
+            throw_ub_format!(
+                "`exit` called twice, first with status {old_exit_status:?}, now with status {exit_status:?}",
+            );
+        }
+
+        // FIXME(genmc): Add a flag to continue exploration even when the program exits with a non-zero exit code.
+        if exit_code != 0 {
+            info!("GenMC: 'exit' called with non-zero argument, aborting execution.");
+            let leak_check = exit_status.do_leak_check();
+            throw_machine_stop!(TerminationInfo::Exit { code: exit_code, leak_check });
+        }
+
+        if matches!(exit_type, ExitType::ExitCalled) {
+            let thread_infos = self.exec_state.thread_id_manager.borrow();
+            let genmc_tid = thread_infos.get_genmc_tid(thread);
+
+            self.handle.borrow_mut().pin_mut().handle_thread_kill(genmc_tid);
+        } else {
+            assert_eq!(thread, ThreadId::MAIN_THREAD);
+        }
+        // We continue executing now, so we store the exit status.
+        self.exec_state.exit_status.set(Some(exit_status));
+        interp_ok(())
     }
 
     /**** Blocking instructions ****/
 
+    #[allow(unused)]
     pub(crate) fn handle_verifier_assume<'tcx>(
         &self,
         machine: &MiriMachine<'tcx>,
@@ -282,14 +745,171 @@ impl GenmcCtx {
     }
 }
 
-impl VisitProvenance for GenmcCtx {
-    fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
-        // We don't have any tags.
+impl GenmcCtx {
+    /// Inform GenMC about a load (atomic or non-atomic).
+    /// Returns the value that GenMC wants this load to read.
+    fn handle_load<'tcx>(
+        &self,
+        machine: &MiriMachine<'tcx>,
+        address: Size,
+        size: Size,
+        memory_ordering: MemOrdering,
+        genmc_old_value: GenmcScalar,
+    ) -> InterpResult<'tcx, GenmcScalar> {
+        assert!(
+            size.bytes() != 0
+                && (memory_ordering == MemOrdering::NotAtomic || size.bytes().is_power_of_two())
+        );
+        if size.bytes() > MAX_ACCESS_SIZE {
+            throw_unsup_format!(
+                "GenMC mode currently does not support atomics larger than {MAX_ACCESS_SIZE} bytes.",
+            );
+        }
+        let thread_infos = self.exec_state.thread_id_manager.borrow();
+        let curr_thread_id = machine.threads.active_thread();
+        let genmc_tid = thread_infos.get_genmc_tid(curr_thread_id);
+
+        debug!(
+            "GenMC: load, thread: {curr_thread_id:?} ({genmc_tid:?}), address: {addr} == {addr:#x}, size: {size:?}, ordering: {memory_ordering:?}, old_value: {genmc_old_value:x?}",
+            addr = address.bytes()
+        );
+
+        let load_result = self.handle.borrow_mut().pin_mut().handle_load(
+            genmc_tid,
+            address.bytes(),
+            size.bytes(),
+            memory_ordering,
+            genmc_old_value,
+        );
+
+        if let Some(error) = load_result.error.as_ref() {
+            // FIXME(genmc): error handling
+            throw_ub_format!("{}", error.to_string_lossy());
+        }
+
+        if !load_result.has_value {
+            // FIXME(GenMC): Implementing certain GenMC optimizations will lead to this.
+            unimplemented!("GenMC: load returned no value.");
+        }
+
+        debug!("GenMC: load returned value: {:?}", load_result.read_value);
+        interp_ok(load_result.read_value)
     }
-}
 
-impl GenmcCtx {
-    fn handle_user_block<'tcx>(&self, machine: &MiriMachine<'tcx>) -> InterpResult<'tcx, ()> {
+    /// Inform GenMC about a store (atomic or non-atomic).
+    /// Returns true if the store is co-maximal, i.e., it should be written to Miri's memory too.
+    fn handle_store<'tcx>(
+        &self,
+        machine: &MiriMachine<'tcx>,
+        address: Size,
+        size: Size,
+        genmc_value: GenmcScalar,
+        genmc_old_value: GenmcScalar,
+        memory_ordering: MemOrdering,
+    ) -> InterpResult<'tcx, bool> {
+        assert!(
+            size.bytes() != 0
+                && (memory_ordering == MemOrdering::NotAtomic || size.bytes().is_power_of_two())
+        );
+        if size.bytes() > MAX_ACCESS_SIZE {
+            throw_unsup_format!(
+                "GenMC mode currently does not support atomics larger than {MAX_ACCESS_SIZE} bytes."
+            );
+        }
+        let thread_infos = self.exec_state.thread_id_manager.borrow();
+        let curr_thread_id = machine.threads.active_thread();
+        let genmc_tid = thread_infos.get_genmc_tid(curr_thread_id);
+
+        debug!(
+            "GenMC: store, thread: {curr_thread_id:?} ({genmc_tid:?}), address: {addr} = {addr:#x}, size: {size:?}, ordering {memory_ordering:?}, value: {genmc_value:?}",
+            addr = address.bytes()
+        );
+
+        let store_result = self.handle.borrow_mut().pin_mut().handle_store(
+            genmc_tid,
+            address.bytes(),
+            size.bytes(),
+            genmc_value,
+            genmc_old_value,
+            memory_ordering,
+        );
+
+        if let Some(error) = store_result.error.as_ref() {
+            // FIXME(genmc): error handling
+            throw_ub_format!("{}", error.to_string_lossy());
+        }
+
+        interp_ok(store_result.is_coherence_order_maximal_write)
+    }
+
+    /// Inform GenMC about an atomic read-modify-write operation.
+    /// This includes atomic swap (also often called "exchange"), but does *not*
+    /// include compare-exchange (see `RMWBinOp` for full list of operations).
+    /// Returns the previous value at that memory location, and optionally the value that should be written back to Miri's memory.
+    fn handle_atomic_rmw_op<'tcx>(
+        &self,
+        ecx: &InterpCx<'tcx, MiriMachine<'tcx>>,
+        address: Size,
+        size: Size,
+        ordering: AtomicRwOrd,
+        genmc_rmw_op: RMWBinOp,
+        genmc_rhs_scalar: GenmcScalar,
+        genmc_old_value: GenmcScalar,
+    ) -> InterpResult<'tcx, (Scalar, Option<Scalar>)> {
+        assert!(
+            !self.get_alloc_data_races(),
+            "atomic read-modify-write operation with data race checking disabled."
+        );
+        assert_ne!(0, size.bytes());
+        assert!(
+            size.bytes() <= MAX_ACCESS_SIZE,
+            "GenMC currently does not support atomic accesses larger than {} bytes (got {} bytes)",
+            MAX_ACCESS_SIZE,
+            size.bytes()
+        );
+
+        let curr_thread_id = ecx.machine.threads.active_thread();
+        let genmc_tid = self.exec_state.thread_id_manager.borrow().get_genmc_tid(curr_thread_id);
+        debug!(
+            "GenMC: atomic_rmw_op, thread: {curr_thread_id:?} ({genmc_tid:?}) (op: {genmc_rmw_op:?}, rhs value: {genmc_rhs_scalar:?}), address: {address:?}, size: {size:?}, ordering: {ordering:?}",
+        );
+        let rmw_result = self.handle.borrow_mut().pin_mut().handle_read_modify_write(
+            genmc_tid,
+            address.bytes(),
+            size.bytes(),
+            genmc_rmw_op,
+            ordering.to_genmc(),
+            genmc_rhs_scalar,
+            genmc_old_value,
+        );
+
+        if let Some(error) = rmw_result.error.as_ref() {
+            // FIXME(genmc): error handling
+            throw_ub_format!("{}", error.to_string_lossy());
+        }
+
+        let old_value_scalar = genmc_scalar_to_scalar(ecx, rmw_result.old_value, size)?;
+
+        let new_value_scalar = if rmw_result.is_coherence_order_maximal_write {
+            Some(genmc_scalar_to_scalar(ecx, rmw_result.new_value, size)?)
+        } else {
+            None
+        };
+        interp_ok((old_value_scalar, new_value_scalar))
+    }
+
+    /**** Blocking functionality ****/
+
+    /// Handle a user thread getting blocked.
+    /// This may happen due to an manual `assume` statement added by a user
+    /// or added by some automated program transformation, e.g., for spinloops.
+    fn handle_user_block<'tcx>(&self, _machine: &MiriMachine<'tcx>) -> InterpResult<'tcx> {
         todo!()
     }
 }
+
+impl VisitProvenance for GenmcCtx {
+    fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
+        // We don't have any tags.
+    }
+}
diff --git a/src/tools/miri/src/concurrency/genmc/run.rs b/src/tools/miri/src/concurrency/genmc/run.rs
new file mode 100644
index 00000000000..33c5b6b0a00
--- /dev/null
+++ b/src/tools/miri/src/concurrency/genmc/run.rs
@@ -0,0 +1,166 @@
+use std::rc::Rc;
+use std::sync::Arc;
+use std::time::Instant;
+
+use genmc_sys::EstimationResult;
+use rustc_middle::ty::TyCtxt;
+
+use super::GlobalState;
+use crate::concurrency::genmc::ExecutionEndResult;
+use crate::rustc_const_eval::interpret::PointerArithmetic;
+use crate::{GenmcConfig, GenmcCtx, MiriConfig};
+
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub(super) enum GenmcMode {
+    Estimation,
+    Verification,
+}
+
+impl GenmcMode {
+    /// Return whether warnings on unsupported features should be printed in this mode.
+    fn print_unsupported_warnings(self) -> bool {
+        self == GenmcMode::Verification
+    }
+}
+
+/// Do a complete run of the program in GenMC mode.
+/// This will call `eval_entry` multiple times, until either:
+/// - An error is detected (indicated by a `None` return value)
+/// - All possible executions are explored.
+///
+/// Returns `None` is an error is detected, or `Some(return_value)` with the return value of the last run of the program.
+pub fn run_genmc_mode<'tcx>(
+    config: &MiriConfig,
+    eval_entry: impl Fn(Rc<GenmcCtx>) -> Option<i32>,
+    tcx: TyCtxt<'tcx>,
+) -> Option<i32> {
+    let genmc_config = config.genmc_config.as_ref().unwrap();
+    // Run in Estimation mode if requested.
+    if genmc_config.do_estimation {
+        eprintln!("Estimating GenMC verification time...");
+        run_genmc_mode_impl(config, &eval_entry, tcx, GenmcMode::Estimation)?;
+    }
+    // Run in Verification mode.
+    eprintln!("Running GenMC Verification...");
+    run_genmc_mode_impl(config, &eval_entry, tcx, GenmcMode::Verification)
+}
+
+fn run_genmc_mode_impl<'tcx>(
+    config: &MiriConfig,
+    eval_entry: &impl Fn(Rc<GenmcCtx>) -> Option<i32>,
+    tcx: TyCtxt<'tcx>,
+    mode: GenmcMode,
+) -> Option<i32> {
+    let time_start = Instant::now();
+    let genmc_config = config.genmc_config.as_ref().unwrap();
+
+    // There exists only one `global_state` per full run in GenMC mode.
+    // It is shared by all `GenmcCtx` in this run.
+    // FIXME(genmc): implement multithreading once GenMC supports it.
+    let global_state =
+        Arc::new(GlobalState::new(tcx.target_usize_max(), mode.print_unsupported_warnings()));
+    let genmc_ctx = Rc::new(GenmcCtx::new(config, global_state, mode));
+
+    // `rep` is used to report the progress, Miri will panic on wrap-around.
+    for rep in 0u64.. {
+        tracing::info!("Miri-GenMC loop {}", rep + 1);
+
+        // Prepare for the next execution and inform GenMC about it.
+        genmc_ctx.prepare_next_execution();
+
+        // Execute the program until completion to get the return value, or return if an error happens:
+        let Some(return_code) = eval_entry(genmc_ctx.clone()) else {
+            genmc_ctx.print_genmc_output(genmc_config, tcx);
+            return None;
+        };
+
+        // We inform GenMC that the execution is complete.
+        // If there was an error, we print it.
+        match genmc_ctx.handle_execution_end() {
+            ExecutionEndResult::Continue => continue,
+            ExecutionEndResult::Stop => {
+                let elapsed_time_sec = Instant::now().duration_since(time_start).as_secs_f64();
+                // Print the output for the current mode.
+                if mode == GenmcMode::Estimation {
+                    genmc_ctx.print_estimation_output(genmc_config, elapsed_time_sec);
+                } else {
+                    genmc_ctx.print_verification_output(genmc_config, elapsed_time_sec);
+                }
+                // Return the return code of the last execution.
+                return Some(return_code);
+            }
+            ExecutionEndResult::Error(error) => {
+                // This can be reached for errors that affect the entire execution, not just a specific event.
+                // For instance, linearizability checking and liveness checking report their errors this way.
+                // Neither are supported by Miri-GenMC at the moment though. However, GenMC also
+                // treats races on deallocation as global errors, so this code path is still reachable.
+                // Since we don't have any span information for the error at this point,
+                // we just print GenMC's error string, and the full GenMC output if requested.
+                eprintln!("(GenMC) Error detected: {error}");
+                genmc_ctx.print_genmc_output(genmc_config, tcx);
+                return None;
+            }
+        }
+    }
+    unreachable!()
+}
+
+impl GenmcCtx {
+    /// Print the full output message produced by GenMC if requested, or a hint on how to enable it.
+    ///
+    /// This message can be very verbose and is likely not useful for the average user.
+    /// This function should be called *after* Miri has printed all of its output.
+    fn print_genmc_output(&self, genmc_config: &GenmcConfig, tcx: TyCtxt<'_>) {
+        if genmc_config.print_genmc_output {
+            eprintln!("GenMC error report:");
+            eprintln!("{}", self.get_result_message());
+        } else {
+            tcx.dcx().note(
+                "add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report"
+            );
+        }
+    }
+
+    /// Given the time taken for the estimation mode run, print the expected time range for verification.
+    /// Verbose output also includes information about the expected number of executions and how many estimation rounds were explored or got blocked.
+    fn print_estimation_output(&self, genmc_config: &GenmcConfig, elapsed_time_sec: f64) {
+        let EstimationResult { mean, sd, explored_execs, blocked_execs } =
+            self.get_estimation_results();
+        #[allow(clippy::as_conversions)]
+        let time_per_exec_sec = elapsed_time_sec / (explored_execs as f64 + blocked_execs as f64);
+        let estimated_mean_sec = time_per_exec_sec * mean;
+        let estimated_sd_sec = time_per_exec_sec * sd;
+
+        if genmc_config.verbose_output {
+            eprintln!("Finished estimation in {elapsed_time_sec:.2?}s");
+            if blocked_execs != 0 {
+                eprintln!("  Explored executions: {explored_execs}");
+                eprintln!("  Blocked  executions: {blocked_execs}");
+            }
+            eprintln!("Expected number of executions: {mean:.0} ± {sd:.0}");
+        }
+        // The estimation can be out-of-bounds of an `f64`.
+        if !(mean.is_finite() && mean >= 0.0 && sd.is_finite() && sd >= 0.0) {
+            eprintln!("WARNING: Estimation gave weird results, there may have been an overflow.");
+        }
+        eprintln!("Expected verification time: {estimated_mean_sec:.2}s ± {estimated_sd_sec:.2}s");
+    }
+
+    /// Given the time taken for the verification mode run, print the expected time range for verification.
+    /// Verbose output also includes information about the expected number of executions and how many estimation rounds were explored or got blocked.
+    fn print_verification_output(&self, genmc_config: &GenmcConfig, elapsed_time_sec: f64) {
+        let explored_execution_count = self.get_explored_execution_count();
+        let blocked_execution_count = self.get_blocked_execution_count();
+        eprintln!(
+            "Verification complete with {} executions. No errors found.",
+            explored_execution_count + blocked_execution_count
+        );
+        if genmc_config.verbose_output {
+            if blocked_execution_count > 0 {
+                eprintln!("Number of complete executions explored: {explored_execution_count}");
+                eprintln!("Number of blocked executions seen: {blocked_execution_count}");
+            }
+            eprintln!("Verification took {elapsed_time_sec:.2?}s.");
+        }
+    }
+}
diff --git a/src/tools/miri/src/concurrency/genmc/scheduling.rs b/src/tools/miri/src/concurrency/genmc/scheduling.rs
new file mode 100644
index 00000000000..b5c23f2d084
--- /dev/null
+++ b/src/tools/miri/src/concurrency/genmc/scheduling.rs
@@ -0,0 +1,69 @@
+use genmc_sys::{ActionKind, ExecutionState};
+
+use super::GenmcCtx;
+use crate::{
+    InterpCx, InterpResult, MiriMachine, TerminationInfo, ThreadId, interp_ok, throw_machine_stop,
+};
+
+impl GenmcCtx {
+    pub(crate) fn schedule_thread<'tcx>(
+        &self,
+        ecx: &InterpCx<'tcx, MiriMachine<'tcx>>,
+    ) -> InterpResult<'tcx, ThreadId> {
+        let thread_manager = &ecx.machine.threads;
+        let active_thread_id = thread_manager.active_thread();
+
+        // Determine whether the next instruction in the current thread might be a load.
+        // This is used for the "writes-first" scheduling in GenMC.
+        // Scheduling writes before reads can be beneficial for verification performance.
+        // `Load` is a safe default for the next instruction type if we cannot guarantee that it isn't a load.
+        let curr_thread_next_instr_kind = if !thread_manager.active_thread_ref().is_enabled() {
+            // The current thread can get blocked (e.g., due to a thread join, `Mutex::lock`, assume statement, ...), then we need to ask GenMC for another thread to schedule.
+            // Most to all blocking operations have load semantics, since they wait on something to change in another thread,
+            // e.g., a thread join waiting on another thread to finish (join loads the return value(s) of the other thread),
+            // or a thread waiting for another thread to unlock a `Mutex`, which loads the mutex state (Locked, Unlocked).
+            ActionKind::Load
+        } else {
+            // This thread is still enabled. If it executes a terminator next, we consider yielding,
+            // but in all other cases we just keep running this thread since it never makes sense
+            // to yield before a non-atomic operation.
+            let Some(frame) = thread_manager.active_thread_stack().last() else {
+                return interp_ok(active_thread_id);
+            };
+            let either::Either::Left(loc) = frame.current_loc() else {
+                // We are unwinding, so the next step is definitely not atomic.
+                return interp_ok(active_thread_id);
+            };
+            let basic_block = &frame.body().basic_blocks[loc.block];
+            if let Some(_statement) = basic_block.statements.get(loc.statement_index) {
+                // Statements can't be atomic.
+                return interp_ok(active_thread_id);
+            }
+
+            // FIXME(genmc): determine terminator kind.
+            ActionKind::Load
+        };
+
+        let thread_infos = self.exec_state.thread_id_manager.borrow();
+        let genmc_tid = thread_infos.get_genmc_tid(active_thread_id);
+
+        let mut mc = self.handle.borrow_mut();
+        let pinned_mc = mc.as_mut().unwrap();
+        let result = pinned_mc.schedule_next(genmc_tid, curr_thread_next_instr_kind);
+        // Depending on the exec_state, we either schedule the given thread, or we are finished with this execution.
+        match result.exec_state {
+            ExecutionState::Ok => interp_ok(thread_infos.get_miri_tid(result.next_thread)),
+            ExecutionState::Blocked => throw_machine_stop!(TerminationInfo::GenmcBlockedExecution),
+            ExecutionState::Finished => {
+                let exit_status = self.exec_state.exit_status.get().expect(
+                    "If the execution is finished, we should have a return value from the program.",
+                );
+                throw_machine_stop!(TerminationInfo::Exit {
+                    code: exit_status.exit_code,
+                    leak_check: exit_status.do_leak_check()
+                });
+            }
+            _ => unreachable!(),
+        }
+    }
+}
diff --git a/src/tools/miri/src/concurrency/genmc/thread_id_map.rs b/src/tools/miri/src/concurrency/genmc/thread_id_map.rs
new file mode 100644
index 00000000000..9676496fe7f
--- /dev/null
+++ b/src/tools/miri/src/concurrency/genmc/thread_id_map.rs
@@ -0,0 +1,63 @@
+use genmc_sys::GENMC_MAIN_THREAD_ID;
+use rustc_data_structures::fx::FxHashMap;
+
+use crate::ThreadId;
+
+#[derive(Debug)]
+pub struct ThreadIdMap {
+    /// Map from Miri thread IDs to GenMC thread IDs.
+    /// We assume as little as possible about Miri thread IDs, so we use a map.
+    miri_to_genmc: FxHashMap<ThreadId, i32>,
+    /// Map from GenMC thread IDs to Miri thread IDs.
+    /// We control which thread IDs are used, so we choose them in as an incrementing counter.
+    genmc_to_miri: Vec<ThreadId>, // FIXME(genmc): check if this assumption is (and will stay) correct.
+}
+
+impl Default for ThreadIdMap {
+    fn default() -> Self {
+        let miri_to_genmc = [(ThreadId::MAIN_THREAD, GENMC_MAIN_THREAD_ID)].into_iter().collect();
+        let genmc_to_miri = vec![ThreadId::MAIN_THREAD];
+        Self { miri_to_genmc, genmc_to_miri }
+    }
+}
+
+impl ThreadIdMap {
+    pub fn reset(&mut self) {
+        self.miri_to_genmc.clear();
+        self.miri_to_genmc.insert(ThreadId::MAIN_THREAD, GENMC_MAIN_THREAD_ID);
+        self.genmc_to_miri.clear();
+        self.genmc_to_miri.push(ThreadId::MAIN_THREAD);
+    }
+
+    #[must_use]
+    /// Add a new Miri thread to the mapping and dispense a new thread ID for GenMC to use.
+    pub fn add_thread(&mut self, thread_id: ThreadId) -> i32 {
+        // NOTE: We select the new thread ids as integers incremented by one (we use the length as the counter).
+        let next_thread_id = self.genmc_to_miri.len();
+        let genmc_tid = next_thread_id.try_into().unwrap();
+        // If there is already an entry, we override it.
+        // This could happen if Miri were to reuse `ThreadId`s, but we assume that if this happens, the previous thread with that id doesn't exist anymore.
+        self.miri_to_genmc.insert(thread_id, genmc_tid);
+        self.genmc_to_miri.push(thread_id);
+
+        genmc_tid
+    }
+
+    #[must_use]
+    /// Try to get the GenMC thread ID corresponding to a given Miri `ThreadId`.
+    /// Panics if there is no mapping for the given `ThreadId`.
+    pub fn get_genmc_tid(&self, thread_id: ThreadId) -> i32 {
+        *self.miri_to_genmc.get(&thread_id).unwrap()
+    }
+
+    #[must_use]
+    /// Get the Miri `ThreadId` corresponding to a given GenMC thread id.
+    /// Panics if the given thread id isn't valid.
+    pub fn get_miri_tid(&self, genmc_tid: i32) -> ThreadId {
+        let index: usize = genmc_tid.try_into().unwrap();
+        self.genmc_to_miri
+            .get(index)
+            .copied()
+            .expect("A thread id returned from GenMC should exist.")
+    }
+}
diff --git a/src/tools/miri/src/concurrency/init_once.rs b/src/tools/miri/src/concurrency/init_once.rs
index 165215f9270..daea20b3779 100644
--- a/src/tools/miri/src/concurrency/init_once.rs
+++ b/src/tools/miri/src/concurrency/init_once.rs
@@ -96,10 +96,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         init_once.status = InitOnceStatus::Complete;
 
         // Each complete happens-before the end of the wait
-        if let Some(data_race) = this.machine.data_race.as_vclocks_ref() {
-            data_race
-                .release_clock(&this.machine.threads, |clock| init_once.clock.clone_from(clock));
-        }
+        this.release_clock(|clock| init_once.clock.clone_from(clock))?;
 
         // Wake up everyone.
         // need to take the queue to avoid having `this` be borrowed multiple times
@@ -125,10 +122,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         init_once.status = InitOnceStatus::Uninitialized;
 
         // Each complete happens-before the end of the wait
-        if let Some(data_race) = this.machine.data_race.as_vclocks_ref() {
-            data_race
-                .release_clock(&this.machine.threads, |clock| init_once.clock.clone_from(clock));
-        }
+        this.release_clock(|clock| init_once.clock.clone_from(clock))?;
 
         // Wake up one waiting thread, so they can go ahead and try to init this.
         if let Some(waiter) = init_once.waiters.pop_front() {
@@ -142,7 +136,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     /// Synchronize with the previous completion of an InitOnce.
     /// Must only be called after checking that it is complete.
     #[inline]
-    fn init_once_observe_completed(&mut self, init_once_ref: &InitOnceRef) {
+    fn init_once_observe_completed(&mut self, init_once_ref: &InitOnceRef) -> InterpResult<'tcx> {
         let this = self.eval_context_mut();
         let init_once = init_once_ref.0.borrow();
 
@@ -152,6 +146,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             "observing the completion of incomplete init once"
         );
 
-        this.acquire_clock(&init_once.clock);
+        this.acquire_clock(&init_once.clock)
     }
 }
diff --git a/src/tools/miri/src/concurrency/mod.rs b/src/tools/miri/src/concurrency/mod.rs
index 435615efd9f..9dae858592f 100644
--- a/src/tools/miri/src/concurrency/mod.rs
+++ b/src/tools/miri/src/concurrency/mod.rs
@@ -22,5 +22,5 @@ pub mod weak_memory;
 mod genmc;
 
 pub use self::data_race_handler::{AllocDataRaceHandler, GlobalDataRaceHandler};
-pub use self::genmc::{GenmcConfig, GenmcCtx};
+pub use self::genmc::{ExitType, GenmcConfig, GenmcCtx, run_genmc_mode};
 pub use self::vector_clock::VClock;
diff --git a/src/tools/miri/src/concurrency/sync.rs b/src/tools/miri/src/concurrency/sync.rs
index 179094db0fe..e4e7fb1d725 100644
--- a/src/tools/miri/src/concurrency/sync.rs
+++ b/src/tools/miri/src/concurrency/sync.rs
@@ -204,7 +204,7 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
             this.mutex_enqueue_and_block(mutex_ref, Some((retval, dest)));
         } else {
             // We can have it right now!
-            this.mutex_lock(&mutex_ref);
+            this.mutex_lock(&mutex_ref)?;
             // Don't forget to write the return value.
             this.write_scalar(retval, &dest)?;
         }
@@ -338,7 +338,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     }
 
     /// Lock by setting the mutex owner and increasing the lock count.
-    fn mutex_lock(&mut self, mutex_ref: &MutexRef) {
+    fn mutex_lock(&mut self, mutex_ref: &MutexRef) -> InterpResult<'tcx> {
         let this = self.eval_context_mut();
         let thread = this.active_thread();
         let mut mutex = mutex_ref.0.borrow_mut();
@@ -352,9 +352,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             mutex.owner = Some(thread);
         }
         mutex.lock_count = mutex.lock_count.strict_add(1);
-        if let Some(data_race) = this.machine.data_race.as_vclocks_ref() {
-            data_race.acquire_clock(&mutex.clock, &this.machine.threads);
-        }
+        this.acquire_clock(&mutex.clock)?;
+        interp_ok(())
     }
 
     /// Try unlocking by decreasing the lock count and returning the old lock
@@ -377,17 +376,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 // The mutex is completely unlocked. Try transferring ownership
                 // to another thread.
 
-                if let Some(data_race) = this.machine.data_race.as_vclocks_ref() {
-                    data_race.release_clock(&this.machine.threads, |clock| {
-                        mutex.clock.clone_from(clock)
-                    });
-                }
+                this.release_clock(|clock| mutex.clock.clone_from(clock))?;
                 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)?;
+                if let Some(thread_id) = thread_id {
+                    this.unblock_thread(thread_id, BlockReason::Mutex)?;
                 }
             }
             Some(old_lock_count)
@@ -425,7 +420,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     assert_eq!(unblock, UnblockKind::Ready);
 
                     assert!(mutex_ref.owner().is_none());
-                    this.mutex_lock(&mutex_ref);
+                    this.mutex_lock(&mutex_ref)?;
 
                     if let Some((retval, dest)) = retval_dest {
                         this.write_scalar(retval, &dest)?;
@@ -439,7 +434,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
     /// Read-lock the lock by adding the `reader` the list of threads that own
     /// this lock.
-    fn rwlock_reader_lock(&mut self, rwlock_ref: &RwLockRef) {
+    fn rwlock_reader_lock(&mut self, rwlock_ref: &RwLockRef) -> InterpResult<'tcx> {
         let this = self.eval_context_mut();
         let thread = this.active_thread();
         trace!("rwlock_reader_lock: now also held (one more time) by {:?}", thread);
@@ -447,9 +442,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         assert!(!rwlock.is_write_locked(), "the lock is write locked");
         let count = rwlock.readers.entry(thread).or_insert(0);
         *count = count.strict_add(1);
-        if let Some(data_race) = this.machine.data_race.as_vclocks_ref() {
-            data_race.acquire_clock(&rwlock.clock_unlocked, &this.machine.threads);
-        }
+        this.acquire_clock(&rwlock.clock_unlocked)?;
+        interp_ok(())
     }
 
     /// Try read-unlock the lock for the current threads and potentially give the lock to a new owner.
@@ -472,12 +466,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             }
             Entry::Vacant(_) => return interp_ok(false), // we did not even own this lock
         }
-        if let Some(data_race) = this.machine.data_race.as_vclocks_ref() {
-            // Add this to the shared-release clock of all concurrent readers.
-            data_race.release_clock(&this.machine.threads, |clock| {
-                rwlock.clock_current_readers.join(clock)
-            });
-        }
+        // Add this to the shared-release clock of all concurrent readers.
+        this.release_clock(|clock| rwlock.clock_current_readers.join(clock))?;
 
         // The thread was a reader. If the lock is not held any more, give it to a writer.
         if rwlock.is_locked().not() {
@@ -521,7 +511,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 }
                 |this, unblock: UnblockKind| {
                     assert_eq!(unblock, UnblockKind::Ready);
-                    this.rwlock_reader_lock(&rwlock_ref);
+                    this.rwlock_reader_lock(&rwlock_ref)?;
                     this.write_scalar(retval, &dest)?;
                     interp_ok(())
                 }
@@ -531,7 +521,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
     /// Lock by setting the writer that owns the lock.
     #[inline]
-    fn rwlock_writer_lock(&mut self, rwlock_ref: &RwLockRef) {
+    fn rwlock_writer_lock(&mut self, rwlock_ref: &RwLockRef) -> InterpResult<'tcx> {
         let this = self.eval_context_mut();
         let thread = this.active_thread();
         trace!("rwlock_writer_lock: now held by {:?}", thread);
@@ -539,9 +529,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let mut rwlock = rwlock_ref.0.borrow_mut();
         assert!(!rwlock.is_locked(), "the rwlock is already locked");
         rwlock.writer = Some(thread);
-        if let Some(data_race) = this.machine.data_race.as_vclocks_ref() {
-            data_race.acquire_clock(&rwlock.clock_unlocked, &this.machine.threads);
-        }
+        this.acquire_clock(&rwlock.clock_unlocked)?;
+        interp_ok(())
     }
 
     /// Try to unlock an rwlock held by the current thread.
@@ -559,11 +548,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             rwlock.writer = None;
             trace!("rwlock_writer_unlock: unlocked by {:?}", thread);
             // Record release clock for next lock holder.
-            if let Some(data_race) = this.machine.data_race.as_vclocks_ref() {
-                data_race.release_clock(&this.machine.threads, |clock| {
-                    rwlock.clock_unlocked.clone_from(clock)
-                });
-            }
+            this.release_clock(|clock| rwlock.clock_unlocked.clone_from(clock))?;
 
             // The thread was a writer.
             //
@@ -613,7 +598,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 }
                 |this, unblock: UnblockKind| {
                     assert_eq!(unblock, UnblockKind::Ready);
-                    this.rwlock_writer_lock(&rwlock_ref);
+                    this.rwlock_writer_lock(&rwlock_ref)?;
                     this.write_scalar(retval, &dest)?;
                     interp_ok(())
                 }
@@ -663,12 +648,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     match unblock {
                         UnblockKind::Ready => {
                             // The condvar was signaled. Make sure we get the clock for that.
-                            if let Some(data_race) = this.machine.data_race.as_vclocks_ref() {
-                                data_race.acquire_clock(
+                            this.acquire_clock(
                                     &condvar_ref.0.borrow().clock,
-                                    &this.machine.threads,
-                                );
-                            }
+                                )?;
                             // 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_ref, retval_succ, dest)
@@ -695,9 +677,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let mut condvar = condvar_ref.0.borrow_mut();
 
         // Each condvar signal happens-before the end of the condvar wake
-        if let Some(data_race) = this.machine.data_race.as_vclocks_ref() {
-            data_race.release_clock(&this.machine.threads, |clock| condvar.clock.clone_from(clock));
-        }
+        this.release_clock(|clock| condvar.clock.clone_from(clock))?;
         let Some(waiter) = condvar.waiters.pop_front() else {
             return interp_ok(false);
         };
@@ -736,9 +716,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                         UnblockKind::Ready => {
                             let futex = futex_ref.0.borrow();
                             // Acquire the clock of the futex.
-                            if let Some(data_race) = this.machine.data_race.as_vclocks_ref() {
-                                data_race.acquire_clock(&futex.clock, &this.machine.threads);
-                            }
+                            this.acquire_clock(&futex.clock)?;
                         },
                         UnblockKind::TimedOut => {
                             // Remove the waiter from the futex.
@@ -766,9 +744,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let mut futex = futex_ref.0.borrow_mut();
 
         // Each futex-wake happens-before the end of the futex wait
-        if let Some(data_race) = this.machine.data_race.as_vclocks_ref() {
-            data_race.release_clock(&this.machine.threads, |clock| futex.clock.clone_from(clock));
-        }
+        this.release_clock(|clock| futex.clock.clone_from(clock))?;
 
         // Remove `count` of the threads in the queue that match any of the bits in the bitset.
         // We collect all of them before unblocking because the unblock callback may access the
diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs
index fe1ef86ccd3..00c5e337b1e 100644
--- a/src/tools/miri/src/concurrency/thread.rs
+++ b/src/tools/miri/src/concurrency/thread.rs
@@ -209,6 +209,11 @@ impl<'tcx> Thread<'tcx> {
         self.thread_name.as_deref()
     }
 
+    /// Return whether this thread is enabled or not.
+    pub fn is_enabled(&self) -> bool {
+        self.state.is_enabled()
+    }
+
     /// Get the name of the current thread for display purposes; will include thread ID if not set.
     fn thread_display_name(&self, id: ThreadId) -> String {
         if let Some(ref thread_name) = self.thread_name {
@@ -404,6 +409,7 @@ pub struct ThreadManager<'tcx> {
     /// A mapping from a thread-local static to the thread specific allocation.
     thread_local_allocs: FxHashMap<(DefId, ThreadId), StrictPointer>,
     /// A flag that indicates that we should change the active thread.
+    /// Completely ignored in GenMC mode.
     yield_active_thread: bool,
     /// A flag that indicates that we should do round robin scheduling of threads else randomized scheduling is used.
     fixed_scheduling: bool,
@@ -676,13 +682,6 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
     #[inline]
     fn run_on_stack_empty(&mut self) -> InterpResult<'tcx, Poll<()>> {
         let this = self.eval_context_mut();
-        // Inform GenMC that a thread has finished all user code. GenMC needs to know this for scheduling.
-        // FIXME(GenMC): Thread-local destructors *are* user code, so this is odd. Also now that we
-        // support pre-main constructors, it can get called there as well.
-        if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() {
-            let thread_id = this.active_thread();
-            genmc_ctx.handle_thread_stack_empty(thread_id);
-        }
         let mut callback = this
             .active_thread_mut()
             .on_stack_empty
@@ -703,19 +702,19 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
     /// If GenMC mode is active, the scheduling is instead handled by GenMC.
     fn schedule(&mut self) -> InterpResult<'tcx, SchedulingAction> {
         let this = self.eval_context_mut();
-        // In GenMC mode, we let GenMC do the scheduling
+
+        // In GenMC mode, we let GenMC do the scheduling.
         if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() {
             let next_thread_id = genmc_ctx.schedule_thread(this)?;
 
             let thread_manager = &mut this.machine.threads;
             thread_manager.active_thread = next_thread_id;
-            thread_manager.yield_active_thread = false;
 
             assert!(thread_manager.threads[thread_manager.active_thread].state.is_enabled());
             return interp_ok(SchedulingAction::ExecuteStep);
         }
 
-        // We are not in GenMC mode, so we control the schedule
+        // We are not in GenMC mode, so we control the scheduling.
         let thread_manager = &mut this.machine.threads;
         let clock = &this.machine.monotonic_clock;
         let rng = this.machine.rng.get_mut();
@@ -863,7 +862,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             GlobalDataRaceHandler::Vclocks(data_race) =>
                 data_race.thread_created(&this.machine.threads, new_thread_id, current_span),
             GlobalDataRaceHandler::Genmc(genmc_ctx) =>
-                genmc_ctx.handle_thread_create(&this.machine.threads, new_thread_id)?,
+                genmc_ctx.handle_thread_create(
+                    &this.machine.threads,
+                    start_routine,
+                    &func_arg,
+                    new_thread_id,
+                )?,
         }
         // Write the current thread-id, switch to the next thread later
         // to treat this write operation as occurring on the current thread.
@@ -916,13 +920,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let thread = this.active_thread_mut();
         assert!(thread.stack.is_empty(), "only threads with an empty stack can be terminated");
         thread.state = ThreadState::Terminated;
-        match &mut this.machine.data_race {
-            GlobalDataRaceHandler::None => {}
-            GlobalDataRaceHandler::Vclocks(data_race) =>
-                data_race.thread_terminated(&this.machine.threads),
-            GlobalDataRaceHandler::Genmc(genmc_ctx) =>
-                genmc_ctx.handle_thread_finish(&this.machine.threads)?,
-        }
+
         // Deallocate TLS.
         let gone_thread = this.active_thread();
         {
@@ -953,6 +951,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 }
             }
         }
+
+        match &mut this.machine.data_race {
+            GlobalDataRaceHandler::None => {}
+            GlobalDataRaceHandler::Vclocks(data_race) =>
+                data_race.thread_terminated(&this.machine.threads),
+            GlobalDataRaceHandler::Genmc(genmc_ctx) => {
+                // Inform GenMC that the thread finished.
+                // This needs to happen once all accesses to the thread are done, including freeing any TLS statics.
+                genmc_ctx.handle_thread_finish(&this.machine.threads)
+            }
+        }
+
         // Unblock joining threads.
         let unblock_reason = BlockReason::Join(gone_thread);
         let threads = &this.machine.threads.threads;
@@ -978,6 +988,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         callback: DynUnblockCallback<'tcx>,
     ) {
         let this = self.eval_context_mut();
+        if timeout.is_some() && this.machine.data_race.as_genmc_ref().is_some() {
+            panic!("Unimplemented: Timeouts not yet supported in GenMC mode.");
+        }
         let timeout = timeout.map(|(clock, anchor, duration)| {
             let anchor = match clock {
                 TimeoutClock::RealTime => {
@@ -1076,6 +1089,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 "{:?} blocked on {:?} when trying to join",
                 thread_mgr.active_thread, joined_thread_id
             );
+            if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() {
+                genmc_ctx.handle_thread_join(thread_mgr.active_thread, joined_thread_id)?;
+            }
+
             // The joined thread is still running, we need to wait for it.
             // Once we get unblocked, perform the appropriate synchronization and write the return value.
             let dest = return_dest.clone();
diff --git a/src/tools/miri/src/concurrency/weak_memory.rs b/src/tools/miri/src/concurrency/weak_memory.rs
index a752ef7e454..6ebade6a568 100644
--- a/src/tools/miri/src/concurrency/weak_memory.rs
+++ b/src/tools/miri/src/concurrency/weak_memory.rs
@@ -62,8 +62,11 @@
 //! You can refer to test cases in weak_memory/extra_cpp.rs and weak_memory/extra_cpp_unsafe.rs for examples of these operations.
 
 // Our and the author's own implementation (tsan11) of the paper have some deviations from the provided operational semantics in §5.3:
-// 1. In the operational semantics, store elements keep a copy of the atomic object's vector clock (AtomicCellClocks::sync_vector in miri),
-// but this is not used anywhere so it's omitted here.
+// 1. In the operational semantics, loads acquire the vector clock of the atomic location
+// irrespective of which store buffer element is loaded. That's incorrect; the synchronization clock
+// needs to be tracked per-store-buffer-element. (The paper has a field "clocks" for that purpose,
+// but it is not actuallt used.) tsan11 does this correctly
+// (https://github.com/ChrisLidbury/tsan11/blob/ecbd6b81e9b9454e01cba78eb9d88684168132c7/lib/tsan/rtl/tsan_relaxed.cc#L305).
 //
 // 2. In the operational semantics, each store element keeps the timestamp of a thread when it loads from the store.
 // If the same thread loads from the same store element multiple times, then the timestamps at all loads are saved in a list of load elements.
@@ -138,16 +141,17 @@ enum LoadRecency {
 
 #[derive(Debug, Clone, PartialEq, Eq)]
 struct StoreElement {
-    /// The identifier of the vector index, corresponding to a thread
-    /// that performed the store.
-    store_index: VectorIdx,
+    /// The thread that performed the store.
+    store_thread: VectorIdx,
+    /// The timestamp of the storing thread when it performed the store
+    store_timestamp: VTimestamp,
+
+    /// The vector clock that can be acquired by loading this store.
+    sync_clock: VClock,
 
     /// Whether this store is SC.
     is_seqcst: bool,
 
-    /// The timestamp of the storing thread when it performed the store
-    timestamp: VTimestamp,
-
     /// The value of this store. `None` means uninitialized.
     // FIXME: Currently, we cannot represent partial initialization.
     val: Option<Scalar>,
@@ -159,9 +163,12 @@ struct StoreElement {
 
 #[derive(Debug, Clone, PartialEq, Eq, Default)]
 struct LoadInfo {
-    /// Timestamp of first loads from this store element by each thread
+    /// Timestamp of first loads from this store element by each thread.
     timestamps: FxHashMap<VectorIdx, VTimestamp>,
-    /// Whether this store element has been read by an SC load
+    /// Whether this store element has been read by an SC load.
+    /// This is crucial to ensure we respect coherence-ordered-before. Concretely we use
+    /// this to ensure that if a store element is seen by an SC load, then all later SC loads
+    /// cannot see `mo`-earlier store elements.
     sc_loaded: bool,
 }
 
@@ -243,8 +250,10 @@ impl<'tcx> StoreBuffer {
         let store_elem = StoreElement {
             // The thread index and timestamp of the initialisation write
             // are never meaningfully used, so it's fine to leave them as 0
-            store_index: VectorIdx::from(0),
-            timestamp: VTimestamp::ZERO,
+            store_thread: VectorIdx::from(0),
+            store_timestamp: VTimestamp::ZERO,
+            // The initialization write is non-atomic so nothing can be acquired.
+            sync_clock: VClock::default(),
             val: init,
             is_seqcst: false,
             load_info: RefCell::new(LoadInfo::default()),
@@ -273,7 +282,7 @@ impl<'tcx> StoreBuffer {
         thread_mgr: &ThreadManager<'_>,
         is_seqcst: bool,
         rng: &mut (impl rand::Rng + ?Sized),
-        validate: impl FnOnce() -> InterpResult<'tcx>,
+        validate: impl FnOnce(Option<&VClock>) -> InterpResult<'tcx>,
     ) -> InterpResult<'tcx, (Option<Scalar>, LoadRecency)> {
         // Having a live borrow to store_buffer while calling validate_atomic_load is fine
         // because the race detector doesn't touch store_buffer
@@ -290,7 +299,7 @@ impl<'tcx> StoreBuffer {
         // after we've picked a store element from the store buffer, as presented
         // in ATOMIC LOAD rule of the paper. This is because fetch_store
         // requires access to ThreadClockSet.clock, which is updated by the race detector
-        validate()?;
+        validate(Some(&store_elem.sync_clock))?;
 
         let (index, clocks) = global.active_thread_state(thread_mgr);
         let loaded = store_elem.load_impl(index, &clocks, is_seqcst);
@@ -303,10 +312,11 @@ impl<'tcx> StoreBuffer {
         global: &DataRaceState,
         thread_mgr: &ThreadManager<'_>,
         is_seqcst: bool,
+        sync_clock: VClock,
     ) -> InterpResult<'tcx> {
         let (index, clocks) = global.active_thread_state(thread_mgr);
 
-        self.store_impl(val, index, &clocks.clock, is_seqcst);
+        self.store_impl(val, index, &clocks.clock, is_seqcst, sync_clock);
         interp_ok(())
     }
 
@@ -333,7 +343,9 @@ impl<'tcx> StoreBuffer {
                     return false;
                 }
 
-                keep_searching = if store_elem.timestamp <= clocks.clock[store_elem.store_index] {
+                keep_searching = if store_elem.store_timestamp
+                    <= clocks.clock[store_elem.store_thread]
+                {
                     // CoWR: if a store happens-before the current load,
                     // then we can't read-from anything earlier in modification order.
                     // C++20 §6.9.2.2 [intro.races] paragraph 18
@@ -345,7 +357,7 @@ impl<'tcx> StoreBuffer {
                     // then we cannot read-from anything earlier in modification order.
                     // C++20 §6.9.2.2 [intro.races] paragraph 16
                     false
-                } else if store_elem.timestamp <= clocks.write_seqcst[store_elem.store_index]
+                } else if store_elem.store_timestamp <= clocks.write_seqcst[store_elem.store_thread]
                     && store_elem.is_seqcst
                 {
                     // The current non-SC load, which may be sequenced-after an SC fence,
@@ -353,7 +365,7 @@ impl<'tcx> StoreBuffer {
                     // C++17 §32.4 [atomics.order] paragraph 4
                     false
                 } else if is_seqcst
-                    && store_elem.timestamp <= clocks.read_seqcst[store_elem.store_index]
+                    && store_elem.store_timestamp <= clocks.read_seqcst[store_elem.store_thread]
                 {
                     // The current SC load cannot read-before the last store sequenced-before
                     // the last SC fence.
@@ -391,17 +403,19 @@ impl<'tcx> StoreBuffer {
         }
     }
 
-    /// ATOMIC STORE IMPL in the paper (except we don't need the location's vector clock)
+    /// ATOMIC STORE IMPL in the paper
     fn store_impl(
         &mut self,
         val: Scalar,
         index: VectorIdx,
         thread_clock: &VClock,
         is_seqcst: bool,
+        sync_clock: VClock,
     ) {
         let store_elem = StoreElement {
-            store_index: index,
-            timestamp: thread_clock[index],
+            store_thread: index,
+            store_timestamp: thread_clock[index],
+            sync_clock,
             // In the language provided in the paper, an atomic store takes the value from a
             // non-atomic memory location.
             // But we already have the immediate value here so we don't need to do the memory
@@ -419,7 +433,7 @@ impl<'tcx> StoreBuffer {
             // so that in a later SC load, only the last SC store (i.e. this one) or stores that
             // aren't ordered by hb with the last SC is picked.
             self.buffer.iter_mut().rev().for_each(|elem| {
-                if elem.timestamp <= thread_clock[elem.store_index] {
+                if elem.store_timestamp <= thread_clock[elem.store_thread] {
                     elem.is_seqcst = true;
                 }
             })
@@ -462,7 +476,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let (alloc_id, base_offset, ..) = this.ptr_get_alloc_id(place.ptr(), 0)?;
         if let (
             crate::AllocExtra {
-                data_race: AllocDataRaceHandler::Vclocks(_, Some(alloc_buffers)),
+                data_race: AllocDataRaceHandler::Vclocks(data_race_clocks, Some(alloc_buffers)),
                 ..
             },
             crate::MiriMachine {
@@ -475,19 +489,29 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 global.sc_write(threads);
             }
             let range = alloc_range(base_offset, place.layout.size);
+            let sync_clock = data_race_clocks.sync_clock(range);
             let buffer = alloc_buffers.get_or_create_store_buffer_mut(range, Some(init))?;
+            // The RMW always reads from the most recent store.
             buffer.read_from_last_store(global, threads, atomic == AtomicRwOrd::SeqCst);
-            buffer.buffered_write(new_val, global, threads, atomic == AtomicRwOrd::SeqCst)?;
+            buffer.buffered_write(
+                new_val,
+                global,
+                threads,
+                atomic == AtomicRwOrd::SeqCst,
+                sync_clock,
+            )?;
         }
         interp_ok(())
     }
 
+    /// The argument to `validate` is the synchronization clock of the memory that is being read,
+    /// if we are reading from a store buffer element.
     fn buffered_atomic_read(
         &self,
         place: &MPlaceTy<'tcx>,
         atomic: AtomicReadOrd,
         latest_in_mo: Scalar,
-        validate: impl FnOnce() -> InterpResult<'tcx>,
+        validate: impl FnOnce(Option<&VClock>) -> InterpResult<'tcx>,
     ) -> InterpResult<'tcx, Option<Scalar>> {
         let this = self.eval_context_ref();
         'fallback: {
@@ -525,7 +549,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         }
 
         // Race detector or weak memory disabled, simply read the latest value
-        validate()?;
+        validate(None)?;
         interp_ok(Some(latest_in_mo))
     }
 
@@ -533,6 +557,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     ///
     /// `init` says with which value to initialize the store buffer in case there wasn't a store
     /// buffer for this memory range before.
+    ///
+    /// Must be called *after* `validate_atomic_store` to ensure that `sync_clock` is up-to-date.
     fn buffered_atomic_write(
         &mut self,
         val: Scalar,
@@ -544,7 +570,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let (alloc_id, base_offset, ..) = this.ptr_get_alloc_id(dest.ptr(), 0)?;
         if let (
             crate::AllocExtra {
-                data_race: AllocDataRaceHandler::Vclocks(_, Some(alloc_buffers)),
+                data_race: AllocDataRaceHandler::Vclocks(data_race_clocks, Some(alloc_buffers)),
                 ..
             },
             crate::MiriMachine {
@@ -556,9 +582,18 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 global.sc_write(threads);
             }
 
-            let buffer = alloc_buffers
-                .get_or_create_store_buffer_mut(alloc_range(base_offset, dest.layout.size), init)?;
-            buffer.buffered_write(val, global, threads, atomic == AtomicWriteOrd::SeqCst)?;
+            let range = alloc_range(base_offset, dest.layout.size);
+            // It's a bit annoying that we have to go back to the data race part to get the clock...
+            // but it does make things a lot simpler.
+            let sync_clock = data_race_clocks.sync_clock(range);
+            let buffer = alloc_buffers.get_or_create_store_buffer_mut(range, init)?;
+            buffer.buffered_write(
+                val,
+                global,
+                threads,
+                atomic == AtomicWriteOrd::SeqCst,
+                sync_clock,
+            )?;
         }
 
         // Caller should've written to dest with the vanilla scalar write, we do nothing here
diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs
index 9ecbd31c5b9..15f7ccbabca 100644
--- a/src/tools/miri/src/diagnostics.rs
+++ b/src/tools/miri/src/diagnostics.rs
@@ -31,8 +31,9 @@ pub enum TerminationInfo {
     },
     Int2PtrWithStrictProvenance,
     Deadlock,
-    /// In GenMC mode, an execution can get stuck in certain cases. This is not an error.
-    GenmcStuckExecution,
+    /// In GenMC mode, executions can get blocked, which stops the current execution without running any cleanup.
+    /// No leak checks should be performed if this happens, since they would give false positives.
+    GenmcBlockedExecution,
     MultipleSymbolDefinitions {
         link_name: Symbol,
         first: SpanData,
@@ -77,7 +78,8 @@ impl fmt::Display for TerminationInfo {
             StackedBorrowsUb { msg, .. } => write!(f, "{msg}"),
             TreeBorrowsUb { title, .. } => write!(f, "{title}"),
             Deadlock => write!(f, "the evaluated program deadlocked"),
-            GenmcStuckExecution => write!(f, "GenMC determined that the execution got stuck"),
+            GenmcBlockedExecution =>
+                write!(f, "GenMC determined that the execution got blocked (this is not an error)"),
             MultipleSymbolDefinitions { link_name, .. } =>
                 write!(f, "multiple definitions of symbol `{link_name}`"),
             SymbolShimClashing { link_name, .. } =>
@@ -139,6 +141,13 @@ pub enum NonHaltingDiagnostic {
         ptr: Pointer,
     },
     ExternTypeReborrow,
+    GenmcCompareExchangeWeak,
+    GenmcCompareExchangeOrderingMismatch {
+        success_ordering: AtomicRwOrd,
+        upgraded_success_ordering: AtomicRwOrd,
+        failure_ordering: AtomicReadOrd,
+        effective_failure_ordering: AtomicReadOrd,
+    },
 }
 
 /// Level of Miri specific diagnostics
@@ -243,11 +252,12 @@ pub fn report_error<'tcx>(
                 labels.push(format!("this thread got stuck here"));
                 None
             }
-            GenmcStuckExecution => {
-                // This case should only happen in GenMC mode. We treat it like a normal program exit.
+            GenmcBlockedExecution => {
+                // This case should only happen in GenMC mode.
                 assert!(ecx.machine.data_race.as_genmc_ref().is_some());
-                tracing::info!("GenMC: found stuck execution");
-                return Some((0, true));
+                // The program got blocked by GenMC without finishing the execution.
+                // No cleanup code was executed, so we don't do any leak checks.
+                return Some((0, false));
             }
             MultipleSymbolDefinitions { .. } | SymbolShimClashing { .. } => None,
         };
@@ -282,7 +292,8 @@ pub fn report_error<'tcx>(
             },
             TreeBorrowsUb { title: _, details, history } => {
                 let mut helps = vec![
-                    note!("this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental")
+                    note!("this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental"),
+                    note!("see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information"),
                 ];
                 for m in details {
                     helps.push(note!("{m}"));
@@ -633,6 +644,8 @@ impl<'tcx> MiriMachine<'tcx> {
                 ("sharing memory with a native function".to_string(), DiagLevel::Warning),
             ExternTypeReborrow =>
                 ("reborrow of reference to `extern type`".to_string(), DiagLevel::Warning),
+            GenmcCompareExchangeWeak | GenmcCompareExchangeOrderingMismatch { .. } =>
+                ("GenMC might miss possible behaviors of this code".to_string(), DiagLevel::Warning),
             CreatedPointerTag(..)
             | PoppedPointerTag(..)
             | CreatedAlloc(..)
@@ -671,6 +684,23 @@ impl<'tcx> MiriMachine<'tcx> {
                 format!("weak memory emulation: outdated value returned from load at {ptr}"),
             ExternTypeReborrow =>
                 format!("reborrow of a reference to `extern type` is not properly supported"),
+            GenmcCompareExchangeWeak =>
+                "GenMC currently does not model spurious failures of `compare_exchange_weak`. Miri with GenMC might miss bugs related to spurious failures."
+                    .to_string(),
+            GenmcCompareExchangeOrderingMismatch {
+                success_ordering,
+                upgraded_success_ordering,
+                failure_ordering,
+                effective_failure_ordering,
+            } => {
+                let was_upgraded_msg = if success_ordering != upgraded_success_ordering {
+                    format!("Success ordering '{success_ordering:?}' was upgraded to '{upgraded_success_ordering:?}' to match failure ordering '{failure_ordering:?}'")
+                } else {
+                    assert_ne!(failure_ordering, effective_failure_ordering);
+                    format!("Due to success ordering '{success_ordering:?}', the failure ordering '{failure_ordering:?}' is treated like '{effective_failure_ordering:?}'")
+                };
+                format!("GenMC currently does not model the failure ordering for `compare_exchange`. {was_upgraded_msg}. Miri with GenMC might miss bugs related to this memory access.")
+            }
         };
 
         let notes = match &e {
diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs
index 4c531a8d1f5..82ca38c3752 100644
--- a/src/tools/miri/src/eval.rs
+++ b/src/tools/miri/src/eval.rs
@@ -32,65 +32,6 @@ pub enum MiriEntryFnType {
 /// will hang the program.
 const MAIN_THREAD_YIELDS_AT_SHUTDOWN: u32 = 256;
 
-#[derive(Copy, Clone, Debug, PartialEq)]
-pub enum AlignmentCheck {
-    /// Do not check alignment.
-    None,
-    /// Check alignment "symbolically", i.e., using only the requested alignment for an allocation and not its real base address.
-    Symbolic,
-    /// Check alignment on the actual physical integer address.
-    Int,
-}
-
-#[derive(Copy, Clone, Debug, PartialEq)]
-pub enum RejectOpWith {
-    /// Isolated op is rejected with an abort of the machine.
-    Abort,
-
-    /// If not Abort, miri returns an error for an isolated op.
-    /// Following options determine if user should be warned about such error.
-    /// Do not print warning about rejected isolated op.
-    NoWarning,
-
-    /// Print a warning about rejected isolated op, with backtrace.
-    Warning,
-
-    /// Print a warning about rejected isolated op, without backtrace.
-    WarningWithoutBacktrace,
-}
-
-#[derive(Copy, Clone, Debug, PartialEq)]
-pub enum IsolatedOp {
-    /// Reject an op requiring communication with the host. By
-    /// default, miri rejects the op with an abort. If not, it returns
-    /// an error code, and prints a warning about it. Warning levels
-    /// are controlled by `RejectOpWith` enum.
-    Reject(RejectOpWith),
-
-    /// Execute op requiring communication with the host, i.e. disable isolation.
-    Allow,
-}
-
-#[derive(Debug, Copy, Clone, PartialEq, Eq)]
-pub enum BacktraceStyle {
-    /// Prints a terser backtrace which ideally only contains relevant information.
-    Short,
-    /// Prints a backtrace with all possible information.
-    Full,
-    /// Prints only the frame that the error occurs in.
-    Off,
-}
-
-#[derive(Debug, Copy, Clone, PartialEq, Eq)]
-pub enum ValidationMode {
-    /// Do not perform any kind of validation.
-    No,
-    /// Validate the interior of the value, but not things behind references.
-    Shallow,
-    /// Fully recursively validate references.
-    Deep,
-}
-
 /// Configuration needed to spawn a Miri instance.
 #[derive(Clone)]
 pub struct MiriConfig {
@@ -171,7 +112,9 @@ pub struct MiriConfig {
     /// Whether floating-point operations can behave non-deterministically.
     pub float_nondet: bool,
     /// Whether floating-point operations can have a non-deterministic rounding error.
-    pub float_rounding_error: bool,
+    pub float_rounding_error: FloatRoundingErrorMode,
+    /// Whether Miri artifically introduces short reads/writes on file descriptors.
+    pub short_fd_operations: bool,
 }
 
 impl Default for MiriConfig {
@@ -213,7 +156,8 @@ impl Default for MiriConfig {
             fixed_scheduling: false,
             force_intrinsic_fallback: false,
             float_nondet: true,
-            float_rounding_error: true,
+            float_rounding_error: FloatRoundingErrorMode::Random,
+            short_fd_operations: true,
         }
     }
 }
@@ -303,8 +247,21 @@ impl<'tcx> MainThreadState<'tcx> {
                 // to be like a global `static`, so that all memory reached by it is considered to "not leak".
                 this.terminate_active_thread(TlsAllocAction::Leak)?;
 
-                // Stop interpreter loop.
-                throw_machine_stop!(TerminationInfo::Exit { code: exit_code, leak_check: true });
+                // In GenMC mode, we do not immediately stop execution on main thread exit.
+                if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() {
+                    // If there's no error, execution will continue (on another thread).
+                    genmc_ctx.handle_exit(
+                        ThreadId::MAIN_THREAD,
+                        exit_code,
+                        crate::concurrency::ExitType::MainThreadFinish,
+                    )?;
+                } else {
+                    // Stop interpreter loop.
+                    throw_machine_stop!(TerminationInfo::Exit {
+                        code: exit_code,
+                        leak_check: true
+                    });
+                }
             }
         }
         interp_ok(Poll::Pending)
@@ -505,10 +462,6 @@ pub fn eval_entry<'tcx>(
     // Copy setting before we move `config`.
     let ignore_leaks = config.ignore_leaks;
 
-    if let Some(genmc_ctx) = &genmc_ctx {
-        genmc_ctx.handle_execution_start();
-    }
-
     let mut ecx = match create_ecx(tcx, entry_id, entry_type, config, genmc_ctx).report_err() {
         Ok(v) => v,
         Err(err) => {
@@ -532,15 +485,6 @@ pub fn eval_entry<'tcx>(
     // Show diagnostic, if any.
     let (return_code, leak_check) = report_error(&ecx, err)?;
 
-    // We inform GenMC that the execution is complete.
-    if let Some(genmc_ctx) = ecx.machine.data_race.as_genmc_ref()
-        && let Err(error) = genmc_ctx.handle_execution_end(&ecx)
-    {
-        // FIXME(GenMC): Improve error reporting.
-        tcx.dcx().err(format!("GenMC returned an error: \"{error}\""));
-        return None;
-    }
-
     // If we get here there was no fatal error.
 
     // Possibly check for memory leaks.
diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs
index 43cb1c9ae05..e0c077e9931 100644
--- a/src/tools/miri/src/helpers.rs
+++ b/src/tools/miri/src/helpers.rs
@@ -32,6 +32,8 @@ pub enum AccessKind {
 ///
 /// A `None` namespace indicates we are looking for a module.
 fn try_resolve_did(tcx: TyCtxt<'_>, path: &[&str], namespace: Option<Namespace>) -> Option<DefId> {
+    let _trace = enter_trace_span!("try_resolve_did", ?path);
+
     /// Yield all children of the given item, that have the given name.
     fn find_children<'tcx: 'a, 'a>(
         tcx: TyCtxt<'tcx>,
@@ -133,7 +135,6 @@ pub fn iter_exported_symbols<'tcx>(
         let exported = tcx.def_kind(def_id).has_codegen_attrs() && {
             let codegen_attrs = tcx.codegen_fn_attrs(def_id);
             codegen_attrs.contains_extern_indicator()
-                || codegen_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
                 || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_COMPILER)
                 || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER)
         };
diff --git a/src/tools/miri/src/intrinsics/atomic.rs b/src/tools/miri/src/intrinsics/atomic.rs
index e6341252927..9bb0ab70de2 100644
--- a/src/tools/miri/src/intrinsics/atomic.rs
+++ b/src/tools/miri/src/intrinsics/atomic.rs
@@ -5,10 +5,13 @@ use rustc_middle::{mir, ty};
 use super::check_intrinsic_arg_count;
 use crate::*;
 
-pub enum AtomicOp {
-    /// The `bool` indicates whether the result of the operation should be negated (`UnOp::Not`,
-    /// must be a boolean-typed operation).
-    MirOp(mir::BinOp, bool),
+pub enum AtomicRmwOp {
+    MirOp {
+        op: mir::BinOp,
+        /// Indicates whether the result of the operation should be negated (`UnOp::Not`, must be a
+        /// boolean-typed operation).
+        neg: bool,
+    },
     Max,
     Min,
 }
@@ -106,55 +109,85 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
             "or" => {
                 let ord = get_ord_at(2);
-                this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitOr, false), rw_ord(ord))?;
+                this.atomic_rmw_op(
+                    args,
+                    dest,
+                    AtomicRmwOp::MirOp { op: BinOp::BitOr, neg: false },
+                    rw_ord(ord),
+                )?;
             }
             "xor" => {
                 let ord = get_ord_at(2);
-                this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitXor, false), rw_ord(ord))?;
+                this.atomic_rmw_op(
+                    args,
+                    dest,
+                    AtomicRmwOp::MirOp { op: BinOp::BitXor, neg: false },
+                    rw_ord(ord),
+                )?;
             }
             "and" => {
                 let ord = get_ord_at(2);
-                this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, false), rw_ord(ord))?;
+                this.atomic_rmw_op(
+                    args,
+                    dest,
+                    AtomicRmwOp::MirOp { op: BinOp::BitAnd, neg: false },
+                    rw_ord(ord),
+                )?;
             }
             "nand" => {
                 let ord = get_ord_at(2);
-                this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::BitAnd, true), rw_ord(ord))?;
+                this.atomic_rmw_op(
+                    args,
+                    dest,
+                    AtomicRmwOp::MirOp { op: BinOp::BitAnd, neg: true },
+                    rw_ord(ord),
+                )?;
             }
             "xadd" => {
                 let ord = get_ord_at(2);
-                this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::Add, false), rw_ord(ord))?;
+                this.atomic_rmw_op(
+                    args,
+                    dest,
+                    AtomicRmwOp::MirOp { op: BinOp::Add, neg: false },
+                    rw_ord(ord),
+                )?;
             }
             "xsub" => {
                 let ord = get_ord_at(2);
-                this.atomic_rmw_op(args, dest, AtomicOp::MirOp(BinOp::Sub, false), rw_ord(ord))?;
+                this.atomic_rmw_op(
+                    args,
+                    dest,
+                    AtomicRmwOp::MirOp { op: BinOp::Sub, neg: false },
+                    rw_ord(ord),
+                )?;
             }
             "min" => {
                 let ord = get_ord_at(1);
                 // Later we will use the type to indicate signed vs unsigned,
                 // so make sure it matches the intrinsic name.
                 assert!(matches!(args[1].layout.ty.kind(), ty::Int(_)));
-                this.atomic_rmw_op(args, dest, AtomicOp::Min, rw_ord(ord))?;
+                this.atomic_rmw_op(args, dest, AtomicRmwOp::Min, rw_ord(ord))?;
             }
             "umin" => {
                 let ord = get_ord_at(1);
                 // Later we will use the type to indicate signed vs unsigned,
                 // so make sure it matches the intrinsic name.
                 assert!(matches!(args[1].layout.ty.kind(), ty::Uint(_)));
-                this.atomic_rmw_op(args, dest, AtomicOp::Min, rw_ord(ord))?;
+                this.atomic_rmw_op(args, dest, AtomicRmwOp::Min, rw_ord(ord))?;
             }
             "max" => {
                 let ord = get_ord_at(1);
                 // Later we will use the type to indicate signed vs unsigned,
                 // so make sure it matches the intrinsic name.
                 assert!(matches!(args[1].layout.ty.kind(), ty::Int(_)));
-                this.atomic_rmw_op(args, dest, AtomicOp::Max, rw_ord(ord))?;
+                this.atomic_rmw_op(args, dest, AtomicRmwOp::Max, rw_ord(ord))?;
             }
             "umax" => {
                 let ord = get_ord_at(1);
                 // Later we will use the type to indicate signed vs unsigned,
                 // so make sure it matches the intrinsic name.
                 assert!(matches!(args[1].layout.ty.kind(), ty::Uint(_)));
-                this.atomic_rmw_op(args, dest, AtomicOp::Max, rw_ord(ord))?;
+                this.atomic_rmw_op(args, dest, AtomicRmwOp::Max, rw_ord(ord))?;
             }
 
             _ => return interp_ok(EmulateItemResult::NotSupported),
@@ -222,8 +255,8 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
         &mut self,
         args: &[OpTy<'tcx>],
         dest: &MPlaceTy<'tcx>,
-        atomic_op: AtomicOp,
-        atomic: AtomicRwOrd,
+        atomic_op: AtomicRmwOp,
+        ord: AtomicRwOrd,
     ) -> InterpResult<'tcx> {
         let this = self.eval_context_mut();
 
@@ -231,8 +264,9 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
         let place = this.deref_pointer(place)?;
         let rhs = this.read_immediate(rhs)?;
 
+        // The LHS can be a pointer, the RHS must be an integer.
         if !(place.layout.ty.is_integral() || place.layout.ty.is_raw_ptr())
-            || !(rhs.layout.ty.is_integral() || rhs.layout.ty.is_raw_ptr())
+            || !rhs.layout.ty.is_integral()
         {
             span_bug!(
                 this.cur_span(),
@@ -240,14 +274,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
             );
         }
 
-        let old = match atomic_op {
-            AtomicOp::Min =>
-                this.atomic_min_max_scalar(&place, rhs, /* min */ true, atomic)?,
-            AtomicOp::Max =>
-                this.atomic_min_max_scalar(&place, rhs, /* min */ false, atomic)?,
-            AtomicOp::MirOp(op, not) =>
-                this.atomic_rmw_op_immediate(&place, &rhs, op, not, atomic)?,
-        };
+        let old = this.atomic_rmw_op_immediate(&place, &rhs, atomic_op, ord)?;
         this.write_immediate(*old, dest)?; // old value is returned
         interp_ok(())
     }
diff --git a/src/tools/miri/src/intrinsics/math.rs b/src/tools/miri/src/intrinsics/math.rs
new file mode 100644
index 00000000000..b9c99f28594
--- /dev/null
+++ b/src/tools/miri/src/intrinsics/math.rs
@@ -0,0 +1,313 @@
+use rand::Rng;
+use rustc_apfloat::{self, Float, FloatConvert, Round};
+use rustc_middle::mir;
+use rustc_middle::ty::{self, FloatTy};
+
+use self::helpers::{ToHost, ToSoft};
+use super::check_intrinsic_arg_count;
+use crate::*;
+
+fn sqrt<'tcx, F: Float + FloatConvert<F> + Into<Scalar>>(
+    this: &mut MiriInterpCx<'tcx>,
+    args: &[OpTy<'tcx>],
+    dest: &MPlaceTy<'tcx>,
+) -> InterpResult<'tcx> {
+    let [f] = check_intrinsic_arg_count(args)?;
+    let f = this.read_scalar(f)?;
+    let f: F = f.to_float()?;
+    // Sqrt is specified to be fully precise.
+    let res = math::sqrt(f);
+    let res = this.adjust_nan(res, &[f]);
+    this.write_scalar(res, dest)
+}
+
+impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
+pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
+    fn emulate_math_intrinsic(
+        &mut self,
+        intrinsic_name: &str,
+        _generic_args: ty::GenericArgsRef<'tcx>,
+        args: &[OpTy<'tcx>],
+        dest: &MPlaceTy<'tcx>,
+    ) -> InterpResult<'tcx, EmulateItemResult> {
+        let this = self.eval_context_mut();
+
+        match intrinsic_name {
+            // Operations we can do with soft-floats.
+            "sqrtf16" => sqrt::<rustc_apfloat::ieee::Half>(this, args, dest)?,
+            "sqrtf32" => sqrt::<rustc_apfloat::ieee::Single>(this, args, dest)?,
+            "sqrtf64" => sqrt::<rustc_apfloat::ieee::Double>(this, args, dest)?,
+            "sqrtf128" => sqrt::<rustc_apfloat::ieee::Quad>(this, args, dest)?,
+
+            "fmaf32" => {
+                let [a, b, c] = check_intrinsic_arg_count(args)?;
+                let a = this.read_scalar(a)?.to_f32()?;
+                let b = this.read_scalar(b)?.to_f32()?;
+                let c = this.read_scalar(c)?.to_f32()?;
+                let res = a.mul_add(b, c).value;
+                let res = this.adjust_nan(res, &[a, b, c]);
+                this.write_scalar(res, dest)?;
+            }
+            "fmaf64" => {
+                let [a, b, c] = check_intrinsic_arg_count(args)?;
+                let a = this.read_scalar(a)?.to_f64()?;
+                let b = this.read_scalar(b)?.to_f64()?;
+                let c = this.read_scalar(c)?.to_f64()?;
+                let res = a.mul_add(b, c).value;
+                let res = this.adjust_nan(res, &[a, b, c]);
+                this.write_scalar(res, dest)?;
+            }
+
+            "fmuladdf32" => {
+                let [a, b, c] = check_intrinsic_arg_count(args)?;
+                let a = this.read_scalar(a)?.to_f32()?;
+                let b = this.read_scalar(b)?.to_f32()?;
+                let c = this.read_scalar(c)?.to_f32()?;
+                let fuse: bool = this.machine.float_nondet && this.machine.rng.get_mut().random();
+                let res = if fuse { a.mul_add(b, c).value } else { ((a * b).value + c).value };
+                let res = this.adjust_nan(res, &[a, b, c]);
+                this.write_scalar(res, dest)?;
+            }
+            "fmuladdf64" => {
+                let [a, b, c] = check_intrinsic_arg_count(args)?;
+                let a = this.read_scalar(a)?.to_f64()?;
+                let b = this.read_scalar(b)?.to_f64()?;
+                let c = this.read_scalar(c)?.to_f64()?;
+                let fuse: bool = this.machine.float_nondet && this.machine.rng.get_mut().random();
+                let res = if fuse { a.mul_add(b, c).value } else { ((a * b).value + c).value };
+                let res = this.adjust_nan(res, &[a, b, c]);
+                this.write_scalar(res, dest)?;
+            }
+
+            #[rustfmt::skip]
+            | "fadd_fast"
+            | "fsub_fast"
+            | "fmul_fast"
+            | "fdiv_fast"
+            | "frem_fast"
+            => {
+                let [a, b] = check_intrinsic_arg_count(args)?;
+                let a = this.read_immediate(a)?;
+                let b = this.read_immediate(b)?;
+                let op = match intrinsic_name {
+                    "fadd_fast" => mir::BinOp::Add,
+                    "fsub_fast" => mir::BinOp::Sub,
+                    "fmul_fast" => mir::BinOp::Mul,
+                    "fdiv_fast" => mir::BinOp::Div,
+                    "frem_fast" => mir::BinOp::Rem,
+                    _ => bug!(),
+                };
+                let float_finite = |x: &ImmTy<'tcx>| -> InterpResult<'tcx, bool> {
+                    let ty::Float(fty) = x.layout.ty.kind() else {
+                        bug!("float_finite: non-float input type {}", x.layout.ty)
+                    };
+                    interp_ok(match fty {
+                        FloatTy::F16 => x.to_scalar().to_f16()?.is_finite(),
+                        FloatTy::F32 => x.to_scalar().to_f32()?.is_finite(),
+                        FloatTy::F64 => x.to_scalar().to_f64()?.is_finite(),
+                        FloatTy::F128 => x.to_scalar().to_f128()?.is_finite(),
+                    })
+                };
+                match (float_finite(&a)?, float_finite(&b)?) {
+                    (false, false) => throw_ub_format!(
+                        "`{intrinsic_name}` intrinsic called with non-finite value as both parameters",
+                    ),
+                    (false, _) => throw_ub_format!(
+                        "`{intrinsic_name}` intrinsic called with non-finite value as first parameter",
+                    ),
+                    (_, false) => throw_ub_format!(
+                        "`{intrinsic_name}` intrinsic called with non-finite value as second parameter",
+                    ),
+                    _ => {}
+                }
+                let res = this.binary_op(op, &a, &b)?;
+                // This cannot be a NaN so we also don't have to apply any non-determinism.
+                // (Also, `binary_op` already called `generate_nan` if needed.)
+                if !float_finite(&res)? {
+                    throw_ub_format!("`{intrinsic_name}` intrinsic produced non-finite value as result");
+                }
+                // Apply a relative error of 4ULP to simulate non-deterministic precision loss
+                // due to optimizations.
+                let res = math::apply_random_float_error_to_imm(this, res, 4)?;
+                this.write_immediate(*res, dest)?;
+            }
+
+            "float_to_int_unchecked" => {
+                let [val] = check_intrinsic_arg_count(args)?;
+                let val = this.read_immediate(val)?;
+
+                let res = this
+                    .float_to_int_checked(&val, dest.layout, Round::TowardZero)?
+                    .ok_or_else(|| {
+                        err_ub_format!(
+                            "`float_to_int_unchecked` intrinsic called on {val} which cannot be represented in target type `{:?}`",
+                            dest.layout.ty
+                        )
+                    })?;
+
+                this.write_immediate(*res, dest)?;
+            }
+
+            // Operations that need host floats.
+            #[rustfmt::skip]
+            | "sinf32"
+            | "cosf32"
+            | "expf32"
+            | "exp2f32"
+            | "logf32"
+            | "log10f32"
+            | "log2f32"
+            => {
+                let [f] = check_intrinsic_arg_count(args)?;
+                let f = this.read_scalar(f)?.to_f32()?;
+
+                let res = math::fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| {
+                    // Using host floats (but it's fine, these operations do not have
+                    // guaranteed precision).
+                    let host = f.to_host();
+                    let res = match intrinsic_name {
+                        "sinf32" => host.sin(),
+                        "cosf32" => host.cos(),
+                        "expf32" => host.exp(),
+                        "exp2f32" => host.exp2(),
+                        "logf32" => host.ln(),
+                        "log10f32" => host.log10(),
+                        "log2f32" => host.log2(),
+                        _ => bug!(),
+                    };
+                    let res = res.to_soft();
+
+                    // Apply a relative error of 4ULP to introduce some non-determinism
+                    // simulating imprecise implementations and optimizations.
+                    let res = math::apply_random_float_error_ulp(
+                        this,
+                        res,
+                        4,
+                    );
+
+                    // Clamp the result to the guaranteed range of this function according to the C standard,
+                    // if any.
+                    math::clamp_float_value(intrinsic_name, res)
+                });
+                let res = this.adjust_nan(res, &[f]);
+                this.write_scalar(res, dest)?;
+            }
+
+            #[rustfmt::skip]
+            | "sinf64"
+            | "cosf64"
+            | "expf64"
+            | "exp2f64"
+            | "logf64"
+            | "log10f64"
+            | "log2f64"
+            => {
+                let [f] = check_intrinsic_arg_count(args)?;
+                let f = this.read_scalar(f)?.to_f64()?;
+
+                let res = math::fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| {
+                    // Using host floats (but it's fine, these operations do not have
+                    // guaranteed precision).
+                    let host = f.to_host();
+                    let res = match intrinsic_name {
+                        "sinf64" => host.sin(),
+                        "cosf64" => host.cos(),
+                        "expf64" => host.exp(),
+                        "exp2f64" => host.exp2(),
+                        "logf64" => host.ln(),
+                        "log10f64" => host.log10(),
+                        "log2f64" => host.log2(),
+                        _ => bug!(),
+                    };
+                    let res = res.to_soft();
+
+                    // Apply a relative error of 4ULP to introduce some non-determinism
+                    // simulating imprecise implementations and optimizations.
+                    let res = math::apply_random_float_error_ulp(
+                        this,
+                        res,
+                        4,
+                    );
+
+                    // Clamp the result to the guaranteed range of this function according to the C standard,
+                    // if any.
+                    math::clamp_float_value(intrinsic_name, res)
+                });
+                let res = this.adjust_nan(res, &[f]);
+                this.write_scalar(res, dest)?;
+            }
+
+            "powf32" => {
+                let [f1, f2] = check_intrinsic_arg_count(args)?;
+                let f1 = this.read_scalar(f1)?.to_f32()?;
+                let f2 = this.read_scalar(f2)?.to_f32()?;
+
+                let res =
+                    math::fixed_float_value(this, intrinsic_name, &[f1, f2]).unwrap_or_else(|| {
+                        // Using host floats (but it's fine, this operation does not have guaranteed precision).
+                        let res = f1.to_host().powf(f2.to_host()).to_soft();
+
+                        // Apply a relative error of 4ULP to introduce some non-determinism
+                        // simulating imprecise implementations and optimizations.
+                        math::apply_random_float_error_ulp(this, res, 4)
+                    });
+                let res = this.adjust_nan(res, &[f1, f2]);
+                this.write_scalar(res, dest)?;
+            }
+            "powf64" => {
+                let [f1, f2] = check_intrinsic_arg_count(args)?;
+                let f1 = this.read_scalar(f1)?.to_f64()?;
+                let f2 = this.read_scalar(f2)?.to_f64()?;
+
+                let res =
+                    math::fixed_float_value(this, intrinsic_name, &[f1, f2]).unwrap_or_else(|| {
+                        // Using host floats (but it's fine, this operation does not have guaranteed precision).
+                        let res = f1.to_host().powf(f2.to_host()).to_soft();
+
+                        // Apply a relative error of 4ULP to introduce some non-determinism
+                        // simulating imprecise implementations and optimizations.
+                        math::apply_random_float_error_ulp(this, res, 4)
+                    });
+                let res = this.adjust_nan(res, &[f1, f2]);
+                this.write_scalar(res, dest)?;
+            }
+
+            "powif32" => {
+                let [f, i] = check_intrinsic_arg_count(args)?;
+                let f = this.read_scalar(f)?.to_f32()?;
+                let i = this.read_scalar(i)?.to_i32()?;
+
+                let res = math::fixed_powi_value(this, f, i).unwrap_or_else(|| {
+                    // Using host floats (but it's fine, this operation does not have guaranteed precision).
+                    let res = f.to_host().powi(i).to_soft();
+
+                    // Apply a relative error of 4ULP to introduce some non-determinism
+                    // simulating imprecise implementations and optimizations.
+                    math::apply_random_float_error_ulp(this, res, 4)
+                });
+                let res = this.adjust_nan(res, &[f]);
+                this.write_scalar(res, dest)?;
+            }
+            "powif64" => {
+                let [f, i] = check_intrinsic_arg_count(args)?;
+                let f = this.read_scalar(f)?.to_f64()?;
+                let i = this.read_scalar(i)?.to_i32()?;
+
+                let res = math::fixed_powi_value(this, f, i).unwrap_or_else(|| {
+                    // Using host floats (but it's fine, this operation does not have guaranteed precision).
+                    let res = f.to_host().powi(i).to_soft();
+
+                    // Apply a relative error of 4ULP to introduce some non-determinism
+                    // simulating imprecise implementations and optimizations.
+                    math::apply_random_float_error_ulp(this, res, 4)
+                });
+                let res = this.adjust_nan(res, &[f]);
+                this.write_scalar(res, dest)?;
+            }
+
+            _ => return interp_ok(EmulateItemResult::NotSupported),
+        }
+
+        interp_ok(EmulateItemResult::NeedsReturn)
+    }
+}
diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs
index b5e81460773..a80b939d84e 100644
--- a/src/tools/miri/src/intrinsics/mod.rs
+++ b/src/tools/miri/src/intrinsics/mod.rs
@@ -1,22 +1,20 @@
 #![warn(clippy::arithmetic_side_effects)]
 
 mod atomic;
+mod math;
 mod simd;
 
-use std::ops::Neg;
+pub use self::atomic::AtomicRmwOp;
 
+#[rustfmt::skip] // prevent `use` reordering
 use rand::Rng;
 use rustc_abi::Size;
-use rustc_apfloat::ieee::{IeeeFloat, Semantics};
-use rustc_apfloat::{self, Float, Round};
-use rustc_middle::mir;
-use rustc_middle::ty::{self, FloatTy, ScalarInt};
+use rustc_middle::{mir, ty};
 use rustc_span::{Symbol, sym};
 
 use self::atomic::EvalContextExt as _;
-use self::helpers::{ToHost, ToSoft};
+use self::math::EvalContextExt as _;
 use self::simd::EvalContextExt as _;
-use crate::math::{IeeeExt, apply_random_float_error_ulp};
 use crate::*;
 
 /// Check that the number of args is what we expect.
@@ -180,294 +178,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 this.write_scalar(Scalar::from_bool(branch), dest)?;
             }
 
-            "sqrtf32" => {
-                let [f] = check_intrinsic_arg_count(args)?;
-                let f = this.read_scalar(f)?.to_f32()?;
-                // Sqrt is specified to be fully precise.
-                let res = math::sqrt(f);
-                let res = this.adjust_nan(res, &[f]);
-                this.write_scalar(res, dest)?;
-            }
-            "sqrtf64" => {
-                let [f] = check_intrinsic_arg_count(args)?;
-                let f = this.read_scalar(f)?.to_f64()?;
-                // Sqrt is specified to be fully precise.
-                let res = math::sqrt(f);
-                let res = this.adjust_nan(res, &[f]);
-                this.write_scalar(res, dest)?;
-            }
-
-            #[rustfmt::skip]
-            | "sinf32"
-            | "cosf32"
-            | "expf32"
-            | "exp2f32"
-            | "logf32"
-            | "log10f32"
-            | "log2f32"
-            => {
-                let [f] = check_intrinsic_arg_count(args)?;
-                let f = this.read_scalar(f)?.to_f32()?;
-
-                let res = fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| {
-                    // Using host floats (but it's fine, these operations do not have
-                    // guaranteed precision).
-                    let host = f.to_host();
-                    let res = match intrinsic_name {
-                        "sinf32" => host.sin(),
-                        "cosf32" => host.cos(),
-                        "expf32" => host.exp(),
-                        "exp2f32" => host.exp2(),
-                        "logf32" => host.ln(),
-                        "log10f32" => host.log10(),
-                        "log2f32" => host.log2(),
-                        _ => bug!(),
-                    };
-                    let res = res.to_soft();
-
-                    // Apply a relative error of 4ULP to introduce some non-determinism
-                    // simulating imprecise implementations and optimizations.
-                    let res = apply_random_float_error_ulp(
-                        this,
-                        res,
-                        2, // log2(4)
-                    );
-
-                    // Clamp the result to the guaranteed range of this function according to the C standard,
-                    // if any.
-                    clamp_float_value(intrinsic_name, res)
-                });
-                let res = this.adjust_nan(res, &[f]);
-                this.write_scalar(res, dest)?;
-            }
-
-            #[rustfmt::skip]
-            | "sinf64"
-            | "cosf64"
-            | "expf64"
-            | "exp2f64"
-            | "logf64"
-            | "log10f64"
-            | "log2f64"
-            => {
-                let [f] = check_intrinsic_arg_count(args)?;
-                let f = this.read_scalar(f)?.to_f64()?;
-
-                let res = fixed_float_value(this, intrinsic_name, &[f]).unwrap_or_else(|| {
-                    // Using host floats (but it's fine, these operations do not have
-                    // guaranteed precision).
-                    let host = f.to_host();
-                    let res = match intrinsic_name {
-                        "sinf64" => host.sin(),
-                        "cosf64" => host.cos(),
-                        "expf64" => host.exp(),
-                        "exp2f64" => host.exp2(),
-                        "logf64" => host.ln(),
-                        "log10f64" => host.log10(),
-                        "log2f64" => host.log2(),
-                        _ => bug!(),
-                    };
-                    let res = res.to_soft();
-
-                    // Apply a relative error of 4ULP to introduce some non-determinism
-                    // simulating imprecise implementations and optimizations.
-                    let res = apply_random_float_error_ulp(
-                        this,
-                        res,
-                        2, // log2(4)
-                    );
-
-                    // Clamp the result to the guaranteed range of this function according to the C standard,
-                    // if any.
-                    clamp_float_value(intrinsic_name, res)
-                });
-                let res = this.adjust_nan(res, &[f]);
-                this.write_scalar(res, dest)?;
-            }
-
-            "fmaf32" => {
-                let [a, b, c] = check_intrinsic_arg_count(args)?;
-                let a = this.read_scalar(a)?.to_f32()?;
-                let b = this.read_scalar(b)?.to_f32()?;
-                let c = this.read_scalar(c)?.to_f32()?;
-                let res = a.mul_add(b, c).value;
-                let res = this.adjust_nan(res, &[a, b, c]);
-                this.write_scalar(res, dest)?;
-            }
-            "fmaf64" => {
-                let [a, b, c] = check_intrinsic_arg_count(args)?;
-                let a = this.read_scalar(a)?.to_f64()?;
-                let b = this.read_scalar(b)?.to_f64()?;
-                let c = this.read_scalar(c)?.to_f64()?;
-                let res = a.mul_add(b, c).value;
-                let res = this.adjust_nan(res, &[a, b, c]);
-                this.write_scalar(res, dest)?;
-            }
-
-            "fmuladdf32" => {
-                let [a, b, c] = check_intrinsic_arg_count(args)?;
-                let a = this.read_scalar(a)?.to_f32()?;
-                let b = this.read_scalar(b)?.to_f32()?;
-                let c = this.read_scalar(c)?.to_f32()?;
-                let fuse: bool = this.machine.float_nondet && this.machine.rng.get_mut().random();
-                let res = if fuse { a.mul_add(b, c).value } else { ((a * b).value + c).value };
-                let res = this.adjust_nan(res, &[a, b, c]);
-                this.write_scalar(res, dest)?;
-            }
-            "fmuladdf64" => {
-                let [a, b, c] = check_intrinsic_arg_count(args)?;
-                let a = this.read_scalar(a)?.to_f64()?;
-                let b = this.read_scalar(b)?.to_f64()?;
-                let c = this.read_scalar(c)?.to_f64()?;
-                let fuse: bool = this.machine.float_nondet && this.machine.rng.get_mut().random();
-                let res = if fuse { a.mul_add(b, c).value } else { ((a * b).value + c).value };
-                let res = this.adjust_nan(res, &[a, b, c]);
-                this.write_scalar(res, dest)?;
-            }
-
-            "powf32" => {
-                let [f1, f2] = check_intrinsic_arg_count(args)?;
-                let f1 = this.read_scalar(f1)?.to_f32()?;
-                let f2 = this.read_scalar(f2)?.to_f32()?;
-
-                let res = fixed_float_value(this, intrinsic_name, &[f1, f2]).unwrap_or_else(|| {
-                    // Using host floats (but it's fine, this operation does not have guaranteed precision).
-                    let res = f1.to_host().powf(f2.to_host()).to_soft();
-
-                    // Apply a relative error of 4ULP to introduce some non-determinism
-                    // simulating imprecise implementations and optimizations.
-                    apply_random_float_error_ulp(
-                        this, res, 2, // log2(4)
-                    )
-                });
-                let res = this.adjust_nan(res, &[f1, f2]);
-                this.write_scalar(res, dest)?;
-            }
-            "powf64" => {
-                let [f1, f2] = check_intrinsic_arg_count(args)?;
-                let f1 = this.read_scalar(f1)?.to_f64()?;
-                let f2 = this.read_scalar(f2)?.to_f64()?;
-
-                let res = fixed_float_value(this, intrinsic_name, &[f1, f2]).unwrap_or_else(|| {
-                    // Using host floats (but it's fine, this operation does not have guaranteed precision).
-                    let res = f1.to_host().powf(f2.to_host()).to_soft();
-
-                    // Apply a relative error of 4ULP to introduce some non-determinism
-                    // simulating imprecise implementations and optimizations.
-                    apply_random_float_error_ulp(
-                        this, res, 2, // log2(4)
-                    )
-                });
-                let res = this.adjust_nan(res, &[f1, f2]);
-                this.write_scalar(res, dest)?;
-            }
-
-            "powif32" => {
-                let [f, i] = check_intrinsic_arg_count(args)?;
-                let f = this.read_scalar(f)?.to_f32()?;
-                let i = this.read_scalar(i)?.to_i32()?;
-
-                let res = fixed_powi_float_value(this, f, i).unwrap_or_else(|| {
-                    // Using host floats (but it's fine, this operation does not have guaranteed precision).
-                    let res = f.to_host().powi(i).to_soft();
-
-                    // Apply a relative error of 4ULP to introduce some non-determinism
-                    // simulating imprecise implementations and optimizations.
-                    apply_random_float_error_ulp(
-                        this, res, 2, // log2(4)
-                    )
-                });
-                let res = this.adjust_nan(res, &[f]);
-                this.write_scalar(res, dest)?;
-            }
-            "powif64" => {
-                let [f, i] = check_intrinsic_arg_count(args)?;
-                let f = this.read_scalar(f)?.to_f64()?;
-                let i = this.read_scalar(i)?.to_i32()?;
-
-                let res = fixed_powi_float_value(this, f, i).unwrap_or_else(|| {
-                    // Using host floats (but it's fine, this operation does not have guaranteed precision).
-                    let res = f.to_host().powi(i).to_soft();
-
-                    // Apply a relative error of 4ULP to introduce some non-determinism
-                    // simulating imprecise implementations and optimizations.
-                    apply_random_float_error_ulp(
-                        this, res, 2, // log2(4)
-                    )
-                });
-                let res = this.adjust_nan(res, &[f]);
-                this.write_scalar(res, dest)?;
-            }
-
-            #[rustfmt::skip]
-            | "fadd_fast"
-            | "fsub_fast"
-            | "fmul_fast"
-            | "fdiv_fast"
-            | "frem_fast"
-            => {
-                let [a, b] = check_intrinsic_arg_count(args)?;
-                let a = this.read_immediate(a)?;
-                let b = this.read_immediate(b)?;
-                let op = match intrinsic_name {
-                    "fadd_fast" => mir::BinOp::Add,
-                    "fsub_fast" => mir::BinOp::Sub,
-                    "fmul_fast" => mir::BinOp::Mul,
-                    "fdiv_fast" => mir::BinOp::Div,
-                    "frem_fast" => mir::BinOp::Rem,
-                    _ => bug!(),
-                };
-                let float_finite = |x: &ImmTy<'tcx>| -> InterpResult<'tcx, bool> {
-                    let ty::Float(fty) = x.layout.ty.kind() else {
-                        bug!("float_finite: non-float input type {}", x.layout.ty)
-                    };
-                    interp_ok(match fty {
-                        FloatTy::F16 => x.to_scalar().to_f16()?.is_finite(),
-                        FloatTy::F32 => x.to_scalar().to_f32()?.is_finite(),
-                        FloatTy::F64 => x.to_scalar().to_f64()?.is_finite(),
-                        FloatTy::F128 => x.to_scalar().to_f128()?.is_finite(),
-                    })
-                };
-                match (float_finite(&a)?, float_finite(&b)?) {
-                    (false, false) => throw_ub_format!(
-                        "`{intrinsic_name}` intrinsic called with non-finite value as both parameters",
-                    ),
-                    (false, _) => throw_ub_format!(
-                        "`{intrinsic_name}` intrinsic called with non-finite value as first parameter",
-                    ),
-                    (_, false) => throw_ub_format!(
-                        "`{intrinsic_name}` intrinsic called with non-finite value as second parameter",
-                    ),
-                    _ => {}
-                }
-                let res = this.binary_op(op, &a, &b)?;
-                // This cannot be a NaN so we also don't have to apply any non-determinism.
-                // (Also, `binary_op` already called `generate_nan` if needed.)
-                if !float_finite(&res)? {
-                    throw_ub_format!("`{intrinsic_name}` intrinsic produced non-finite value as result");
-                }
-                // Apply a relative error of 4ULP to simulate non-deterministic precision loss
-                // due to optimizations.
-                let res = apply_random_float_error_to_imm(this, res, 2 /* log2(4) */)?;
-                this.write_immediate(*res, dest)?;
-            }
-
-            "float_to_int_unchecked" => {
-                let [val] = check_intrinsic_arg_count(args)?;
-                let val = this.read_immediate(val)?;
-
-                let res = this
-                    .float_to_int_checked(&val, dest.layout, Round::TowardZero)?
-                    .ok_or_else(|| {
-                        err_ub_format!(
-                            "`float_to_int_unchecked` intrinsic called on {val} which cannot be represented in target type `{:?}`",
-                            dest.layout.ty
-                        )
-                    })?;
-
-                this.write_immediate(*res, dest)?;
-            }
-
             // Other
             "breakpoint" => {
                 let [] = check_intrinsic_arg_count(args)?;
@@ -479,139 +189,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 // Make these a NOP, so we get the better Miri-native error messages.
             }
 
-            _ => return interp_ok(EmulateItemResult::NotSupported),
+            _ => return this.emulate_math_intrinsic(intrinsic_name, generic_args, args, dest),
         }
 
         interp_ok(EmulateItemResult::NeedsReturn)
     }
 }
-
-/// Applies a random ULP floating point error to `val` and returns the new value.
-/// So if you want an X ULP error, `ulp_exponent` should be log2(X).
-///
-/// Will fail if `val` is not a floating point number.
-fn apply_random_float_error_to_imm<'tcx>(
-    ecx: &mut MiriInterpCx<'tcx>,
-    val: ImmTy<'tcx>,
-    ulp_exponent: u32,
-) -> InterpResult<'tcx, ImmTy<'tcx>> {
-    let scalar = val.to_scalar_int()?;
-    let res: ScalarInt = match val.layout.ty.kind() {
-        ty::Float(FloatTy::F16) =>
-            apply_random_float_error_ulp(ecx, scalar.to_f16(), ulp_exponent).into(),
-        ty::Float(FloatTy::F32) =>
-            apply_random_float_error_ulp(ecx, scalar.to_f32(), ulp_exponent).into(),
-        ty::Float(FloatTy::F64) =>
-            apply_random_float_error_ulp(ecx, scalar.to_f64(), ulp_exponent).into(),
-        ty::Float(FloatTy::F128) =>
-            apply_random_float_error_ulp(ecx, scalar.to_f128(), ulp_exponent).into(),
-        _ => bug!("intrinsic called with non-float input type"),
-    };
-
-    interp_ok(ImmTy::from_scalar_int(res, val.layout))
-}
-
-/// For the intrinsics:
-/// - sinf32, sinf64
-/// - cosf32, cosf64
-/// - expf32, expf64, exp2f32, exp2f64
-/// - logf32, logf64, log2f32, log2f64, log10f32, log10f64
-/// - powf32, powf64
-///
-/// # Return
-///
-/// Returns `Some(output)` if the `intrinsic` results in a defined fixed `output` specified in the C standard
-/// (specifically, C23 annex F.10)  when given `args` as arguments. Outputs that are unaffected by a relative error
-/// (such as INF and zero) are not handled here, they are assumed to be handled by the underlying
-/// implementation. Returns `None` if no specific value is guaranteed.
-///
-/// # Note
-///
-/// For `powf*` operations of the form:
-///
-/// - `(SNaN)^(±0)`
-/// - `1^(SNaN)`
-///
-/// The result is implementation-defined:
-/// - musl returns for both `1.0`
-/// - glibc returns for both `NaN`
-///
-/// This discrepancy exists because SNaN handling is not consistently defined across platforms,
-/// and the C standard leaves behavior for SNaNs unspecified.
-///
-/// Miri chooses to adhere to both implementations and returns either one of them non-deterministically.
-fn fixed_float_value<S: Semantics>(
-    ecx: &mut MiriInterpCx<'_>,
-    intrinsic_name: &str,
-    args: &[IeeeFloat<S>],
-) -> Option<IeeeFloat<S>> {
-    let one = IeeeFloat::<S>::one();
-    Some(match (intrinsic_name, args) {
-        // cos(+- 0) = 1
-        ("cosf32" | "cosf64", [input]) if input.is_zero() => one,
-
-        // e^0 = 1
-        ("expf32" | "expf64" | "exp2f32" | "exp2f64", [input]) if input.is_zero() => one,
-
-        // (-1)^(±INF) = 1
-        ("powf32" | "powf64", [base, exp]) if *base == -one && exp.is_infinite() => one,
-
-        // 1^y = 1 for any y, even a NaN
-        ("powf32" | "powf64", [base, exp]) if *base == one => {
-            let rng = ecx.machine.rng.get_mut();
-            // SNaN exponents get special treatment: they might return 1, or a NaN.
-            let return_nan = exp.is_signaling() && ecx.machine.float_nondet && rng.random();
-            // Handle both the musl and glibc cases non-deterministically.
-            if return_nan { ecx.generate_nan(args) } else { one }
-        }
-
-        // x^(±0) = 1 for any x, even a NaN
-        ("powf32" | "powf64", [base, exp]) if exp.is_zero() => {
-            let rng = ecx.machine.rng.get_mut();
-            // SNaN bases get special treatment: they might return 1, or a NaN.
-            let return_nan = base.is_signaling() && ecx.machine.float_nondet && rng.random();
-            // Handle both the musl and glibc cases non-deterministically.
-            if return_nan { ecx.generate_nan(args) } else { one }
-        }
-
-        // There are a lot of cases for fixed outputs according to the C Standard, but these are
-        // mainly INF or zero which are not affected by the applied error.
-        _ => return None,
-    })
-}
-
-/// Returns `Some(output)` if `powi` (called `pown` in C) results in a fixed value specified in the
-/// C standard (specifically, C23 annex F.10.4.6) when doing `base^exp`. Otherwise, returns `None`.
-fn fixed_powi_float_value<S: Semantics>(
-    ecx: &mut MiriInterpCx<'_>,
-    base: IeeeFloat<S>,
-    exp: i32,
-) -> Option<IeeeFloat<S>> {
-    Some(match exp {
-        0 => {
-            let one = IeeeFloat::<S>::one();
-            let rng = ecx.machine.rng.get_mut();
-            let return_nan = ecx.machine.float_nondet && rng.random() && base.is_signaling();
-            // For SNaN treatment, we are consistent with `powf`above.
-            // (We wouldn't have two, unlike powf all implementations seem to agree for powi,
-            // but for now we are maximally conservative.)
-            if return_nan { ecx.generate_nan(&[base]) } else { one }
-        }
-
-        _ => return None,
-    })
-}
-
-/// Given an floating-point operation and a floating-point value, clamps the result to the output
-/// range of the given operation.
-fn clamp_float_value<S: Semantics>(intrinsic_name: &str, val: IeeeFloat<S>) -> IeeeFloat<S> {
-    match intrinsic_name {
-        // sin and cos: [-1, 1]
-        "sinf32" | "cosf32" | "sinf64" | "cosf64" =>
-            val.clamp(IeeeFloat::<S>::one().neg(), IeeeFloat::<S>::one()),
-        // exp: [0, +INF]
-        "expf32" | "exp2f32" | "expf64" | "exp2f64" =>
-            IeeeFloat::<S>::maximum(val, IeeeFloat::<S>::ZERO),
-        _ => val,
-    }
-}
diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs
index 5ed6d6b346c..7f5f25b9f66 100644
--- a/src/tools/miri/src/lib.rs
+++ b/src/tools/miri/src/lib.rs
@@ -18,6 +18,7 @@
 #![feature(derive_coerce_pointee)]
 #![feature(arbitrary_self_types)]
 #![feature(iter_advance_by)]
+#![cfg_attr(not(bootstrap), feature(duration_from_nanos_u128))]
 // Configure clippy and other lints
 #![allow(
     clippy::collapsible_else_if,
@@ -132,21 +133,20 @@ pub use crate::concurrency::thread::{
     BlockReason, DynUnblockCallback, EvalContextExt as _, StackEmptyCallback, ThreadId,
     ThreadManager, TimeoutAnchor, TimeoutClock, UnblockKind,
 };
-pub use crate::concurrency::{GenmcConfig, GenmcCtx};
+pub use crate::concurrency::{GenmcConfig, GenmcCtx, run_genmc_mode};
 pub use crate::data_structures::dedup_range_map::DedupRangeMap;
 pub use crate::data_structures::mono_hash_map::MonoHashMap;
 pub use crate::diagnostics::{
     EvalContextExt as _, NonHaltingDiagnostic, TerminationInfo, report_error,
 };
-pub use crate::eval::{
-    AlignmentCheck, BacktraceStyle, IsolatedOp, MiriConfig, MiriEntryFnType, RejectOpWith,
-    ValidationMode, create_ecx, eval_entry,
-};
+pub use crate::eval::{MiriConfig, MiriEntryFnType, create_ecx, eval_entry};
 pub use crate::helpers::{AccessKind, EvalContextExt as _, ToU64 as _, ToUsize as _};
 pub use crate::intrinsics::EvalContextExt as _;
 pub use crate::machine::{
-    AllocExtra, DynMachineCallback, FrameExtra, MachineCallback, MemoryKind, MiriInterpCx,
-    MiriInterpCxExt, MiriMachine, MiriMemoryKind, PrimitiveLayouts, Provenance, ProvenanceExtra,
+    AlignmentCheck, AllocExtra, BacktraceStyle, DynMachineCallback, FloatRoundingErrorMode,
+    FrameExtra, IsolatedOp, MachineCallback, MemoryKind, MiriInterpCx, MiriInterpCxExt,
+    MiriMachine, MiriMemoryKind, PrimitiveLayouts, Provenance, ProvenanceExtra, RejectOpWith,
+    ValidationMode,
 };
 pub use crate::operator::EvalContextExt as _;
 pub use crate::provenance_gc::{EvalContextExt as _, LiveAllocs, VisitProvenance, VisitWith};
diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs
index 00c3373bb0f..04c8bee72c0 100644
--- a/src/tools/miri/src/machine.rs
+++ b/src/tools/miri/src/machine.rs
@@ -12,10 +12,11 @@ use rand::rngs::StdRng;
 use rand::{Rng, SeedableRng};
 use rustc_abi::{Align, ExternAbi, Size};
 use rustc_apfloat::{Float, FloatConvert};
-use rustc_hir::attrs::InlineAttr;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 #[allow(unused)]
 use rustc_data_structures::static_assert_size;
+use rustc_hir::attrs::InlineAttr;
+use rustc_middle::middle::codegen_fn_attrs::TargetFeatureKind;
 use rustc_middle::mir;
 use rustc_middle::query::TyCtxtAt;
 use rustc_middle::ty::layout::{
@@ -48,6 +49,75 @@ pub const SIGRTMAX: i32 = 42;
 /// base address for each evaluation would produce unbounded memory usage.
 const ADDRS_PER_ANON_GLOBAL: usize = 32;
 
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub enum AlignmentCheck {
+    /// Do not check alignment.
+    None,
+    /// Check alignment "symbolically", i.e., using only the requested alignment for an allocation and not its real base address.
+    Symbolic,
+    /// Check alignment on the actual physical integer address.
+    Int,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub enum RejectOpWith {
+    /// Isolated op is rejected with an abort of the machine.
+    Abort,
+
+    /// If not Abort, miri returns an error for an isolated op.
+    /// Following options determine if user should be warned about such error.
+    /// Do not print warning about rejected isolated op.
+    NoWarning,
+
+    /// Print a warning about rejected isolated op, with backtrace.
+    Warning,
+
+    /// Print a warning about rejected isolated op, without backtrace.
+    WarningWithoutBacktrace,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub enum IsolatedOp {
+    /// Reject an op requiring communication with the host. By
+    /// default, miri rejects the op with an abort. If not, it returns
+    /// an error code, and prints a warning about it. Warning levels
+    /// are controlled by `RejectOpWith` enum.
+    Reject(RejectOpWith),
+
+    /// Execute op requiring communication with the host, i.e. disable isolation.
+    Allow,
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum BacktraceStyle {
+    /// Prints a terser backtrace which ideally only contains relevant information.
+    Short,
+    /// Prints a backtrace with all possible information.
+    Full,
+    /// Prints only the frame that the error occurs in.
+    Off,
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum ValidationMode {
+    /// Do not perform any kind of validation.
+    No,
+    /// Validate the interior of the value, but not things behind references.
+    Shallow,
+    /// Fully recursively validate references.
+    Deep,
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq)]
+pub enum FloatRoundingErrorMode {
+    /// Apply a random error (the default).
+    Random,
+    /// Don't apply any error.
+    None,
+    /// Always apply the maximum error (with a random sign).
+    Max,
+}
+
 /// Extra data stored with each stack frame
 pub struct FrameExtra<'tcx> {
     /// Extra data for the Borrow Tracker.
@@ -280,16 +350,16 @@ impl interpret::Provenance for Provenance {
         Ok(())
     }
 
-    fn join(left: Option<Self>, right: Option<Self>) -> Option<Self> {
+    fn join(left: Self, right: Self) -> Option<Self> {
         match (left, right) {
             // If both are the *same* concrete tag, that is the result.
             (
-                Some(Provenance::Concrete { alloc_id: left_alloc, tag: left_tag }),
-                Some(Provenance::Concrete { alloc_id: right_alloc, tag: right_tag }),
-            ) if left_alloc == right_alloc && left_tag == right_tag => left,
+                Provenance::Concrete { alloc_id: left_alloc, tag: left_tag },
+                Provenance::Concrete { alloc_id: right_alloc, tag: right_tag },
+            ) if left_alloc == right_alloc && left_tag == right_tag => Some(left),
             // If one side is a wildcard, the best possible outcome is that it is equal to the other
             // one, and we use that.
-            (Some(Provenance::Wildcard), o) | (o, Some(Provenance::Wildcard)) => o,
+            (Provenance::Wildcard, o) | (o, Provenance::Wildcard) => Some(o),
             // Otherwise, fall back to `None`.
             _ => None,
         }
@@ -598,7 +668,10 @@ pub struct MiriMachine<'tcx> {
     /// Whether floating-point operations can behave non-deterministically.
     pub float_nondet: bool,
     /// Whether floating-point operations can have a non-deterministic rounding error.
-    pub float_rounding_error: bool,
+    pub float_rounding_error: FloatRoundingErrorMode,
+
+    /// Whether Miri artifically introduces short reads/writes on file descriptors.
+    pub short_fd_operations: bool,
 }
 
 impl<'tcx> MiriMachine<'tcx> {
@@ -674,11 +747,13 @@ impl<'tcx> MiriMachine<'tcx> {
             thread_cpu_affinity
                 .insert(threads.active_thread(), CpuAffinityMask::new(&layout_cx, config.num_cpus));
         }
+        let alloc_addresses =
+            RefCell::new(alloc_addresses::GlobalStateInner::new(config, stack_addr, tcx));
         MiriMachine {
             tcx,
             borrow_tracker,
             data_race,
-            alloc_addresses: RefCell::new(alloc_addresses::GlobalStateInner::new(config, stack_addr)),
+            alloc_addresses,
             // `env_vars` depends on a full interpreter so we cannot properly initialize it yet.
             env_vars: EnvVars::default(),
             main_fn_ret_place: None,
@@ -760,6 +835,7 @@ impl<'tcx> MiriMachine<'tcx> {
             force_intrinsic_fallback: config.force_intrinsic_fallback,
             float_nondet: config.float_nondet,
             float_rounding_error: config.float_rounding_error,
+            short_fd_operations: config.short_fd_operations,
         }
     }
 
@@ -936,6 +1012,7 @@ impl VisitProvenance for MiriMachine<'_> {
             force_intrinsic_fallback: _,
             float_nondet: _,
             float_rounding_error: _,
+            short_fd_operations: _,
         } = self;
 
         threads.visit_provenance(visit);
@@ -1076,7 +1153,8 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
                 .target_features
                 .iter()
                 .filter(|&feature| {
-                    !feature.implied && !ecx.tcx.sess.target_features.contains(&feature.name)
+                    feature.kind != TargetFeatureKind::Implied
+                        && !ecx.tcx.sess.target_features.contains(&feature.name)
                 })
                 .fold(String::new(), |mut s, feature| {
                     if !s.is_empty() {
@@ -1111,6 +1189,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
     ) -> InterpResult<'tcx, Option<(&'tcx mir::Body<'tcx>, ty::Instance<'tcx>)>> {
         // For foreign items, try to see if we can emulate them.
         if ecx.tcx.is_foreign_item(instance.def_id()) {
+            let _trace = enter_trace_span!("emulate_foreign_item");
             // An external function call that does not have a MIR body. We either find MIR elsewhere
             // or emulate its effect.
             // This will be Ok(None) if we're emulating the intrinsic entirely within Miri (no need
@@ -1123,6 +1202,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
         }
 
         // Otherwise, load the MIR.
+        let _trace = enter_trace_span!("load_mir");
         interp_ok(Some((ecx.load_mir(instance.def, None)?, instance)))
     }
 
@@ -1205,7 +1285,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
         ecx: &mut InterpCx<'tcx, Self>,
         val: ImmTy<'tcx>,
     ) -> InterpResult<'tcx, ImmTy<'tcx>> {
-        crate::math::apply_random_float_error_to_imm(ecx, val, 2 /* log2(4) */)
+        crate::math::apply_random_float_error_to_imm(ecx, val, 4)
     }
 
     #[inline(always)]
@@ -1394,6 +1474,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
             GlobalDataRaceHandler::Genmc(genmc_ctx) =>
                 genmc_ctx.memory_load(machine, ptr.addr(), range.size)?,
             GlobalDataRaceHandler::Vclocks(_data_race) => {
+                let _trace = enter_trace_span!(data_race::before_memory_read);
                 let AllocDataRaceHandler::Vclocks(data_race, weak_memory) = &alloc_extra.data_race
                 else {
                     unreachable!();
@@ -1425,10 +1506,10 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
         }
         match &machine.data_race {
             GlobalDataRaceHandler::None => {}
-            GlobalDataRaceHandler::Genmc(genmc_ctx) => {
-                genmc_ctx.memory_store(machine, ptr.addr(), range.size)?;
-            }
+            GlobalDataRaceHandler::Genmc(genmc_ctx) =>
+                genmc_ctx.memory_store(machine, ptr.addr(), range.size)?,
             GlobalDataRaceHandler::Vclocks(_global_state) => {
+                let _trace = enter_trace_span!(data_race::before_memory_write);
                 let AllocDataRaceHandler::Vclocks(data_race, weak_memory) =
                     &mut alloc_extra.data_race
                 else {
@@ -1463,8 +1544,9 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
         match &machine.data_race {
             GlobalDataRaceHandler::None => {}
             GlobalDataRaceHandler::Genmc(genmc_ctx) =>
-                genmc_ctx.handle_dealloc(machine, ptr.addr(), size, align, kind)?,
+                genmc_ctx.handle_dealloc(machine, alloc_id, ptr.addr(), kind)?,
             GlobalDataRaceHandler::Vclocks(_global_state) => {
+                let _trace = enter_trace_span!(data_race::before_memory_deallocation);
                 let data_race = alloc_extra.data_race.as_vclocks_mut().unwrap();
                 data_race.write(
                     alloc_id,
@@ -1675,6 +1757,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
         local: mir::Local,
     ) -> InterpResult<'tcx> {
         if let Some(data_race) = &frame.extra.data_race {
+            let _trace = enter_trace_span!(data_race::after_local_read);
             data_race.local_read(local, &ecx.machine);
         }
         interp_ok(())
@@ -1686,6 +1769,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
         storage_live: bool,
     ) -> InterpResult<'tcx> {
         if let Some(data_race) = &ecx.frame().extra.data_race {
+            let _trace = enter_trace_span!(data_race::after_local_write);
             data_race.local_write(local, storage_live, &ecx.machine);
         }
         interp_ok(())
@@ -1708,6 +1792,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
         if let Some(data_race) =
             &machine.threads.active_thread_stack().last().unwrap().extra.data_race
         {
+            let _trace = enter_trace_span!(data_race::after_local_moved_to_memory);
             data_race.local_moved_to_memory(
                 local,
                 alloc_info.data_race.as_vclocks_mut().unwrap(),
diff --git a/src/tools/miri/src/math.rs b/src/tools/miri/src/math.rs
index e9e5a1070c9..50472ed3638 100644
--- a/src/tools/miri/src/math.rs
+++ b/src/tools/miri/src/math.rs
@@ -1,91 +1,335 @@
+use std::ops::Neg;
+use std::{f32, f64};
+
 use rand::Rng as _;
-use rustc_apfloat::Float as _;
-use rustc_apfloat::ieee::IeeeFloat;
+use rustc_apfloat::Float;
+use rustc_apfloat::ieee::{DoubleS, IeeeFloat, Semantics, SingleS};
 use rustc_middle::ty::{self, FloatTy, ScalarInt};
 
 use crate::*;
 
 /// Disturbes a floating-point result by a relative error in the range (-2^scale, 2^scale).
-///
-/// For a 2^N ULP error, you can use an `err_scale` of `-(F::PRECISION - 1 - N)`.
-/// In other words, a 1 ULP (absolute) error is the same as a `2^-(F::PRECISION-1)` relative error.
-/// (Subtracting 1 compensates for the integer bit.)
 pub(crate) fn apply_random_float_error<F: rustc_apfloat::Float>(
     ecx: &mut crate::MiriInterpCx<'_>,
     val: F,
     err_scale: i32,
 ) -> F {
-    if !ecx.machine.float_nondet || !ecx.machine.float_rounding_error {
+    if !ecx.machine.float_nondet
+        || matches!(ecx.machine.float_rounding_error, FloatRoundingErrorMode::None)
+        // relative errors don't do anything to zeros... avoid messing up the sign
+        || val.is_zero()
+        // The logic below makes no sense if the input is already non-finite.
+        || !val.is_finite()
+    {
         return val;
     }
-
     let rng = ecx.machine.rng.get_mut();
+
     // Generate a random integer in the range [0, 2^PREC).
     // (When read as binary, the position of the first `1` determines the exponent,
     // and the remaining bits fill the mantissa. `PREC` is one plus the size of the mantissa,
     // so this all works out.)
-    let r = F::from_u128(rng.random_range(0..(1 << F::PRECISION))).value;
+    let r = F::from_u128(match ecx.machine.float_rounding_error {
+        FloatRoundingErrorMode::Random => rng.random_range(0..(1 << F::PRECISION)),
+        FloatRoundingErrorMode::Max => (1 << F::PRECISION) - 1, // force max error
+        FloatRoundingErrorMode::None => unreachable!(),
+    })
+    .value;
     // Multiply this with 2^(scale - PREC). The result is between 0 and
     // 2^PREC * 2^(scale - PREC) = 2^scale.
     let err = r.scalbn(err_scale.strict_sub(F::PRECISION.try_into().unwrap()));
     // give it a random sign
     let err = if rng.random() { -err } else { err };
-    // multiple the value with (1+err)
-    (val * (F::from_u128(1).value + err).value).value
+    // Compute `val*(1+err)`, distributed out as `val + val*err` to avoid the imprecise addition
+    // error being amplified by multiplication.
+    (val + (val * err).value).value
 }
 
-/// [`apply_random_float_error`] gives instructions to apply a 2^N ULP error.
-/// This function implements these instructions such that applying a 2^N ULP error is less error prone.
-/// So for a 2^N ULP error, you would pass N as the `ulp_exponent` argument.
+/// Applies an error of `[-N, +N]` ULP to the given value.
 pub(crate) fn apply_random_float_error_ulp<F: rustc_apfloat::Float>(
     ecx: &mut crate::MiriInterpCx<'_>,
     val: F,
-    ulp_exponent: u32,
+    max_error: u32,
 ) -> F {
-    let n = i32::try_from(ulp_exponent)
-        .expect("`err_scale_for_ulp`: exponent is too large to create an error scale");
-    // we know this fits
-    let prec = i32::try_from(F::PRECISION).unwrap();
-    let err_scale = -(prec - n - 1);
-    apply_random_float_error(ecx, val, err_scale)
+    // We could try to be clever and reuse `apply_random_float_error`, but that is hard to get right
+    // (see <https://github.com/rust-lang/miri/pull/4558#discussion_r2316838085> for why) so we
+    // implement the logic directly instead.
+    if !ecx.machine.float_nondet
+        || matches!(ecx.machine.float_rounding_error, FloatRoundingErrorMode::None)
+        // FIXME: also disturb zeros? That requires a lot more cases in `fixed_float_value`
+        // and might make the std test suite quite unhappy.
+        || val.is_zero()
+        // The logic below makes no sense if the input is already non-finite.
+        || !val.is_finite()
+    {
+        return val;
+    }
+    let rng = ecx.machine.rng.get_mut();
+
+    let max_error = i64::from(max_error);
+    let error = match ecx.machine.float_rounding_error {
+        FloatRoundingErrorMode::Random => rng.random_range(-max_error..=max_error),
+        FloatRoundingErrorMode::Max =>
+            if rng.random() {
+                max_error
+            } else {
+                -max_error
+            },
+        FloatRoundingErrorMode::None => unreachable!(),
+    };
+    // If upwards ULP and downwards ULP differ, we take the average.
+    let ulp = (((val.next_up().value - val).value + (val - val.next_down().value).value).value
+        / F::from_u128(2).value)
+        .value;
+    // Shift the value by N times the ULP
+    (val + (ulp * F::from_i128(error.into()).value).value).value
 }
 
-/// Applies a random 16ULP floating point error to `val` and returns the new value.
+/// Applies an error of `[-N, +N]` ULP to the given value.
 /// Will fail if `val` is not a floating point number.
 pub(crate) fn apply_random_float_error_to_imm<'tcx>(
     ecx: &mut MiriInterpCx<'tcx>,
     val: ImmTy<'tcx>,
-    ulp_exponent: u32,
+    max_error: u32,
 ) -> InterpResult<'tcx, ImmTy<'tcx>> {
     let scalar = val.to_scalar_int()?;
     let res: ScalarInt = match val.layout.ty.kind() {
         ty::Float(FloatTy::F16) =>
-            apply_random_float_error_ulp(ecx, scalar.to_f16(), ulp_exponent).into(),
+            apply_random_float_error_ulp(ecx, scalar.to_f16(), max_error).into(),
         ty::Float(FloatTy::F32) =>
-            apply_random_float_error_ulp(ecx, scalar.to_f32(), ulp_exponent).into(),
+            apply_random_float_error_ulp(ecx, scalar.to_f32(), max_error).into(),
         ty::Float(FloatTy::F64) =>
-            apply_random_float_error_ulp(ecx, scalar.to_f64(), ulp_exponent).into(),
+            apply_random_float_error_ulp(ecx, scalar.to_f64(), max_error).into(),
         ty::Float(FloatTy::F128) =>
-            apply_random_float_error_ulp(ecx, scalar.to_f128(), ulp_exponent).into(),
+            apply_random_float_error_ulp(ecx, scalar.to_f128(), max_error).into(),
         _ => bug!("intrinsic called with non-float input type"),
     };
 
     interp_ok(ImmTy::from_scalar_int(res, val.layout))
 }
 
-pub(crate) fn sqrt<S: rustc_apfloat::ieee::Semantics>(x: IeeeFloat<S>) -> IeeeFloat<S> {
+/// Given a floating-point operation and a floating-point value, clamps the result to the output
+/// range of the given operation according to the C standard, if any.
+pub(crate) fn clamp_float_value<S: Semantics>(
+    intrinsic_name: &str,
+    val: IeeeFloat<S>,
+) -> IeeeFloat<S>
+where
+    IeeeFloat<S>: IeeeExt,
+{
+    let zero = IeeeFloat::<S>::ZERO;
+    let one = IeeeFloat::<S>::one();
+    let two = IeeeFloat::<S>::two();
+    let pi = IeeeFloat::<S>::pi();
+    let pi_over_2 = (pi / two).value;
+
+    match intrinsic_name {
+        // sin, cos, tanh: [-1, 1]
+        #[rustfmt::skip]
+        | "sinf32"
+        | "sinf64"
+        | "cosf32"
+        | "cosf64"
+        | "tanhf"
+        | "tanh"
+         => val.clamp(one.neg(), one),
+
+        // exp: [0, +INF)
+        "expf32" | "exp2f32" | "expf64" | "exp2f64" => val.maximum(zero),
+
+        // cosh: [1, +INF)
+        "coshf" | "cosh" => val.maximum(one),
+
+        // acos: [0, π]
+        "acosf" | "acos" => val.clamp(zero, pi),
+
+        // asin: [-π, +π]
+        "asinf" | "asin" => val.clamp(pi.neg(), pi),
+
+        // atan: (-π/2, +π/2)
+        "atanf" | "atan" => val.clamp(pi_over_2.neg(), pi_over_2),
+
+        // erfc: (-1, 1)
+        "erff" | "erf" => val.clamp(one.neg(), one),
+
+        // erfc: (0, 2)
+        "erfcf" | "erfc" => val.clamp(zero, two),
+
+        // atan2(y, x): arctan(y/x) in [−π, +π]
+        "atan2f" | "atan2" => val.clamp(pi.neg(), pi),
+
+        _ => val,
+    }
+}
+
+/// For the intrinsics:
+/// - sinf32, sinf64, sinhf, sinh
+/// - cosf32, cosf64, coshf, cosh
+/// - tanhf, tanh, atanf, atan, atan2f, atan2
+/// - expf32, expf64, exp2f32, exp2f64
+/// - logf32, logf64, log2f32, log2f64, log10f32, log10f64
+/// - powf32, powf64
+/// - erff, erf, erfcf, erfc
+/// - hypotf, hypot
+///
+/// # Return
+///
+/// Returns `Some(output)` if the `intrinsic` results in a defined fixed `output` specified in the C standard
+/// (specifically, C23 annex F.10)  when given `args` as arguments. Outputs that are unaffected by a relative error
+/// (such as INF and zero) are not handled here, they are assumed to be handled by the underlying
+/// implementation. Returns `None` if no specific value is guaranteed.
+///
+/// # Note
+///
+/// For `powf*` operations of the form:
+///
+/// - `(SNaN)^(±0)`
+/// - `1^(SNaN)`
+///
+/// The result is implementation-defined:
+/// - musl returns for both `1.0`
+/// - glibc returns for both `NaN`
+///
+/// This discrepancy exists because SNaN handling is not consistently defined across platforms,
+/// and the C standard leaves behavior for SNaNs unspecified.
+///
+/// Miri chooses to adhere to both implementations and returns either one of them non-deterministically.
+pub(crate) fn fixed_float_value<S: Semantics>(
+    ecx: &mut MiriInterpCx<'_>,
+    intrinsic_name: &str,
+    args: &[IeeeFloat<S>],
+) -> Option<IeeeFloat<S>>
+where
+    IeeeFloat<S>: IeeeExt,
+{
+    let this = ecx.eval_context_mut();
+    let one = IeeeFloat::<S>::one();
+    let two = IeeeFloat::<S>::two();
+    let three = IeeeFloat::<S>::three();
+    let pi = IeeeFloat::<S>::pi();
+    let pi_over_2 = (pi / two).value;
+    let pi_over_4 = (pi_over_2 / two).value;
+
+    // Remove `f32`/`f64` suffix, if any.
+    let name = intrinsic_name
+        .strip_suffix("f32")
+        .or_else(|| intrinsic_name.strip_suffix("f64"))
+        .unwrap_or(intrinsic_name);
+    // Also strip trailing `f` (indicates "float"), with an exception for "erf" to avoid
+    // removing that `f`.
+    let name = if name == "erf" { name } else { name.strip_suffix("f").unwrap_or(name) };
+    Some(match (name, args) {
+        // cos(±0) and cosh(±0)= 1
+        ("cos" | "cosh", [input]) if input.is_zero() => one,
+
+        // e^0 = 1
+        ("exp" | "exp2", [input]) if input.is_zero() => one,
+
+        // tanh(±INF) = ±1
+        ("tanh", [input]) if input.is_infinite() => one.copy_sign(*input),
+
+        // atan(±INF) = ±π/2
+        ("atan", [input]) if input.is_infinite() => pi_over_2.copy_sign(*input),
+
+        // erf(±INF) = ±1
+        ("erf", [input]) if input.is_infinite() => one.copy_sign(*input),
+
+        // erfc(-INF) = 2
+        ("erfc", [input]) if input.is_neg_infinity() => (one + one).value,
+
+        // hypot(x, ±0) = abs(x), if x is not a NaN.
+        // `_hypot` is the Windows name for this.
+        ("_hypot" | "hypot", [x, y]) if !x.is_nan() && y.is_zero() => x.abs(),
+
+        // atan2(±0,−0) = ±π.
+        // atan2(±0, y) = ±π for y < 0.
+        // Must check for non NaN because `y.is_negative()` also applies to NaN.
+        ("atan2", [x, y]) if (x.is_zero() && (y.is_negative() && !y.is_nan())) => pi.copy_sign(*x),
+
+        // atan2(±x,−∞) = ±π for finite x > 0.
+        ("atan2", [x, y]) if (!x.is_zero() && !x.is_infinite()) && y.is_neg_infinity() =>
+            pi.copy_sign(*x),
+
+        // atan2(x, ±0) = −π/2 for x < 0.
+        // atan2(x, ±0) =  π/2 for x > 0.
+        ("atan2", [x, y]) if !x.is_zero() && y.is_zero() => pi_over_2.copy_sign(*x),
+
+        //atan2(±∞, −∞) = ±3π/4
+        ("atan2", [x, y]) if x.is_infinite() && y.is_neg_infinity() =>
+            (pi_over_4 * three).value.copy_sign(*x),
+
+        //atan2(±∞, +∞) = ±π/4
+        ("atan2", [x, y]) if x.is_infinite() && y.is_pos_infinity() => pi_over_4.copy_sign(*x),
+
+        // atan2(±∞, y) returns ±π/2 for finite y.
+        ("atan2", [x, y]) if x.is_infinite() && (!y.is_infinite() && !y.is_nan()) =>
+            pi_over_2.copy_sign(*x),
+
+        // (-1)^(±INF) = 1
+        ("pow", [base, exp]) if *base == -one && exp.is_infinite() => one,
+
+        // 1^y = 1 for any y, even a NaN
+        ("pow", [base, exp]) if *base == one => {
+            let rng = this.machine.rng.get_mut();
+            // SNaN exponents get special treatment: they might return 1, or a NaN.
+            let return_nan = exp.is_signaling() && this.machine.float_nondet && rng.random();
+            // Handle both the musl and glibc cases non-deterministically.
+            if return_nan { this.generate_nan(args) } else { one }
+        }
+
+        // x^(±0) = 1 for any x, even a NaN
+        ("pow", [base, exp]) if exp.is_zero() => {
+            let rng = this.machine.rng.get_mut();
+            // SNaN bases get special treatment: they might return 1, or a NaN.
+            let return_nan = base.is_signaling() && this.machine.float_nondet && rng.random();
+            // Handle both the musl and glibc cases non-deterministically.
+            if return_nan { this.generate_nan(args) } else { one }
+        }
+
+        // There are a lot of cases for fixed outputs according to the C Standard, but these are
+        // mainly INF or zero which are not affected by the applied error.
+        _ => return None,
+    })
+}
+
+/// Returns `Some(output)` if `powi` (called `pown` in C) results in a fixed value specified in the
+/// C standard (specifically, C23 annex F.10.4.6) when doing `base^exp`. Otherwise, returns `None`.
+pub(crate) fn fixed_powi_value<S: Semantics>(
+    ecx: &mut MiriInterpCx<'_>,
+    base: IeeeFloat<S>,
+    exp: i32,
+) -> Option<IeeeFloat<S>>
+where
+    IeeeFloat<S>: IeeeExt,
+{
+    match exp {
+        0 => {
+            let one = IeeeFloat::<S>::one();
+            let rng = ecx.machine.rng.get_mut();
+            let return_nan = ecx.machine.float_nondet && rng.random() && base.is_signaling();
+            // For SNaN treatment, we are consistent with `powf`above.
+            // (We wouldn't have two, unlike powf all implementations seem to agree for powi,
+            // but for now we are maximally conservative.)
+            Some(if return_nan { ecx.generate_nan(&[base]) } else { one })
+        }
+
+        _ => None,
+    }
+}
+
+pub(crate) fn sqrt<F: Float>(x: F) -> F {
     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,
+        _ if x.is_negative() => F::NAN,
         // sqrt(∞) = ∞
-        rustc_apfloat::Category::Infinity => IeeeFloat::INFINITY,
+        rustc_apfloat::Category::Infinity => F::INFINITY,
         rustc_apfloat::Category::Normal => {
             // Floating point precision, excluding the integer bit
-            let prec = i32::try_from(S::PRECISION).unwrap() - 1;
+            let prec = i32::try_from(F::PRECISION).unwrap() - 1;
 
             // x = 2^(exp - prec) * mant
             // where mant is an integer with prec+1 bits
@@ -150,24 +394,52 @@ pub(crate) fn sqrt<S: rustc_apfloat::ieee::Semantics>(x: IeeeFloat<S>) -> IeeeFl
             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)
+            F::from_u128(res).value.scalbn(exp / 2 - prec)
         }
     }
 }
 
-/// Extend functionality of rustc_apfloat softfloats
+/// Extend functionality of `rustc_apfloat` softfloats for IEEE float types.
 pub trait IeeeExt: rustc_apfloat::Float {
+    // Some values we use:
+
     #[inline]
     fn one() -> Self {
         Self::from_u128(1).value
     }
 
     #[inline]
+    fn two() -> Self {
+        Self::from_u128(2).value
+    }
+
+    #[inline]
+    fn three() -> Self {
+        Self::from_u128(3).value
+    }
+
+    fn pi() -> Self;
+
+    #[inline]
     fn clamp(self, min: Self, max: Self) -> Self {
         self.maximum(min).minimum(max)
     }
 }
-impl<S: rustc_apfloat::ieee::Semantics> IeeeExt for IeeeFloat<S> {}
+
+macro_rules! impl_ieee_pi {
+    ($float_ty:ident, $semantic:ty) => {
+        impl IeeeExt for IeeeFloat<$semantic> {
+            #[inline]
+            fn pi() -> Self {
+                // We take the value from the standard library as the most reasonable source for an exact π here.
+                Self::from_bits($float_ty::consts::PI.to_bits().into())
+            }
+        }
+    };
+}
+
+impl_ieee_pi!(f32, SingleS);
+impl_ieee_pi!(f64, DoubleS);
 
 #[cfg(test)]
 mod tests {
diff --git a/src/tools/miri/src/operator.rs b/src/tools/miri/src/operator.rs
index 3c3f2c28535..116f45f18dd 100644
--- a/src/tools/miri/src/operator.rs
+++ b/src/tools/miri/src/operator.rs
@@ -57,7 +57,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 let ptr = left.to_scalar().to_pointer(this)?;
                 // We do the actual operation with usize-typed scalars.
                 let left = ImmTy::from_uint(ptr.addr().bytes(), this.machine.layouts.usize);
-                let result = this.binary_op(bin_op, &left, &right)?;
+                let result = this.binary_op(bin_op, &left, right)?;
                 // Construct a new pointer with the provenance of `ptr` (the LHS).
                 let result_ptr = Pointer::new(
                     ptr.provenance,
diff --git a/src/tools/miri/src/shims/extern_static.rs b/src/tools/miri/src/shims/extern_static.rs
index 49c0c380a08..c2527bf8e25 100644
--- a/src/tools/miri/src/shims/extern_static.rs
+++ b/src/tools/miri/src/shims/extern_static.rs
@@ -62,7 +62,7 @@ impl<'tcx> MiriMachine<'tcx> {
             }
             "android" => {
                 Self::null_ptr_extern_statics(ecx, &["bsd_signal"])?;
-                Self::weak_symbol_extern_statics(ecx, &["signal", "getrandom"])?;
+                Self::weak_symbol_extern_statics(ecx, &["signal", "getrandom", "gettid"])?;
             }
             "windows" => {
                 // "_tls_used"
diff --git a/src/tools/miri/src/shims/files.rs b/src/tools/miri/src/shims/files.rs
index 0d4642c6ad0..8c29cb040b5 100644
--- a/src/tools/miri/src/shims/files.rs
+++ b/src/tools/miri/src/shims/files.rs
@@ -168,8 +168,9 @@ pub trait FileDescription: std::fmt::Debug + FileDescriptionExt {
     }
 
     /// Determines whether this FD non-deterministically has its reads and writes shortened.
-    fn nondet_short_accesses(&self) -> bool {
-        true
+    fn short_fd_operations(&self) -> bool {
+        // We only enable this for FD kinds where we think short accesses gain useful test coverage.
+        false
     }
 
     /// Seeks to the given offset (which can be relative to the beginning, end, or current position).
@@ -395,6 +396,13 @@ impl FileDescription for FileHandle {
         communicate_allowed && self.file.is_terminal()
     }
 
+    fn short_fd_operations(&self) -> bool {
+        // While short accesses on file-backed FDs are very rare (at least for sufficiently small
+        // accesses), they can realistically happen when a signal interrupts the syscall.
+        // FIXME: we should return `false` if this is a named pipe...
+        true
+    }
+
     fn as_unix<'tcx>(&self, ecx: &MiriInterpCx<'tcx>) -> &dyn UnixFileDescription {
         assert!(
             ecx.target_os_is_unix(),
diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs
index 21545b68029..eca8cccf5ef 100644
--- a/src/tools/miri/src/shims/foreign_items.rs
+++ b/src/tools/miri/src/shims/foreign_items.rs
@@ -3,8 +3,8 @@ use std::io::Write;
 use std::path::Path;
 
 use rustc_abi::{Align, AlignFromBytesError, CanonAbi, Size};
-use rustc_apfloat::Float;
 use rustc_ast::expand::allocator::alloc_error_handler_name;
+use rustc_hir::attrs::Linkage;
 use rustc_hir::def::DefKind;
 use rustc_hir::def_id::CrateNum;
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
@@ -14,9 +14,9 @@ use rustc_middle::{mir, ty};
 use rustc_span::Symbol;
 use rustc_target::callconv::FnAbi;
 
-use self::helpers::{ToHost, ToSoft};
 use super::alloc::EvalContextExt as _;
 use super::backtrace::EvalContextExt as _;
+use crate::helpers::EvalContextExt as _;
 use crate::*;
 
 /// Type of dynamic symbols (for `dlsym` et al)
@@ -138,7 +138,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             Entry::Occupied(e) => e.into_mut(),
             Entry::Vacant(e) => {
                 // Find it if it was not cached.
-                let mut instance_and_crate: Option<(ty::Instance<'_>, CrateNum)> = None;
+
+                struct SymbolTarget<'tcx> {
+                    instance: ty::Instance<'tcx>,
+                    cnum: CrateNum,
+                    is_weak: bool,
+                }
+                let mut symbol_target: Option<SymbolTarget<'tcx>> = None;
                 helpers::iter_exported_symbols(tcx, |cnum, def_id| {
                     let attrs = tcx.codegen_fn_attrs(def_id);
                     // Skip over imports of items.
@@ -146,7 +152,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                         return interp_ok(());
                     }
                     // Skip over items without an explicitly defined symbol name.
-                    if !(attrs.export_name.is_some()
+                    if !(attrs.symbol_name.is_some()
                         || attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE)
                         || attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL))
                     {
@@ -155,40 +161,80 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
                     let instance = Instance::mono(tcx, def_id);
                     let symbol_name = tcx.symbol_name(instance).name;
+                    let is_weak = attrs.linkage == Some(Linkage::WeakAny);
                     if symbol_name == link_name.as_str() {
-                        if let Some((original_instance, original_cnum)) = instance_and_crate {
-                            // Make sure we are consistent wrt what is 'first' and 'second'.
-                            let original_span = tcx.def_span(original_instance.def_id()).data();
-                            let span = tcx.def_span(def_id).data();
-                            if original_span < span {
-                                throw_machine_stop!(TerminationInfo::MultipleSymbolDefinitions {
-                                    link_name,
-                                    first: original_span,
-                                    first_crate: tcx.crate_name(original_cnum),
-                                    second: span,
-                                    second_crate: tcx.crate_name(cnum),
-                                });
-                            } else {
-                                throw_machine_stop!(TerminationInfo::MultipleSymbolDefinitions {
-                                    link_name,
-                                    first: span,
-                                    first_crate: tcx.crate_name(cnum),
-                                    second: original_span,
-                                    second_crate: tcx.crate_name(original_cnum),
-                                });
+                        if let Some(original) = &symbol_target {
+                            // There is more than one definition with this name. What we do now
+                            // depends on whether one or both definitions are weak.
+                            match (is_weak, original.is_weak) {
+                                (false, true) => {
+                                    // Original definition is a weak definition. Override it.
+
+                                    symbol_target = Some(SymbolTarget {
+                                        instance: ty::Instance::mono(tcx, def_id),
+                                        cnum,
+                                        is_weak,
+                                    });
+                                }
+                                (true, false) => {
+                                    // Current definition is a weak definition. Keep the original one.
+                                }
+                                (true, true) | (false, false) => {
+                                    // Either both definitions are non-weak or both are weak. In
+                                    // either case return an error. For weak definitions we error
+                                    // because it is unspecified which definition would have been
+                                    // picked by the linker.
+
+                                    // Make sure we are consistent wrt what is 'first' and 'second'.
+                                    let original_span =
+                                        tcx.def_span(original.instance.def_id()).data();
+                                    let span = tcx.def_span(def_id).data();
+                                    if original_span < span {
+                                        throw_machine_stop!(
+                                            TerminationInfo::MultipleSymbolDefinitions {
+                                                link_name,
+                                                first: original_span,
+                                                first_crate: tcx.crate_name(original.cnum),
+                                                second: span,
+                                                second_crate: tcx.crate_name(cnum),
+                                            }
+                                        );
+                                    } else {
+                                        throw_machine_stop!(
+                                            TerminationInfo::MultipleSymbolDefinitions {
+                                                link_name,
+                                                first: span,
+                                                first_crate: tcx.crate_name(cnum),
+                                                second: original_span,
+                                                second_crate: tcx.crate_name(original.cnum),
+                                            }
+                                        );
+                                    }
+                                }
                             }
+                        } else {
+                            symbol_target = Some(SymbolTarget {
+                                instance: ty::Instance::mono(tcx, def_id),
+                                cnum,
+                                is_weak,
+                            });
                         }
-                        if !matches!(tcx.def_kind(def_id), DefKind::Fn | DefKind::AssocFn) {
-                            throw_ub_format!(
-                                "attempt to call an exported symbol that is not defined as a function"
-                            );
-                        }
-                        instance_and_crate = Some((ty::Instance::mono(tcx, def_id), cnum));
                     }
                     interp_ok(())
                 })?;
 
-                e.insert(instance_and_crate.map(|ic| ic.0))
+                // Once we identified the instance corresponding to the symbol, ensure
+                // it is a function. It is okay to encounter non-functions in the search above
+                // as long as the final instance we arrive at is a function.
+                if let Some(SymbolTarget { instance, .. }) = symbol_target {
+                    if !matches!(tcx.def_kind(instance.def_id()), DefKind::Fn | DefKind::AssocFn) {
+                        throw_ub_format!(
+                            "attempt to call an exported symbol that is not defined as a function"
+                        );
+                    }
+                }
+
+                e.insert(symbol_target.map(|SymbolTarget { instance, .. }| instance))
             }
         };
         match instance {
@@ -443,13 +489,22 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
             "exit" => {
                 let [code] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
                 let code = this.read_scalar(code)?.to_i32()?;
+                if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() {
+                    // If there is no error, execution should continue (on a different thread).
+                    genmc_ctx.handle_exit(
+                        this.machine.threads.active_thread(),
+                        code,
+                        crate::concurrency::ExitType::ExitCalled,
+                    )?;
+                    todo!(); // FIXME(genmc): Add a way to return here that is allowed to not do progress (can't use existing EmulateItemResult variants).
+                }
                 throw_machine_stop!(TerminationInfo::Exit { code, leak_check: false });
             }
             "abort" => {
                 let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
                 throw_machine_stop!(TerminationInfo::Abort(
                     "the program aborted execution".to_owned()
-                ))
+                ));
             }
 
             // Standard C allocation
@@ -761,208 +816,6 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 this.write_pointer(ptr_dest, dest)?;
             }
 
-            // math functions (note that there are also intrinsics for some other functions)
-            #[rustfmt::skip]
-            | "cbrtf"
-            | "coshf"
-            | "sinhf"
-            | "tanf"
-            | "tanhf"
-            | "acosf"
-            | "asinf"
-            | "atanf"
-            | "log1pf"
-            | "expm1f"
-            | "tgammaf"
-            | "erff"
-            | "erfcf"
-            => {
-                let [f] = this.check_shim_sig_lenient(abi, CanonAbi::C , 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();
-                let res = match link_name.as_str() {
-                    "cbrtf" => f_host.cbrt(),
-                    "coshf" => f_host.cosh(),
-                    "sinhf" => f_host.sinh(),
-                    "tanf" => f_host.tan(),
-                    "tanhf" => f_host.tanh(),
-                    "acosf" => f_host.acos(),
-                    "asinf" => f_host.asin(),
-                    "atanf" => f_host.atan(),
-                    "log1pf" => f_host.ln_1p(),
-                    "expm1f" => f_host.exp_m1(),
-                    "tgammaf" => f_host.gamma(),
-                    "erff" => f_host.erf(),
-                    "erfcf" => f_host.erfc(),
-                    _ => bug!(),
-                };
-                let res = res.to_soft();
-                // Apply a relative error of 16ULP to introduce some non-determinism
-                // simulating imprecise implementations and optimizations.
-                // FIXME: temporarily disabled as it breaks std tests.
-                // let res = math::apply_random_float_error_ulp(
-                //     this,
-                //     res,
-                //     4, // log2(16)
-                // );
-                let res = this.adjust_nan(res, &[f]);
-                this.write_scalar(res, dest)?;
-            }
-            #[rustfmt::skip]
-            | "_hypotf"
-            | "hypotf"
-            | "atan2f"
-            | "fdimf"
-            => {
-                let [f1, f2] = this.check_shim_sig_lenient(abi, CanonAbi::C , 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
-                // (see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/floating-point-primitives?view=vs-2019)
-                // Using host floats (but it's fine, these operations do not have guaranteed precision).
-                let res = match link_name.as_str() {
-                    "_hypotf" | "hypotf" => f1.to_host().hypot(f2.to_host()).to_soft(),
-                    "atan2f" => f1.to_host().atan2(f2.to_host()).to_soft(),
-                    #[allow(deprecated)]
-                    "fdimf" => f1.to_host().abs_sub(f2.to_host()).to_soft(),
-                    _ => bug!(),
-                };
-                // Apply a relative error of 16ULP to introduce some non-determinism
-                // simulating imprecise implementations and optimizations.
-                // FIXME: temporarily disabled as it breaks std tests.
-                // let res = math::apply_random_float_error_ulp(
-                //     this,
-                //     res,
-                //     4, // log2(16)
-                // );
-                let res = this.adjust_nan(res, &[f1, f2]);
-                this.write_scalar(res, dest)?;
-            }
-            #[rustfmt::skip]
-            | "cbrt"
-            | "cosh"
-            | "sinh"
-            | "tan"
-            | "tanh"
-            | "acos"
-            | "asin"
-            | "atan"
-            | "log1p"
-            | "expm1"
-            | "tgamma"
-            | "erf"
-            | "erfc"
-            => {
-                let [f] = this.check_shim_sig_lenient(abi, CanonAbi::C , 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();
-                let res = match link_name.as_str() {
-                    "cbrt" => f_host.cbrt(),
-                    "cosh" => f_host.cosh(),
-                    "sinh" => f_host.sinh(),
-                    "tan" => f_host.tan(),
-                    "tanh" => f_host.tanh(),
-                    "acos" => f_host.acos(),
-                    "asin" => f_host.asin(),
-                    "atan" => f_host.atan(),
-                    "log1p" => f_host.ln_1p(),
-                    "expm1" => f_host.exp_m1(),
-                    "tgamma" => f_host.gamma(),
-                    "erf" => f_host.erf(),
-                    "erfc" => f_host.erfc(),
-                    _ => bug!(),
-                };
-                let res = res.to_soft();
-                // Apply a relative error of 16ULP to introduce some non-determinism
-                // simulating imprecise implementations and optimizations.
-                // FIXME: temporarily disabled as it breaks std tests.
-                // let res = math::apply_random_float_error_ulp(
-                //     this,
-                //     res.to_soft(),
-                //     4, // log2(16)
-                // );
-                let res = this.adjust_nan(res, &[f]);
-                this.write_scalar(res, dest)?;
-            }
-            #[rustfmt::skip]
-            | "_hypot"
-            | "hypot"
-            | "atan2"
-            | "fdim"
-            => {
-                let [f1, f2] = this.check_shim_sig_lenient(abi, CanonAbi::C , 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
-                // (see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/floating-point-primitives?view=vs-2019)
-                // Using host floats (but it's fine, these operations do not have guaranteed precision).
-                let res = match link_name.as_str() {
-                    "_hypot" | "hypot" => f1.to_host().hypot(f2.to_host()).to_soft(),
-                    "atan2" => f1.to_host().atan2(f2.to_host()).to_soft(),
-                    #[allow(deprecated)]
-                    "fdim" => f1.to_host().abs_sub(f2.to_host()).to_soft(),
-                    _ => bug!(),
-                };
-                // Apply a relative error of 16ULP to introduce some non-determinism
-                // simulating imprecise implementations and optimizations.
-                // FIXME: temporarily disabled as it breaks std tests.
-                // let res = math::apply_random_float_error_ulp(
-                //     this,
-                //     res,
-                //     4, // log2(16)
-                // );
-                let res = this.adjust_nan(res, &[f1, f2]);
-                this.write_scalar(res, dest)?;
-            }
-            #[rustfmt::skip]
-            | "_ldexp"
-            | "ldexp"
-            | "scalbn"
-            => {
-                let [x, exp] = this.check_shim_sig_lenient(abi, CanonAbi::C , 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()?;
-
-                let res = x.scalbn(exp);
-                let res = this.adjust_nan(res, &[x]);
-                this.write_scalar(res, dest)?;
-            }
-            "lgammaf_r" => {
-                let [x, signp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
-                let x = this.read_scalar(x)?.to_f32()?;
-                let signp = this.deref_pointer_as(signp, this.machine.layouts.i32)?;
-
-                // Using host floats (but it's fine, these operations do not have guaranteed precision).
-                let (res, sign) = x.to_host().ln_gamma();
-                this.write_int(sign, &signp)?;
-                let res = res.to_soft();
-                // Apply a relative error of 16ULP to introduce some non-determinism
-                // simulating imprecise implementations and optimizations.
-                // FIXME: temporarily disabled as it breaks std tests.
-                // let res = math::apply_random_float_error_ulp(this, res, 4 /* log2(16) */);
-                let res = this.adjust_nan(res, &[x]);
-                this.write_scalar(res, dest)?;
-            }
-            "lgamma_r" => {
-                let [x, signp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
-                let x = this.read_scalar(x)?.to_f64()?;
-                let signp = this.deref_pointer_as(signp, this.machine.layouts.i32)?;
-
-                // Using host floats (but it's fine, these operations do not have guaranteed precision).
-                let (res, sign) = x.to_host().ln_gamma();
-                this.write_int(sign, &signp)?;
-                let res = res.to_soft();
-                // Apply a relative error of 16ULP to introduce some non-determinism
-                // simulating imprecise implementations and optimizations.
-                // FIXME: temporarily disabled as it breaks std tests.
-                // let res = math::apply_random_float_error_ulp(this, res, 4 /* log2(16) */);
-                let res = this.adjust_nan(res, &[x]);
-                this.write_scalar(res, dest)?;
-            }
-
             // LLVM intrinsics
             "llvm.prefetch" => {
                 let [p, rw, loc, ty] =
@@ -1044,8 +897,18 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 }
             }
 
-            // Platform-specific shims
-            _ =>
+            // Fallback to shims in submodules.
+            _ => {
+                // Math shims
+                #[expect(irrefutable_let_patterns)]
+                if let res = shims::math::EvalContextExt::emulate_foreign_item_inner(
+                    this, link_name, abi, args, dest,
+                )? && !matches!(res, EmulateItemResult::NotSupported)
+                {
+                    return interp_ok(res);
+                }
+
+                // Platform-specific shims
                 return match this.tcx.sess.target.os.as_ref() {
                     _ if this.target_os_is_unix() =>
                         shims::unix::foreign_items::EvalContextExt::emulate_foreign_item_inner(
@@ -1060,7 +923,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                             this, link_name, abi, args, dest,
                         ),
                     _ => interp_ok(EmulateItemResult::NotSupported),
-                },
+                };
+            }
         };
         // We only fall through to here if we did *not* hit the `_` arm above,
         // i.e., if we actually emulated the function with one of the shims.
diff --git a/src/tools/miri/src/shims/math.rs b/src/tools/miri/src/shims/math.rs
new file mode 100644
index 00000000000..576e76494bc
--- /dev/null
+++ b/src/tools/miri/src/shims/math.rs
@@ -0,0 +1,247 @@
+use rustc_abi::CanonAbi;
+use rustc_apfloat::Float;
+use rustc_middle::ty::Ty;
+use rustc_span::Symbol;
+use rustc_target::callconv::FnAbi;
+
+use self::helpers::{ToHost, ToSoft};
+use crate::*;
+
+impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
+pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
+    fn emulate_foreign_item_inner(
+        &mut self,
+        link_name: Symbol,
+        abi: &FnAbi<'tcx, Ty<'tcx>>,
+        args: &[OpTy<'tcx>],
+        dest: &MPlaceTy<'tcx>,
+    ) -> InterpResult<'tcx, EmulateItemResult> {
+        let this = self.eval_context_mut();
+
+        // math functions (note that there are also intrinsics for some other functions)
+        match link_name.as_str() {
+            // math functions (note that there are also intrinsics for some other functions)
+            #[rustfmt::skip]
+            | "cbrtf"
+            | "coshf"
+            | "sinhf"
+            | "tanf"
+            | "tanhf"
+            | "acosf"
+            | "asinf"
+            | "atanf"
+            | "log1pf"
+            | "expm1f"
+            | "tgammaf"
+            | "erff"
+            | "erfcf"
+            => {
+                let [f] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?;
+                let f = this.read_scalar(f)?.to_f32()?;
+
+                let res = math::fixed_float_value(this, link_name.as_str(), &[f]).unwrap_or_else(|| {
+                    // Using host floats (but it's fine, these operations do not have
+                    // guaranteed precision).
+                    let f_host = f.to_host();
+                    let res = match link_name.as_str() {
+                        "cbrtf" => f_host.cbrt(),
+                        "coshf" => f_host.cosh(),
+                        "sinhf" => f_host.sinh(),
+                        "tanf" => f_host.tan(),
+                        "tanhf" => f_host.tanh(),
+                        "acosf" => f_host.acos(),
+                        "asinf" => f_host.asin(),
+                        "atanf" => f_host.atan(),
+                        "log1pf" => f_host.ln_1p(),
+                        "expm1f" => f_host.exp_m1(),
+                        "tgammaf" => f_host.gamma(),
+                        "erff" => f_host.erf(),
+                        "erfcf" => f_host.erfc(),
+                        _ => bug!(),
+                    };
+                    let res = res.to_soft();
+                    // Apply a relative error of 4ULP to introduce some non-determinism
+                    // simulating imprecise implementations and optimizations.
+                    let res = math::apply_random_float_error_ulp(this, res, 4);
+
+                    // Clamp the result to the guaranteed range of this function according to the C standard,
+                    // if any.
+                    math::clamp_float_value(link_name.as_str(), res)
+                });
+                let res = this.adjust_nan(res, &[f]);
+                this.write_scalar(res, dest)?;
+            }
+            #[rustfmt::skip]
+            | "_hypotf"
+            | "hypotf"
+            | "atan2f"
+            | "fdimf"
+            => {
+                let [f1, f2] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?;
+                let f1 = this.read_scalar(f1)?.to_f32()?;
+                let f2 = this.read_scalar(f2)?.to_f32()?;
+
+                let res = math::fixed_float_value(this, link_name.as_str(), &[f1, f2])
+                    .unwrap_or_else(|| {
+                        let res = match link_name.as_str() {
+                            // underscore case for windows, here and below
+                            // (see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/floating-point-primitives?view=vs-2019)
+                            // Using host floats (but it's fine, these operations do not have guaranteed precision).
+                            "_hypotf" | "hypotf" => f1.to_host().hypot(f2.to_host()).to_soft(),
+                            "atan2f" => f1.to_host().atan2(f2.to_host()).to_soft(),
+                            #[allow(deprecated)]
+                            "fdimf" => f1.to_host().abs_sub(f2.to_host()).to_soft(),
+                            _ => bug!(),
+                        };
+                        // Apply a relative error of 4ULP to introduce some non-determinism
+                        // simulating imprecise implementations and optimizations.
+                        let res = math::apply_random_float_error_ulp(this, res, 4);
+
+                        // Clamp the result to the guaranteed range of this function according to the C standard,
+                        // if any.
+                        math::clamp_float_value(link_name.as_str(), res)
+                    });
+                let res = this.adjust_nan(res, &[f1, f2]);
+                this.write_scalar(res, dest)?;
+            }
+            #[rustfmt::skip]
+            | "cbrt"
+            | "cosh"
+            | "sinh"
+            | "tan"
+            | "tanh"
+            | "acos"
+            | "asin"
+            | "atan"
+            | "log1p"
+            | "expm1"
+            | "tgamma"
+            | "erf"
+            | "erfc"
+            => {
+                let [f] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?;
+                let f = this.read_scalar(f)?.to_f64()?;
+
+                let res = math::fixed_float_value(this, link_name.as_str(), &[f]).unwrap_or_else(|| {
+                    // Using host floats (but it's fine, these operations do not have
+                    // guaranteed precision).
+                    let f_host = f.to_host();
+                    let res = match link_name.as_str() {
+                        "cbrt" => f_host.cbrt(),
+                        "cosh" => f_host.cosh(),
+                        "sinh" => f_host.sinh(),
+                        "tan" => f_host.tan(),
+                        "tanh" => f_host.tanh(),
+                        "acos" => f_host.acos(),
+                        "asin" => f_host.asin(),
+                        "atan" => f_host.atan(),
+                        "log1p" => f_host.ln_1p(),
+                        "expm1" => f_host.exp_m1(),
+                        "tgamma" => f_host.gamma(),
+                        "erf" => f_host.erf(),
+                        "erfc" => f_host.erfc(),
+                        _ => bug!(),
+                    };
+                    let res = res.to_soft();
+                    // Apply a relative error of 4ULP to introduce some non-determinism
+                    // simulating imprecise implementations and optimizations.
+                    let res = math::apply_random_float_error_ulp(this, res, 4);
+
+                    // Clamp the result to the guaranteed range of this function according to the C standard,
+                    // if any.
+                    math::clamp_float_value(link_name.as_str(), res)
+                });
+                let res = this.adjust_nan(res, &[f]);
+                this.write_scalar(res, dest)?;
+            }
+            #[rustfmt::skip]
+            | "_hypot"
+            | "hypot"
+            | "atan2"
+            | "fdim"
+            => {
+                let [f1, f2] = this.check_shim_sig_lenient(abi, CanonAbi::C , link_name, args)?;
+                let f1 = this.read_scalar(f1)?.to_f64()?;
+                let f2 = this.read_scalar(f2)?.to_f64()?;
+
+                let res = math::fixed_float_value(this, link_name.as_str(), &[f1, f2]).unwrap_or_else(|| {
+                    let res = match link_name.as_str() {
+                        // underscore case for windows, here and below
+                        // (see https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/floating-point-primitives?view=vs-2019)
+                        // Using host floats (but it's fine, these operations do not have guaranteed precision).
+                        "_hypot" | "hypot" => f1.to_host().hypot(f2.to_host()).to_soft(),
+                        "atan2" => f1.to_host().atan2(f2.to_host()).to_soft(),
+                        #[allow(deprecated)]
+                        "fdim" => f1.to_host().abs_sub(f2.to_host()).to_soft(),
+                        _ => bug!(),
+                    };
+                    // Apply a relative error of 4ULP to introduce some non-determinism
+                    // simulating imprecise implementations and optimizations.
+                    let res = math::apply_random_float_error_ulp(this, res, 4);
+
+                    // Clamp the result to the guaranteed range of this function according to the C standard,
+                    // if any.
+                    math::clamp_float_value(link_name.as_str(), res)
+                });
+                let res = this.adjust_nan(res, &[f1, f2]);
+                this.write_scalar(res, dest)?;
+            }
+            #[rustfmt::skip]
+            | "_ldexp"
+            | "ldexp"
+            | "scalbn"
+            => {
+                let [x, exp] = this.check_shim_sig_lenient(abi, CanonAbi::C , 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()?;
+
+                let res = x.scalbn(exp);
+                let res = this.adjust_nan(res, &[x]);
+                this.write_scalar(res, dest)?;
+            }
+            "lgammaf_r" => {
+                let [x, signp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
+                let x = this.read_scalar(x)?.to_f32()?;
+                let signp = this.deref_pointer_as(signp, this.machine.layouts.i32)?;
+
+                // Using host floats (but it's fine, these operations do not have guaranteed precision).
+                let (res, sign) = x.to_host().ln_gamma();
+                this.write_int(sign, &signp)?;
+
+                let res = res.to_soft();
+                // Apply a relative error of 4ULP to introduce some non-determinism
+                // simulating imprecise implementations and optimizations.
+                let res = math::apply_random_float_error_ulp(this, res, 4);
+                // Clamp the result to the guaranteed range of this function according to the C standard,
+                // if any.
+                let res = math::clamp_float_value(link_name.as_str(), res);
+                let res = this.adjust_nan(res, &[x]);
+                this.write_scalar(res, dest)?;
+            }
+            "lgamma_r" => {
+                let [x, signp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
+                let x = this.read_scalar(x)?.to_f64()?;
+                let signp = this.deref_pointer_as(signp, this.machine.layouts.i32)?;
+
+                // Using host floats (but it's fine, these operations do not have guaranteed precision).
+                let (res, sign) = x.to_host().ln_gamma();
+                this.write_int(sign, &signp)?;
+
+                let res = res.to_soft();
+                // Apply a relative error of 4ULP to introduce some non-determinism
+                // simulating imprecise implementations and optimizations.
+                let res = math::apply_random_float_error_ulp(this, res, 4);
+                // Clamp the result to the guaranteed range of this function according to the C standard,
+                // if any.
+                let res = math::clamp_float_value(link_name.as_str(), res);
+                let res = this.adjust_nan(res, &[x]);
+                this.write_scalar(res, dest)?;
+            }
+
+            _ => return interp_ok(EmulateItemResult::NotSupported),
+        }
+
+        interp_ok(EmulateItemResult::NeedsReturn)
+    }
+}
diff --git a/src/tools/miri/src/shims/mod.rs b/src/tools/miri/src/shims/mod.rs
index 7f594d4fdd6..7f7bc3b1cf7 100644
--- a/src/tools/miri/src/shims/mod.rs
+++ b/src/tools/miri/src/shims/mod.rs
@@ -4,6 +4,7 @@ mod aarch64;
 mod alloc;
 mod backtrace;
 mod files;
+mod math;
 #[cfg(all(unix, feature = "native-lib"))]
 mod native_lib;
 mod unix;
diff --git a/src/tools/miri/src/shims/native_lib/ffi.rs b/src/tools/miri/src/shims/native_lib/ffi.rs
new file mode 100644
index 00000000000..0badf22bb76
--- /dev/null
+++ b/src/tools/miri/src/shims/native_lib/ffi.rs
@@ -0,0 +1,46 @@
+//! Support code for dealing with libffi.
+
+use libffi::low::CodePtr;
+use libffi::middle::{Arg as ArgPtr, Cif, Type as FfiType};
+
+/// Perform the actual FFI call.
+///
+/// # Safety
+///
+/// The safety invariants of the foreign function being called must be upheld (if any).
+pub unsafe fn call<R: libffi::high::CType>(fun: CodePtr, args: &mut [OwnedArg]) -> R {
+    let arg_ptrs: Vec<_> = args.iter().map(|arg| arg.ptr()).collect();
+    let cif = Cif::new(args.iter_mut().map(|arg| arg.ty.take().unwrap()), R::reify().into_middle());
+    // SAFETY: Caller upholds that the function is safe to call, and since we
+    // were passed a slice reference we know the `OwnedArg`s won't have been
+    // dropped by this point.
+    unsafe { cif.call(fun, &arg_ptrs) }
+}
+
+/// An argument for an FFI call.
+#[derive(Debug, Clone)]
+pub struct OwnedArg {
+    /// The type descriptor for this argument.
+    ty: Option<FfiType>,
+    /// Corresponding bytes for the value.
+    bytes: Box<[u8]>,
+}
+
+impl OwnedArg {
+    /// Instantiates an argument from a type descriptor and bytes.
+    pub fn new(ty: FfiType, bytes: Box<[u8]>) -> Self {
+        Self { ty: Some(ty), bytes }
+    }
+
+    /// Creates a libffi argument pointer pointing to this argument's bytes.
+    /// NB: Since `libffi::middle::Arg` ignores the lifetime of the reference
+    /// it's derived from, it is up to the caller to ensure the `OwnedArg` is
+    /// not dropped before unsafely calling `libffi::middle::Cif::call()`!
+    fn ptr(&self) -> ArgPtr {
+        // FIXME: Using `&self.bytes[0]` to reference the whole array is
+        // definitely unsound under SB, but we're waiting on
+        // https://github.com/libffi-rs/libffi-rs/commit/112a37b3b6ffb35bd75241fbcc580de40ba74a73
+        // to land in a release so that we don't need to do this.
+        ArgPtr::new(&self.bytes[0])
+    }
+}
diff --git a/src/tools/miri/src/shims/native_lib/mod.rs b/src/tools/miri/src/shims/native_lib/mod.rs
index 2827ed997a7..da8f785e373 100644
--- a/src/tools/miri/src/shims/native_lib/mod.rs
+++ b/src/tools/miri/src/shims/native_lib/mod.rs
@@ -2,14 +2,15 @@
 
 use std::ops::Deref;
 
-use libffi::high::call as ffi;
 use libffi::low::CodePtr;
-use rustc_abi::{BackendRepr, HasDataLayout, Size};
-use rustc_middle::mir::interpret::Pointer;
-use rustc_middle::ty::{self as ty, IntTy, UintTy};
+use libffi::middle::Type as FfiType;
+use rustc_abi::{HasDataLayout, Size};
+use rustc_middle::ty::{self as ty, IntTy, Ty, UintTy};
 use rustc_span::Symbol;
 use serde::{Deserialize, Serialize};
 
+mod ffi;
+
 #[cfg_attr(
     not(all(
         target_os = "linux",
@@ -20,6 +21,7 @@ use serde::{Deserialize, Serialize};
 )]
 pub mod trace;
 
+use self::ffi::OwnedArg;
 use crate::*;
 
 /// The final results of an FFI trace, containing every relevant event detected
@@ -70,12 +72,12 @@ impl AccessRange {
 impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
 trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
     /// Call native host function and return the output as an immediate.
-    fn call_native_with_args<'a>(
+    fn call_native_with_args(
         &mut self,
         link_name: Symbol,
         dest: &MPlaceTy<'tcx>,
-        ptr: CodePtr,
-        libffi_args: Vec<libffi::high::Arg<'a>>,
+        fun: CodePtr,
+        libffi_args: &mut [OwnedArg],
     ) -> InterpResult<'tcx, (crate::ImmTy<'tcx>, Option<MemEvents>)> {
         let this = self.eval_context_mut();
         #[cfg(target_os = "linux")]
@@ -93,55 +95,55 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     // Unsafe because of the call to native code.
                     // Because this is calling a C function it is not necessarily sound,
                     // but there is no way around this and we've checked as much as we can.
-                    let x = unsafe { ffi::call::<i8>(ptr, libffi_args.as_slice()) };
+                    let x = unsafe { ffi::call::<i8>(fun, libffi_args) };
                     Scalar::from_i8(x)
                 }
                 ty::Int(IntTy::I16) => {
-                    let x = unsafe { ffi::call::<i16>(ptr, libffi_args.as_slice()) };
+                    let x = unsafe { ffi::call::<i16>(fun, libffi_args) };
                     Scalar::from_i16(x)
                 }
                 ty::Int(IntTy::I32) => {
-                    let x = unsafe { ffi::call::<i32>(ptr, libffi_args.as_slice()) };
+                    let x = unsafe { ffi::call::<i32>(fun, libffi_args) };
                     Scalar::from_i32(x)
                 }
                 ty::Int(IntTy::I64) => {
-                    let x = unsafe { ffi::call::<i64>(ptr, libffi_args.as_slice()) };
+                    let x = unsafe { ffi::call::<i64>(fun, libffi_args) };
                     Scalar::from_i64(x)
                 }
                 ty::Int(IntTy::Isize) => {
-                    let x = unsafe { ffi::call::<isize>(ptr, libffi_args.as_slice()) };
+                    let x = unsafe { ffi::call::<isize>(fun, libffi_args) };
                     Scalar::from_target_isize(x.try_into().unwrap(), this)
                 }
                 // uints
                 ty::Uint(UintTy::U8) => {
-                    let x = unsafe { ffi::call::<u8>(ptr, libffi_args.as_slice()) };
+                    let x = unsafe { ffi::call::<u8>(fun, libffi_args) };
                     Scalar::from_u8(x)
                 }
                 ty::Uint(UintTy::U16) => {
-                    let x = unsafe { ffi::call::<u16>(ptr, libffi_args.as_slice()) };
+                    let x = unsafe { ffi::call::<u16>(fun, libffi_args) };
                     Scalar::from_u16(x)
                 }
                 ty::Uint(UintTy::U32) => {
-                    let x = unsafe { ffi::call::<u32>(ptr, libffi_args.as_slice()) };
+                    let x = unsafe { ffi::call::<u32>(fun, libffi_args) };
                     Scalar::from_u32(x)
                 }
                 ty::Uint(UintTy::U64) => {
-                    let x = unsafe { ffi::call::<u64>(ptr, libffi_args.as_slice()) };
+                    let x = unsafe { ffi::call::<u64>(fun, libffi_args) };
                     Scalar::from_u64(x)
                 }
                 ty::Uint(UintTy::Usize) => {
-                    let x = unsafe { ffi::call::<usize>(ptr, libffi_args.as_slice()) };
+                    let x = unsafe { ffi::call::<usize>(fun, libffi_args) };
                     Scalar::from_target_usize(x.try_into().unwrap(), this)
                 }
                 // Functions with no declared return type (i.e., the default return)
                 // have the output_type `Tuple([])`.
                 ty::Tuple(t_list) if (*t_list).deref().is_empty() => {
-                    unsafe { ffi::call::<()>(ptr, libffi_args.as_slice()) };
+                    unsafe { ffi::call::<()>(fun, libffi_args) };
                     return interp_ok(ImmTy::uninit(dest.layout));
                 }
                 ty::RawPtr(..) => {
-                    let x = unsafe { ffi::call::<*const ()>(ptr, libffi_args.as_slice()) };
-                    let ptr = Pointer::new(Provenance::Wildcard, Size::from_bytes(x.addr()));
+                    let x = unsafe { ffi::call::<*const ()>(fun, libffi_args) };
+                    let ptr = StrictPointer::new(Provenance::Wildcard, Size::from_bytes(x.addr()));
                     Scalar::from_pointer(ptr, this)
                 }
                 _ =>
@@ -242,13 +244,9 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
                 match evt {
                     AccessEvent::Read(_) => {
-                        // FIXME: ProvenanceMap should have something like get_range().
-                        let p_map = alloc.provenance();
-                        for idx in overlap {
-                            // If a provenance was read by the foreign code, expose it.
-                            if let Some(prov) = p_map.get(Size::from_bytes(idx), this) {
-                                this.expose_provenance(prov)?;
-                            }
+                        // If a provenance was read by the foreign code, expose it.
+                        for prov in alloc.provenance().get_range(this, overlap.into()) {
+                            this.expose_provenance(prov)?;
                         }
                     }
                     AccessEvent::Write(_, certain) => {
@@ -271,6 +269,150 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
         interp_ok(())
     }
+
+    /// Extract the value from the result of reading an operand from the machine
+    /// and convert it to a `OwnedArg`.
+    fn op_to_ffi_arg(&self, v: &OpTy<'tcx>, tracing: bool) -> InterpResult<'tcx, OwnedArg> {
+        let this = self.eval_context_ref();
+
+        // This should go first so that we emit unsupported before doing a bunch
+        // of extra work for types that aren't supported yet.
+        let ty = this.ty_to_ffitype(v.layout.ty)?;
+
+        // Helper to print a warning when a pointer is shared with the native code.
+        let expose = |prov: Provenance| -> InterpResult<'tcx> {
+            // The first time this happens, print a warning.
+            if !this.machine.native_call_mem_warned.replace(true) {
+                // Newly set, so first time we get here.
+                this.emit_diagnostic(NonHaltingDiagnostic::NativeCallSharedMem { tracing });
+            }
+
+            this.expose_provenance(prov)?;
+            interp_ok(())
+        };
+
+        // Compute the byte-level representation of the argument. If there's a pointer in there, we
+        // expose it inside the AM. Later in `visit_reachable_allocs`, the "meta"-level provenance
+        // for accessing the pointee gets exposed; this is crucial to justify the C code effectively
+        // casting the integer in `byte` to a pointer and using that.
+        let bytes = match v.as_mplace_or_imm() {
+            either::Either::Left(mplace) => {
+                // Get the alloc id corresponding to this mplace, alongside
+                // a pointer that's offset to point to this particular
+                // mplace (not one at the base addr of the allocation).
+                let sz = mplace.layout.size.bytes_usize();
+                if sz == 0 {
+                    throw_unsup_format!("attempting to pass a ZST over FFI");
+                }
+                let (id, ofs, _) = this.ptr_get_alloc_id(mplace.ptr(), sz.try_into().unwrap())?;
+                let ofs = ofs.bytes_usize();
+                let range = ofs..ofs.strict_add(sz);
+                // Expose all provenances in the allocation within the byte range of the struct, if
+                // any. These pointers are being directly passed to native code by-value.
+                let alloc = this.get_alloc_raw(id)?;
+                for prov in alloc.provenance().get_range(this, range.clone().into()) {
+                    expose(prov)?;
+                }
+                // Read the bytes that make up this argument. We cannot use the normal getter as
+                // those would fail if any part of the argument is uninitialized. Native code
+                // is kind of outside the interpreter, after all...
+                Box::from(alloc.inspect_with_uninit_and_ptr_outside_interpreter(range))
+            }
+            either::Either::Right(imm) => {
+                let mut bytes: Box<[u8]> = vec![0; imm.layout.size.bytes_usize()].into();
+
+                // A little helper to write scalars to our byte array.
+                let mut write_scalar = |this: &MiriInterpCx<'tcx>, sc: Scalar, pos: usize| {
+                    // If a scalar is a pointer, then expose its provenance.
+                    if let interpret::Scalar::Ptr(p, _) = sc {
+                        expose(p.provenance)?;
+                    }
+                    write_target_uint(
+                        this.data_layout().endian,
+                        &mut bytes[pos..][..sc.size().bytes_usize()],
+                        sc.to_scalar_int()?.to_bits_unchecked(),
+                    )
+                    .unwrap();
+                    interp_ok(())
+                };
+
+                // Write the scalar into the `bytes` buffer.
+                match *imm {
+                    Immediate::Scalar(sc) => write_scalar(this, sc, 0)?,
+                    Immediate::ScalarPair(sc_first, sc_second) => {
+                        // The first scalar has an offset of zero; compute the offset of the 2nd.
+                        let ofs_second = {
+                            let rustc_abi::BackendRepr::ScalarPair(a, b) = imm.layout.backend_repr
+                            else {
+                                span_bug!(
+                                    this.cur_span(),
+                                    "op_to_ffi_arg: invalid scalar pair layout: {:#?}",
+                                    imm.layout
+                                )
+                            };
+                            a.size(this).align_to(b.align(this).abi).bytes_usize()
+                        };
+
+                        write_scalar(this, sc_first, 0)?;
+                        write_scalar(this, sc_second, ofs_second)?;
+                    }
+                    Immediate::Uninit => {
+                        // Nothing to write.
+                    }
+                }
+
+                bytes
+            }
+        };
+        interp_ok(OwnedArg::new(ty, bytes))
+    }
+
+    /// Parses an ADT to construct the matching libffi type.
+    fn adt_to_ffitype(
+        &self,
+        orig_ty: Ty<'_>,
+        adt_def: ty::AdtDef<'tcx>,
+        args: &'tcx ty::List<ty::GenericArg<'tcx>>,
+    ) -> InterpResult<'tcx, FfiType> {
+        // TODO: Certain non-C reprs should be okay also.
+        if !adt_def.repr().c() {
+            throw_unsup_format!("passing a non-#[repr(C)] struct over FFI: {orig_ty}")
+        }
+        // TODO: unions, etc.
+        if !adt_def.is_struct() {
+            throw_unsup_format!(
+                "unsupported argument type for native call: {orig_ty} is an enum or union"
+            );
+        }
+
+        let this = self.eval_context_ref();
+        let mut fields = vec![];
+        for field in &adt_def.non_enum_variant().fields {
+            fields.push(this.ty_to_ffitype(field.ty(*this.tcx, args))?);
+        }
+
+        interp_ok(FfiType::structure(fields))
+    }
+
+    /// Gets the matching libffi type for a given Ty.
+    fn ty_to_ffitype(&self, ty: Ty<'tcx>) -> InterpResult<'tcx, FfiType> {
+        interp_ok(match ty.kind() {
+            ty::Int(IntTy::I8) => FfiType::i8(),
+            ty::Int(IntTy::I16) => FfiType::i16(),
+            ty::Int(IntTy::I32) => FfiType::i32(),
+            ty::Int(IntTy::I64) => FfiType::i64(),
+            ty::Int(IntTy::Isize) => FfiType::isize(),
+            // the uints
+            ty::Uint(UintTy::U8) => FfiType::u8(),
+            ty::Uint(UintTy::U16) => FfiType::u16(),
+            ty::Uint(UintTy::U32) => FfiType::u32(),
+            ty::Uint(UintTy::U64) => FfiType::u64(),
+            ty::Uint(UintTy::Usize) => FfiType::usize(),
+            ty::RawPtr(..) => FfiType::pointer(),
+            ty::Adt(adt_def, args) => self.adt_to_ffitype(ty, *adt_def, args)?,
+            _ => throw_unsup_format!("unsupported argument type for native call: {}", ty),
+        })
+    }
 }
 
 impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
@@ -299,36 +441,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // Do we have ptrace?
         let tracing = trace::Supervisor::is_enabled();
 
-        // Get the function arguments, and convert them to `libffi`-compatible form.
-        let mut libffi_args = Vec::<CArg>::with_capacity(args.len());
+        // Get the function arguments, copy them, and prepare the type descriptions.
+        let mut libffi_args = Vec::<OwnedArg>::with_capacity(args.len());
         for arg in args.iter() {
-            if !matches!(arg.layout.backend_repr, BackendRepr::Scalar(_)) {
-                throw_unsup_format!("only scalar argument types are supported for native calls")
-            }
-            let imm = this.read_immediate(arg)?;
-            libffi_args.push(imm_to_carg(&imm, this)?);
-            // If we are passing a pointer, expose its provenance. Below, all exposed memory
-            // (previously exposed and new exposed) will then be properly prepared.
-            if matches!(arg.layout.ty.kind(), ty::RawPtr(..)) {
-                let ptr = imm.to_scalar().to_pointer(this)?;
-                let Some(prov) = ptr.provenance else {
-                    // Pointer without provenance may not access any memory anyway, skip.
-                    continue;
-                };
-                // The first time this happens, print a warning.
-                if !this.machine.native_call_mem_warned.replace(true) {
-                    // Newly set, so first time we get here.
-                    this.emit_diagnostic(NonHaltingDiagnostic::NativeCallSharedMem { tracing });
-                }
-
-                this.expose_provenance(prov)?;
-            }
+            libffi_args.push(this.op_to_ffi_arg(arg, tracing)?);
         }
-        // Convert arguments to `libffi::high::Arg` type.
-        let libffi_args = libffi_args
-            .iter()
-            .map(|arg| arg.arg_downcast())
-            .collect::<Vec<libffi::high::Arg<'_>>>();
 
         // Prepare all exposed memory (both previously exposed, and just newly exposed since a
         // pointer was passed as argument). Uninitialised memory is left as-is, but any data
@@ -347,8 +464,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             std::hint::black_box(alloc.get_bytes_unchecked_raw().expose_provenance());
 
             if !tracing {
-                // Expose all provenances in this allocation, since the native code can do $whatever.
-                // Can be skipped when tracing; in that case we'll expose just the actually-read parts later.
+                // Expose all provenances in this allocation, since the native code can do
+                // $whatever. Can be skipped when tracing; in that case we'll expose just the
+                // actually-read parts later.
                 for prov in alloc.provenance().provenances() {
                     this.expose_provenance(prov)?;
                 }
@@ -358,7 +476,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             if info.mutbl.is_mut() {
                 let (alloc, cx) = this.get_alloc_raw_mut(alloc_id)?;
                 // These writes could initialize everything and wreck havoc with the pointers.
-                // We can skip that when tracing; in that case we'll later do that only for the memory that got actually written.
+                // We can skip that when tracing; in that case we'll later do that only for the
+                // memory that got actually written.
                 if !tracing {
                     alloc.process_native_write(&cx.tcx, None);
                 }
@@ -371,7 +490,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
         // Call the function and store output, depending on return type in the function signature.
         let (ret, maybe_memevents) =
-            this.call_native_with_args(link_name, dest, code_ptr, libffi_args)?;
+            this.call_native_with_args(link_name, dest, code_ptr, &mut libffi_args)?;
 
         if tracing {
             this.tracing_apply_accesses(maybe_memevents.unwrap())?;
@@ -381,83 +500,3 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         interp_ok(true)
     }
 }
-
-#[derive(Debug, Clone)]
-/// Enum of supported arguments to external C functions.
-// We introduce this enum instead of just calling `ffi::arg` and storing a list
-// of `libffi::high::Arg` directly, because the `libffi::high::Arg` just wraps a reference
-// to the value it represents: https://docs.rs/libffi/latest/libffi/high/call/struct.Arg.html
-// and we need to store a copy of the value, and pass a reference to this copy to C instead.
-enum CArg {
-    /// 8-bit signed integer.
-    Int8(i8),
-    /// 16-bit signed integer.
-    Int16(i16),
-    /// 32-bit signed integer.
-    Int32(i32),
-    /// 64-bit signed integer.
-    Int64(i64),
-    /// isize.
-    ISize(isize),
-    /// 8-bit unsigned integer.
-    UInt8(u8),
-    /// 16-bit unsigned integer.
-    UInt16(u16),
-    /// 32-bit unsigned integer.
-    UInt32(u32),
-    /// 64-bit unsigned integer.
-    UInt64(u64),
-    /// usize.
-    USize(usize),
-    /// Raw pointer, stored as C's `void*`.
-    RawPtr(*mut std::ffi::c_void),
-}
-
-impl<'a> CArg {
-    /// Convert a `CArg` to a `libffi` argument type.
-    fn arg_downcast(&'a self) -> libffi::high::Arg<'a> {
-        match self {
-            CArg::Int8(i) => ffi::arg(i),
-            CArg::Int16(i) => ffi::arg(i),
-            CArg::Int32(i) => ffi::arg(i),
-            CArg::Int64(i) => ffi::arg(i),
-            CArg::ISize(i) => ffi::arg(i),
-            CArg::UInt8(i) => ffi::arg(i),
-            CArg::UInt16(i) => ffi::arg(i),
-            CArg::UInt32(i) => ffi::arg(i),
-            CArg::UInt64(i) => ffi::arg(i),
-            CArg::USize(i) => ffi::arg(i),
-            CArg::RawPtr(i) => ffi::arg(i),
-        }
-    }
-}
-
-/// Extract the scalar value from the result of reading a scalar from the machine,
-/// and convert it to a `CArg`.
-fn imm_to_carg<'tcx>(v: &ImmTy<'tcx>, cx: &impl HasDataLayout) -> InterpResult<'tcx, CArg> {
-    interp_ok(match v.layout.ty.kind() {
-        // If the primitive provided can be converted to a type matching the type pattern
-        // then create a `CArg` of this primitive value with the corresponding `CArg` constructor.
-        // the ints
-        ty::Int(IntTy::I8) => CArg::Int8(v.to_scalar().to_i8()?),
-        ty::Int(IntTy::I16) => CArg::Int16(v.to_scalar().to_i16()?),
-        ty::Int(IntTy::I32) => CArg::Int32(v.to_scalar().to_i32()?),
-        ty::Int(IntTy::I64) => CArg::Int64(v.to_scalar().to_i64()?),
-        ty::Int(IntTy::Isize) =>
-            CArg::ISize(v.to_scalar().to_target_isize(cx)?.try_into().unwrap()),
-        // the uints
-        ty::Uint(UintTy::U8) => CArg::UInt8(v.to_scalar().to_u8()?),
-        ty::Uint(UintTy::U16) => CArg::UInt16(v.to_scalar().to_u16()?),
-        ty::Uint(UintTy::U32) => CArg::UInt32(v.to_scalar().to_u32()?),
-        ty::Uint(UintTy::U64) => CArg::UInt64(v.to_scalar().to_u64()?),
-        ty::Uint(UintTy::Usize) =>
-            CArg::USize(v.to_scalar().to_target_usize(cx)?.try_into().unwrap()),
-        ty::RawPtr(..) => {
-            let s = v.to_scalar().to_pointer(cx)?.addr();
-            // This relies on the `expose_provenance` in the `visit_reachable_allocs` callback
-            // above.
-            CArg::RawPtr(std::ptr::with_exposed_provenance_mut(s.bytes_usize()))
-        }
-        _ => throw_unsup_format!("unsupported argument type for native call: {}", v.layout.ty),
-    })
-}
diff --git a/src/tools/miri/src/shims/native_lib/trace/child.rs b/src/tools/miri/src/shims/native_lib/trace/child.rs
index b998ba822dd..95b0617a026 100644
--- a/src/tools/miri/src/shims/native_lib/trace/child.rs
+++ b/src/tools/miri/src/shims/native_lib/trace/child.rs
@@ -90,14 +90,6 @@ impl Supervisor {
         // Unwinding might be messed up due to partly protected memory, so let's abort if something
         // breaks inside here.
         let res = std::panic::abort_unwind(|| {
-            // SAFETY: We do not access machine memory past this point until the
-            // supervisor is ready to allow it.
-            // FIXME: this is sketchy, as technically the memory is still in the Rust Abstract Machine,
-            // and the compiler would be allowed to reorder accesses below this block...
-            unsafe {
-                Self::protect_pages(alloc.pages(), mman::ProtFlags::PROT_NONE).unwrap();
-            }
-
             // Send over the info.
             // NB: if we do not wait to receive a blank confirmation response, it is
             // possible that the supervisor is alerted of the SIGSTOP *before* it has
@@ -110,16 +102,14 @@ impl Supervisor {
             // count.
             signal::raise(signal::SIGSTOP).unwrap();
 
-            let res = f();
+            // SAFETY: We have coordinated with the supervisor to ensure that this memory will keep
+            // working as normal, just with extra tracing. So even if the compiler moves memory
+            // accesses down to after the `mprotect`, they won't actually segfault.
+            unsafe {
+                Self::protect_pages(alloc.pages(), mman::ProtFlags::PROT_NONE).unwrap();
+            }
 
-            // We can't use IPC channels here to signal that FFI mode has ended,
-            // since they might allocate memory which could get us stuck in a SIGTRAP
-            // with no easy way out! While this could be worked around, it is much
-            // simpler and more robust to simply use the signals which are left for
-            // arbitrary usage. Since this will block until we are continued by the
-            // supervisor, we can assume past this point that everything is back to
-            // normal.
-            signal::raise(signal::SIGUSR1).unwrap();
+            let res = f();
 
             // SAFETY: We set memory back to normal, so this is safe.
             unsafe {
@@ -130,6 +120,12 @@ impl Supervisor {
                 .unwrap();
             }
 
+            // Signal the supervisor that we are done. Will block until the supervisor continues us.
+            // This will also shut down the segfault handler, so it's important that all memory is
+            // reset back to normal above. There must not be a window in time where accessing the
+            // pages we protected above actually causes the program to abort.
+            signal::raise(signal::SIGUSR1).unwrap();
+
             res
         });
 
diff --git a/src/tools/miri/src/shims/native_lib/trace/parent.rs b/src/tools/miri/src/shims/native_lib/trace/parent.rs
index 83f6c7a13fc..3ae98259ab3 100644
--- a/src/tools/miri/src/shims/native_lib/trace/parent.rs
+++ b/src/tools/miri/src/shims/native_lib/trace/parent.rs
@@ -18,6 +18,11 @@ const ARCH_WORD_SIZE: usize = 4;
 #[cfg(target_arch = "x86_64")]
 const ARCH_WORD_SIZE: usize = 8;
 
+// x86 max instruction length is 15 bytes:
+// https://www.intel.com/content/www/us/en/developer/articles/technical/intel-sdm.html
+// See vol. 3B section 24.25.
+const ARCH_MAX_INSTR_SIZE: usize = 15;
+
 /// The address of the page set to be edited, initialised to a sentinel null
 /// pointer.
 static PAGE_ADDR: AtomicPtr<u8> = AtomicPtr::new(std::ptr::null_mut());
@@ -132,10 +137,10 @@ impl Iterator for ChildListener {
                                 return Some(ExecEvent::Syscall(pid));
                             },
                         // Child with the given pid was stopped by the given signal.
-                        // It's somewhat dubious when this is returned instead of
-                        // WaitStatus::Stopped, but for our purposes they are the
-                        // same thing.
-                        wait::WaitStatus::PtraceEvent(pid, signal, _) =>
+                        // It's somewhat unclear when which of these two is returned;
+                        // we just treat them the same.
+                        wait::WaitStatus::Stopped(pid, signal)
+                        | wait::WaitStatus::PtraceEvent(pid, signal, _) =>
                             if self.attached {
                                 // This is our end-of-FFI signal!
                                 if signal == signal::SIGUSR1 {
@@ -148,19 +153,6 @@ impl Iterator for ChildListener {
                                 // Just pass along the signal.
                                 ptrace::cont(pid, signal).unwrap();
                             },
-                        // Child was stopped at the given signal. Same logic as for
-                        // WaitStatus::PtraceEvent.
-                        wait::WaitStatus::Stopped(pid, signal) =>
-                            if self.attached {
-                                if signal == signal::SIGUSR1 {
-                                    self.attached = false;
-                                    return Some(ExecEvent::End);
-                                } else {
-                                    return Some(ExecEvent::Status(pid, signal));
-                                }
-                            } else {
-                                ptrace::cont(pid, signal).unwrap();
-                            },
                         _ => (),
                     },
                 // This case should only trigger when all children died.
@@ -250,7 +242,7 @@ pub fn sv_loop(
                 // We can't trust simply calling `Pid::this()` in the child process to give the right
                 // PID for us, so we get it this way.
                 curr_pid = wait_for_signal(None, signal::SIGSTOP, InitialCont::No).unwrap();
-
+                // Continue until next syscall.
                 ptrace::syscall(curr_pid, None).unwrap();
             }
             // Child wants to end tracing.
@@ -289,8 +281,7 @@ pub fn sv_loop(
                         }
                     }
                 },
-            // Child entered a syscall; we wait for exits inside of this, so it
-            // should never trigger on return from a syscall we care about.
+            // Child entered or exited a syscall. For now we ignore this and just continue.
             ExecEvent::Syscall(pid) => {
                 ptrace::syscall(pid, None).unwrap();
             }
@@ -344,8 +335,8 @@ fn wait_for_signal(
                 return Err(ExecEnd(Some(code)));
             }
             wait::WaitStatus::Signaled(_, _, _) => return Err(ExecEnd(None)),
-            wait::WaitStatus::Stopped(pid, signal) => (signal, pid),
-            wait::WaitStatus::PtraceEvent(pid, signal, _) => (signal, pid),
+            wait::WaitStatus::Stopped(pid, signal)
+            | wait::WaitStatus::PtraceEvent(pid, signal, _) => (signal, pid),
             // This covers PtraceSyscall and variants that are impossible with
             // the flags set (e.g. WaitStatus::StillAlive).
             _ => {
@@ -486,7 +477,27 @@ fn handle_segfault(
     let stack_ptr = ch_stack.strict_add(CALLBACK_STACK_SIZE / 2);
     let regs_bak = ptrace::getregs(pid).unwrap();
     let mut new_regs = regs_bak;
-    let ip_prestep = regs_bak.ip();
+
+    // Read at least one instruction from the ip. It's possible that the instruction
+    // that triggered the segfault was short and at the end of the mapped text area,
+    // so some of these reads may fail; in that case, just write empty bytes. If all
+    // reads failed, the disassembler will report an error.
+    let instr = (0..(ARCH_MAX_INSTR_SIZE.div_ceil(ARCH_WORD_SIZE)))
+        .flat_map(|ofs| {
+            // This reads one word of memory; we divided by `ARCH_WORD_SIZE` above to compensate for that.
+            ptrace::read(
+                pid,
+                std::ptr::without_provenance_mut(
+                    regs_bak.ip().strict_add(ARCH_WORD_SIZE.strict_mul(ofs)),
+                ),
+            )
+            .unwrap_or_default()
+            .to_ne_bytes()
+        })
+        .collect::<Vec<_>>();
+
+    // Now figure out the size + type of access and log it down.
+    capstone_disassemble(&instr, addr, cs, acc_events).expect("Failed to disassemble instruction");
 
     // Move the instr ptr into the deprotection code.
     #[expect(clippy::as_conversions)]
@@ -526,33 +537,8 @@ fn handle_segfault(
         ptrace::write(pid, std::ptr::with_exposed_provenance_mut(a), 0).unwrap();
     }
 
-    // Save registers and grab the bytes that were executed. This would
-    // be really nasty if it was a jump or similar but those thankfully
-    // won't do memory accesses and so can't trigger this!
     let regs_bak = ptrace::getregs(pid).unwrap();
     new_regs = regs_bak;
-    let ip_poststep = regs_bak.ip();
-
-    // Ensure that we've actually gone forwards.
-    assert!(ip_poststep > ip_prestep);
-    // But not by too much. 64 bytes should be "big enough" on ~any architecture.
-    assert!(ip_prestep.strict_add(64) > ip_poststep);
-
-    // We need to do reads/writes in word-sized chunks.
-    let diff = (ip_poststep.strict_sub(ip_prestep)).div_ceil(ARCH_WORD_SIZE);
-    let instr = (ip_prestep..ip_prestep.strict_add(diff)).fold(vec![], |mut ret, ip| {
-        // This only needs to be a valid pointer in the child process, not ours.
-        ret.append(
-            &mut ptrace::read(pid, std::ptr::without_provenance_mut(ip))
-                .unwrap()
-                .to_ne_bytes()
-                .to_vec(),
-        );
-        ret
-    });
-
-    // Now figure out the size + type of access and log it down.
-    capstone_disassemble(&instr, addr, cs, acc_events).expect("Failed to disassemble instruction");
 
     // Reprotect everything and continue.
     #[expect(clippy::as_conversions)]
diff --git a/src/tools/miri/src/shims/time.rs b/src/tools/miri/src/shims/time.rs
index b5b35797fec..6e56fdfe35a 100644
--- a/src/tools/miri/src/shims/time.rs
+++ b/src/tools/miri/src/shims/time.rs
@@ -322,8 +322,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
         // Since our emulated ticks in `mach_absolute_time` *are* nanoseconds,
         // no scaling needs to happen.
-        let (numer, denom) = (1, 1);
-        this.write_int_fields(&[numer.into(), denom.into()], &info)?;
+        let (numerator, denom) = (1, 1);
+        this.write_int_fields(&[numerator.into(), denom.into()], &info)?;
 
         interp_ok(Scalar::from_i32(0)) // KERN_SUCCESS
     }
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 04c5d28838b..6cb0d221fc0 100644
--- a/src/tools/miri/src/shims/unix/android/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/android/foreign_items.rs
@@ -4,13 +4,14 @@ use rustc_span::Symbol;
 use rustc_target::callconv::FnAbi;
 
 use crate::shims::unix::android::thread::prctl;
+use crate::shims::unix::env::EvalContextExt as _;
 use crate::shims::unix::linux_like::epoll::EvalContextExt as _;
 use crate::shims::unix::linux_like::eventfd::EvalContextExt as _;
 use crate::shims::unix::linux_like::syscall::syscall;
 use crate::*;
 
-pub fn is_dyn_sym(_name: &str) -> bool {
-    false
+pub fn is_dyn_sym(name: &str) -> bool {
+    matches!(name, "gettid")
 }
 
 impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
@@ -54,6 +55,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?;
             }
 
+            "gettid" => {
+                let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
+                let result = this.unix_gettid(link_name.as_str())?;
+                this.write_scalar(result, dest)?;
+            }
+
             // Dynamically invoked syscalls
             "syscall" => syscall(this, link_name, abi, args, dest)?,
 
diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs
index e226a55d8b1..95e26ef5d5d 100644
--- a/src/tools/miri/src/shims/unix/fd.rs
+++ b/src/tools/miri/src/shims/unix/fd.rs
@@ -264,14 +264,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             return this.set_last_error_and_return(LibcError("EBADF"), dest);
         };
 
+        // Handle the zero-sized case. The man page says:
+        // > If count is zero, read() may detect the errors described below.  In the absence of any
+        // > errors, or if read() does not check for errors, a read() with a count of 0 returns zero
+        // > and has no other effects.
+        if count == 0 {
+            this.write_null(dest)?;
+            return interp_ok(());
+        }
         // Non-deterministically decide to further reduce the count, simulating a partial read (but
-        // never to 0, that has different behavior).
-        let count =
-            if fd.nondet_short_accesses() && count >= 2 && this.machine.rng.get_mut().random() {
-                count / 2
-            } else {
-                count
-            };
+        // never to 0, that would indicate EOF).
+        let count = if this.machine.short_fd_operations
+            && fd.short_fd_operations()
+            && count >= 2
+            && this.machine.rng.get_mut().random()
+        {
+            count / 2 // since `count` is at least 2, the result is still at least 1
+        } else {
+            count
+        };
 
         trace!("read: FD mapped to {fd:?}");
         // We want to read at most `count` bytes. We are sure that `count` is not negative
@@ -338,14 +349,29 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             return this.set_last_error_and_return(LibcError("EBADF"), dest);
         };
 
-        // Non-deterministically decide to further reduce the count, simulating a partial write (but
-        // never to 0, that has different behavior).
-        let count =
-            if fd.nondet_short_accesses() && count >= 2 && this.machine.rng.get_mut().random() {
-                count / 2
-            } else {
-                count
-            };
+        // Handle the zero-sized case. The man page says:
+        // > If count is zero and fd refers to a regular file, then write() may return a failure
+        // > status if one of the errors below is detected.  If no errors are detected, or error
+        // > detection is not performed, 0 is returned without causing any other effect.   If  count
+        // > is  zero  and  fd refers to a file other than a regular file, the results are not
+        // > specified.
+        if count == 0 {
+            // For now let's not open the can of worms of what exactly "not specified" could mean...
+            this.write_null(dest)?;
+            return interp_ok(());
+        }
+        // Non-deterministically decide to further reduce the count, simulating a partial write.
+        // We avoid reducing the write size to 0: the docs seem to be entirely fine with that,
+        // but the standard library is not (https://github.com/rust-lang/rust/issues/145959).
+        let count = if this.machine.short_fd_operations
+            && fd.short_fd_operations()
+            && count >= 2
+            && this.machine.rng.get_mut().random()
+        {
+            count / 2
+        } else {
+            count
+        };
 
         let finish = {
             let dest = dest.clone();
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 9e247053fbc..413df85ee3a 100644
--- a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs
@@ -159,7 +159,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 let result = this.macos_fbsd_readdir_r(dirp, entry, result)?;
                 this.write_scalar(result, dest)?;
             }
-
+            "readdir" | "readdir@FBSD_1.0" => {
+                let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
+                let result = this.readdir64("dirent", dirp)?;
+                this.write_scalar(result, dest)?;
+            }
             // Miscellaneous
             "__error" => {
                 let [] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs
index f9bcacf64c4..22bec9bd839 100644
--- a/src/tools/miri/src/shims/unix/fs.rs
+++ b/src/tools/miri/src/shims/unix/fs.rs
@@ -900,14 +900,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         }
     }
 
-    fn linux_solarish_readdir64(
-        &mut self,
-        dirent_type: &str,
-        dirp_op: &OpTy<'tcx>,
-    ) -> InterpResult<'tcx, Scalar> {
+    fn readdir64(&mut self, dirent_type: &str, dirp_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
         let this = self.eval_context_mut();
 
-        if !matches!(&*this.tcx.sess.target.os, "linux" | "solaris" | "illumos") {
+        if !matches!(&*this.tcx.sess.target.os, "linux" | "solaris" | "illumos" | "freebsd") {
             panic!("`linux_solaris_readdir64` should not be called on {}", this.tcx.sess.target.os);
         }
 
@@ -926,6 +922,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
         let entry = match open_dir.read_dir.next() {
             Some(Ok(dir_entry)) => {
+                // If the host is a Unix system, fill in the inode number with its real value.
+                // If not, use 0 as a fallback value.
+                #[cfg(unix)]
+                let ino = std::os::unix::fs::DirEntryExt::ino(&dir_entry);
+                #[cfg(not(unix))]
+                let ino = 0u64;
+
                 // Write the directory entry into a newly allocated buffer.
                 // The name is written with write_bytes, while the rest of the
                 // dirent64 (or dirent) struct is written using write_int_fields.
@@ -947,6 +950,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 //     pub d_reclen: c_ushort,
                 //     pub d_name: [c_char; 3],
                 // }
+                //
+                // On FreeBSD:
+                // pub struct dirent{
+                //     pub d_fileno: uint32_t,
+                //     pub d_reclen: uint16_t,
+                //     pub d_type: uint8_t,
+                //     pub d_namlen: uint8_t,
+                //     pub d_name: [c_char; 256]
+                // }
 
                 let mut name = dir_entry.file_name(); // not a Path as there are no separators!
                 name.push("\0"); // Add a NUL terminator
@@ -965,31 +977,35 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     MiriMemoryKind::Runtime.into(),
                     AllocInit::Uninit,
                 )?;
-                let entry: Pointer = entry.into();
+                let entry = this.ptr_to_mplace(entry.into(), dirent_layout);
 
-                // If the host is a Unix system, fill in the inode number with its real value.
-                // If not, use 0 as a fallback value.
-                #[cfg(unix)]
-                let ino = std::os::unix::fs::DirEntryExt::ino(&dir_entry);
-                #[cfg(not(unix))]
-                let ino = 0u64;
-
-                let file_type = this.file_type_to_d_type(dir_entry.file_type())?;
+                // Write common fields
+                let ino_name =
+                    if this.tcx.sess.target.os == "freebsd" { "d_fileno" } else { "d_ino" };
                 this.write_int_fields_named(
-                    &[("d_ino", ino.into()), ("d_off", 0), ("d_reclen", size.into())],
-                    &this.ptr_to_mplace(entry, dirent_layout),
+                    &[(ino_name, ino.into()), ("d_reclen", size.into())],
+                    &entry,
                 )?;
 
-                if let Some(d_type) = this
-                    .try_project_field_named(&this.ptr_to_mplace(entry, dirent_layout), "d_type")?
-                {
+                // Write "optional" fields.
+                if let Some(d_off) = this.try_project_field_named(&entry, "d_off")? {
+                    this.write_null(&d_off)?;
+                }
+
+                if let Some(d_namlen) = this.try_project_field_named(&entry, "d_namlen")? {
+                    this.write_int(name_len.strict_sub(1), &d_namlen)?;
+                }
+
+                let file_type = this.file_type_to_d_type(dir_entry.file_type())?;
+                if let Some(d_type) = this.try_project_field_named(&entry, "d_type")? {
                     this.write_int(file_type, &d_type)?;
                 }
 
-                let name_ptr = entry.wrapping_offset(Size::from_bytes(d_name_offset), this);
+                // The name is not a normal field, we already computed the offset above.
+                let name_ptr = entry.ptr().wrapping_offset(Size::from_bytes(d_name_offset), this);
                 this.write_bytes_ptr(name_ptr, name_bytes.iter().copied())?;
 
-                Some(entry)
+                Some(entry.ptr())
             }
             None => {
                 // end of stream: return NULL
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 e7e0c3b6ecd..79052698f4b 100644
--- a/src/tools/miri/src/shims/unix/linux/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/linux/foreign_items.rs
@@ -38,7 +38,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // File related shims
             "readdir64" => {
                 let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
-                let result = this.linux_solarish_readdir64("dirent64", dirp)?;
+                let result = this.readdir64("dirent64", dirp)?;
                 this.write_scalar(result, dest)?;
             }
             "sync_file_range" => {
diff --git a/src/tools/miri/src/shims/unix/linux_like/epoll.rs b/src/tools/miri/src/shims/unix/linux_like/epoll.rs
index d460abc783d..3133c149293 100644
--- a/src/tools/miri/src/shims/unix/linux_like/epoll.rs
+++ b/src/tools/miri/src/shims/unix/linux_like/epoll.rs
@@ -608,7 +608,7 @@ fn check_and_update_one_event_interest<'tcx>(
         // If we are tracking data races, remember the current clock so we can sync with it later.
         ecx.release_clock(|clock| {
             event_instance.clock.clone_from(clock);
-        });
+        })?;
         // Triggers the notification by inserting it to the ready list.
         ready_list.insert(epoll_key, event_instance);
         interp_ok(true)
@@ -639,7 +639,7 @@ fn return_ready_list<'tcx>(
                 &des.1,
             )?;
             // Synchronize waking thread with the event of interest.
-            ecx.acquire_clock(&epoll_event_instance.clock);
+            ecx.acquire_clock(&epoll_event_instance.clock)?;
 
             num_of_events = num_of_events.strict_add(1);
         } else {
diff --git a/src/tools/miri/src/shims/unix/linux_like/eventfd.rs b/src/tools/miri/src/shims/unix/linux_like/eventfd.rs
index 2d35ef064db..5d4f207d365 100644
--- a/src/tools/miri/src/shims/unix/linux_like/eventfd.rs
+++ b/src/tools/miri/src/shims/unix/linux_like/eventfd.rs
@@ -37,11 +37,6 @@ impl FileDescription for EventFd {
         "event"
     }
 
-    fn nondet_short_accesses(&self) -> bool {
-        // We always read and write exactly one `u64`.
-        false
-    }
-
     fn close<'tcx>(
         self,
         _communicate_allowed: bool,
@@ -204,7 +199,7 @@ fn eventfd_write<'tcx>(
             // Future `read` calls will synchronize with this write, so update the FD clock.
             ecx.release_clock(|clock| {
                 eventfd.clock.borrow_mut().join(clock);
-            });
+            })?;
 
             // Store new counter value.
             eventfd.counter.set(new_count);
@@ -299,7 +294,7 @@ fn eventfd_read<'tcx>(
         );
     } else {
         // Synchronize with all prior `write` calls to this FD.
-        ecx.acquire_clock(&eventfd.clock.borrow());
+        ecx.acquire_clock(&eventfd.clock.borrow())?;
 
         // Return old counter value into user-space buffer.
         ecx.write_int(counter, &buf_place)?;
diff --git a/src/tools/miri/src/shims/unix/macos/sync.rs b/src/tools/miri/src/shims/unix/macos/sync.rs
index 05616dd5a42..c4ddff7805e 100644
--- a/src/tools/miri/src/shims/unix/macos/sync.rs
+++ b/src/tools/miri/src/shims/unix/macos/sync.rs
@@ -296,7 +296,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
             this.mutex_enqueue_and_block(mutex_ref, None);
         } else {
-            this.mutex_lock(&mutex_ref);
+            this.mutex_lock(&mutex_ref)?;
         }
 
         interp_ok(())
@@ -321,7 +321,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // reentrancy.
             this.write_scalar(Scalar::from_bool(false), dest)?;
         } else {
-            this.mutex_lock(&mutex_ref);
+            this.mutex_lock(&mutex_ref)?;
             this.write_scalar(Scalar::from_bool(true), dest)?;
         }
 
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 d7033a65fe2..31269bf00c9 100644
--- a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs
@@ -106,7 +106,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             }
             "readdir" => {
                 let [dirp] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
-                let result = this.linux_solarish_readdir64("dirent", dirp)?;
+                let result = this.readdir64("dirent", dirp)?;
                 this.write_scalar(result, dest)?;
             }
 
diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs
index 5ad4fd501a6..cefd8b81ac1 100644
--- a/src/tools/miri/src/shims/unix/sync.rs
+++ b/src/tools/miri/src/shims/unix/sync.rs
@@ -498,14 +498,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.mutex_ref);
+                        this.mutex_lock(&mutex.mutex_ref)?;
                         0
                     }
                 }
             }
         } else {
             // The mutex is unlocked. Let's lock it.
-            this.mutex_lock(&mutex.mutex_ref);
+            this.mutex_lock(&mutex.mutex_ref)?;
             0
         };
         this.write_scalar(Scalar::from_i32(ret), dest)?;
@@ -525,14 +525,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.mutex_ref);
+                        this.mutex_lock(&mutex.mutex_ref)?;
                         0
                     }
                 }
             }
         } else {
             // The mutex is unlocked. Let's lock it.
-            this.mutex_lock(&mutex.mutex_ref);
+            this.mutex_lock(&mutex.mutex_ref)?;
             0
         }))
     }
@@ -600,7 +600,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 dest.clone(),
             );
         } else {
-            this.rwlock_reader_lock(&rwlock.rwlock_ref);
+            this.rwlock_reader_lock(&rwlock.rwlock_ref)?;
             this.write_null(dest)?;
         }
 
@@ -615,7 +615,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         if rwlock.rwlock_ref.is_write_locked() {
             interp_ok(Scalar::from_i32(this.eval_libc_i32("EBUSY")))
         } else {
-            this.rwlock_reader_lock(&rwlock.rwlock_ref);
+            this.rwlock_reader_lock(&rwlock.rwlock_ref)?;
             interp_ok(Scalar::from_i32(0))
         }
     }
@@ -648,7 +648,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 dest.clone(),
             );
         } else {
-            this.rwlock_writer_lock(&rwlock.rwlock_ref);
+            this.rwlock_writer_lock(&rwlock.rwlock_ref)?;
             this.write_null(dest)?;
         }
 
@@ -663,7 +663,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         if rwlock.rwlock_ref.is_locked() {
             interp_ok(Scalar::from_i32(this.eval_libc_i32("EBUSY")))
         } else {
-            this.rwlock_writer_lock(&rwlock.rwlock_ref);
+            this.rwlock_writer_lock(&rwlock.rwlock_ref)?;
             interp_ok(Scalar::from_i32(0))
         }
     }
diff --git a/src/tools/miri/src/shims/unix/unnamed_socket.rs b/src/tools/miri/src/shims/unix/unnamed_socket.rs
index 817ddd7954d..81703d6e176 100644
--- a/src/tools/miri/src/shims/unix/unnamed_socket.rs
+++ b/src/tools/miri/src/shims/unix/unnamed_socket.rs
@@ -123,6 +123,14 @@ impl FileDescription for AnonSocket {
         anonsocket_write(self, ptr, len, ecx, finish)
     }
 
+    fn short_fd_operations(&self) -> bool {
+        // Pipes guarantee that sufficiently small accesses are not broken apart:
+        // <https://pubs.opengroup.org/onlinepubs/9799919799/functions/write.html#tag_17_699_08>.
+        // For now, we don't bother checking for the size, and just entirely disable
+        // short accesses on pipes.
+        matches!(self.fd_type, AnonSocketType::Socketpair)
+    }
+
     fn as_unix<'tcx>(&self, _ecx: &MiriInterpCx<'tcx>) -> &dyn UnixFileDescription {
         self
     }
@@ -251,7 +259,7 @@ fn anonsocket_write<'tcx>(
         // Remember this clock so `read` can synchronize with us.
         ecx.release_clock(|clock| {
             writebuf.clock.join(clock);
-        });
+        })?;
         // Do full write / partial write based on the space available.
         let write_size = len.min(available_space);
         let actual_write_size = ecx.write_to_host(&mut writebuf.buf, write_size, ptr)?.unwrap();
@@ -337,7 +345,7 @@ fn anonsocket_read<'tcx>(
         // Synchronize with all previous writes to this buffer.
         // FIXME: this over-synchronizes; a more precise approach would be to
         // only sync with the writes whose data we will read.
-        ecx.acquire_clock(&readbuf.clock);
+        ecx.acquire_clock(&readbuf.clock)?;
 
         // Do full read / partial read based on the space available.
         // Conveniently, `read` exists on `VecDeque` and has exactly the desired behavior.
diff --git a/src/tools/miri/src/shims/wasi/foreign_items.rs b/src/tools/miri/src/shims/wasi/foreign_items.rs
index bfcdbd8130d..ffc02dc986f 100644
--- a/src/tools/miri/src/shims/wasi/foreign_items.rs
+++ b/src/tools/miri/src/shims/wasi/foreign_items.rs
@@ -35,6 +35,74 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 this.write_pointer(res, dest)?;
             }
 
+            // Standard input/output
+            // FIXME: These shims are hacks that just get basic stdout/stderr working. We can't
+            // constrain them to "std" since std itself uses the wasi crate for this.
+            "get-stdout" => {
+                let [] =
+                    this.check_shim_sig(shim_sig!(extern "C" fn() -> i32), link_name, abi, args)?;
+                this.write_scalar(Scalar::from_i32(1), dest)?; // POSIX FD number for stdout
+            }
+            "get-stderr" => {
+                let [] =
+                    this.check_shim_sig(shim_sig!(extern "C" fn() -> i32), link_name, abi, args)?;
+                this.write_scalar(Scalar::from_i32(2), dest)?; // POSIX FD number for stderr
+            }
+            "[resource-drop]output-stream" => {
+                let [handle] =
+                    this.check_shim_sig(shim_sig!(extern "C" fn(i32) -> ()), link_name, abi, args)?;
+                let handle = this.read_scalar(handle)?.to_i32()?;
+
+                if !(handle == 1 || handle == 2) {
+                    throw_unsup_format!("wasm output-stream: unsupported handle");
+                }
+                // We don't actually close these FDs, so this is a NOP.
+            }
+            "[method]output-stream.blocking-write-and-flush" => {
+                let [handle, buf, len, ret_area] = this.check_shim_sig(
+                    shim_sig!(extern "C" fn(i32, *mut _, usize, *mut _) -> ()),
+                    link_name,
+                    abi,
+                    args,
+                )?;
+                let handle = this.read_scalar(handle)?.to_i32()?;
+                let buf = this.read_pointer(buf)?;
+                let len = this.read_target_usize(len)?;
+                let ret_area = this.read_pointer(ret_area)?;
+
+                if len > 4096 {
+                    throw_unsup_format!(
+                        "wasm output-stream.blocking-write-and-flush: buffer too big"
+                    );
+                }
+                let len = usize::try_from(len).unwrap();
+                let Some(fd) = this.machine.fds.get(handle) else {
+                    throw_unsup_format!(
+                        "wasm output-stream.blocking-write-and-flush: unsupported handle"
+                    );
+                };
+                fd.write(
+                    this.machine.communicate(),
+                    buf,
+                    len,
+                    this,
+                    callback!(
+                        @capture<'tcx> {
+                            len: usize,
+                            ret_area: Pointer,
+                        }
+                        |this, result: Result<usize, IoError>| {
+                            if !matches!(result, Ok(l) if l == len) {
+                                throw_unsup_format!("wasm output-stream.blocking-write-and-flush: returning errors is not supported");
+                            }
+                            // 0 in the first byte of the ret_area indicates success.
+                            let ret = this.ptr_to_mplace(ret_area, this.machine.layouts.u8);
+                            this.write_null(&ret)?;
+                            interp_ok(())
+                    }),
+                )?;
+            }
+
             _ => return interp_ok(EmulateItemResult::NotSupported),
         }
         interp_ok(EmulateItemResult::NeedsReturn)
diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs
index 7b13f1d9080..c3ca01bbf34 100644
--- a/src/tools/miri/src/shims/windows/foreign_items.rs
+++ b/src/tools/miri/src/shims/windows/foreign_items.rs
@@ -820,6 +820,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 this.write_int(length.strict_sub(1), dest)?;
             }
 
+            "_Unwind_RaiseException" => {
+                // This is not formally part of POSIX, but it is very wide-spread on POSIX systems.
+                // It was originally specified as part of the Itanium C++ ABI:
+                // https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html#base-throw.
+                // MinGW implements _Unwind_RaiseException on top of SEH exceptions.
+                if this.tcx.sess.target.env != "gnu" {
+                    throw_unsup_format!(
+                        "`_Unwind_RaiseException` is not supported on non-MinGW Windows",
+                    );
+                }
+                // This function looks and behaves excatly like miri_start_unwind.
+                let [payload] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
+                this.handle_miri_start_unwind(payload)?;
+                return interp_ok(EmulateItemResult::NeedsUnwind);
+            }
+
             // 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() => {
@@ -880,22 +896,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 this.write_null(dest)?;
             }
 
-            "_Unwind_RaiseException" => {
-                // This is not formally part of POSIX, but it is very wide-spread on POSIX systems.
-                // It was originally specified as part of the Itanium C++ ABI:
-                // https://itanium-cxx-abi.github.io/cxx-abi/abi-eh.html#base-throw.
-                // MinGW implements _Unwind_RaiseException on top of SEH exceptions.
-                if this.tcx.sess.target.env != "gnu" {
-                    throw_unsup_format!(
-                        "`_Unwind_RaiseException` is not supported on non-MinGW Windows",
-                    );
-                }
-                // This function looks and behaves excatly like miri_start_unwind.
-                let [payload] = this.check_shim_sig_lenient(abi, CanonAbi::C, link_name, args)?;
-                this.handle_miri_start_unwind(payload)?;
-                return interp_ok(EmulateItemResult::NeedsUnwind);
-            }
-
             _ => return interp_ok(EmulateItemResult::NotSupported),
         }
 
diff --git a/src/tools/miri/src/shims/windows/handle.rs b/src/tools/miri/src/shims/windows/handle.rs
index 8a965ea316d..92d6321bed1 100644
--- a/src/tools/miri/src/shims/windows/handle.rs
+++ b/src/tools/miri/src/shims/windows/handle.rs
@@ -289,9 +289,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         }
 
         if this.ptr_is_null(target_handle_ptr)? {
-            throw_unsup_format!(
-                "`DuplicateHandle` `lpTargetHandle` parameter is null, which is unsupported"
-            );
+            throw_machine_stop!(TerminationInfo::Abort(
+                "`DuplicateHandle` `lpTargetHandle` parameter must not be null, as otherwise the \
+                newly created handle is leaked"
+                    .to_string()
+            ));
         }
 
         if options != this.eval_windows("c", "DUPLICATE_SAME_ACCESS") {
diff --git a/src/tools/miri/src/shims/windows/sync.rs b/src/tools/miri/src/shims/windows/sync.rs
index 9165e76b63d..a893999ef8e 100644
--- a/src/tools/miri/src/shims/windows/sync.rs
+++ b/src/tools/miri/src/shims/windows/sync.rs
@@ -60,7 +60,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 true
             }
             InitOnceStatus::Complete => {
-                this.init_once_observe_completed(init_once_ref);
+                this.init_once_observe_completed(init_once_ref)?;
                 this.write_scalar(this.eval_windows("c", "FALSE"), pending_place)?;
                 this.write_scalar(this.eval_windows("c", "TRUE"), dest)?;
                 true
diff --git a/src/tools/miri/test-cargo-miri/run-test.py b/src/tools/miri/test-cargo-miri/run-test.py
index 40bfe7f845f..6b3b6343b82 100755
--- a/src/tools/miri/test-cargo-miri/run-test.py
+++ b/src/tools/miri/test-cargo-miri/run-test.py
@@ -37,7 +37,7 @@ def cargo_miri(cmd, quiet = True, targets = None):
 
 def normalize_stdout(str):
     str = str.replace("src\\", "src/") # normalize paths across platforms
-    str = re.sub("finished in \\d+\\.\\d\\ds", "finished in $TIME", str) # the time keeps changing, obviously
+    str = re.sub("\\b\\d+\\.\\d+s\\b", "$TIME", str) # the time keeps changing, obviously
     return str
 
 def check_output(actual, path, name):
diff --git a/src/tools/miri/test-cargo-miri/test.default.stdout.ref b/src/tools/miri/test-cargo-miri/test.default.stdout.ref
index 2d74d82f769..ef092ef703b 100644
--- a/src/tools/miri/test-cargo-miri/test.default.stdout.ref
+++ b/src/tools/miri/test-cargo-miri/test.default.stdout.ref
@@ -14,3 +14,4 @@ running 5 tests
 .....
 test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
 
+all doctests ran in $TIME; merged doctests compilation took $TIME
diff --git a/src/tools/miri/test-cargo-miri/test.filter.stdout.ref b/src/tools/miri/test-cargo-miri/test.filter.stdout.ref
index b68bc983276..071aa5691c1 100644
--- a/src/tools/miri/test-cargo-miri/test.filter.stdout.ref
+++ b/src/tools/miri/test-cargo-miri/test.filter.stdout.ref
@@ -15,3 +15,4 @@ running 0 tests
 
 test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 5 filtered out; finished in $TIME
 
+all doctests ran in $TIME; merged doctests compilation took $TIME
diff --git a/src/tools/miri/test-cargo-miri/test.multiple_targets.stdout.ref b/src/tools/miri/test-cargo-miri/test.multiple_targets.stdout.ref
index a376530a8cf..20139e9ffe6 100644
--- a/src/tools/miri/test-cargo-miri/test.multiple_targets.stdout.ref
+++ b/src/tools/miri/test-cargo-miri/test.multiple_targets.stdout.ref
@@ -25,8 +25,10 @@ running 5 tests
 .....
 test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
 
+all doctests ran in $TIME; merged doctests compilation took $TIME
 
 running 5 tests
 .....
 test result: ok. 5 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
 
+all doctests ran in $TIME; merged doctests compilation took $TIME
diff --git a/src/tools/miri/tests/deps/Cargo.lock b/src/tools/miri/tests/deps/Cargo.lock
index 4b783ebdc4e..65ca4215c60 100644
--- a/src/tools/miri/tests/deps/Cargo.lock
+++ b/src/tools/miri/tests/deps/Cargo.lock
@@ -296,9 +296,9 @@ dependencies = [
 
 [[package]]
 name = "slab"
-version = "0.4.10"
+version = "0.4.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d"
+checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589"
 
 [[package]]
 name = "socket2"
diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.stderr
index bcec5557a3f..c7c1a769cda 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.stderr
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.stderr
@@ -8,13 +8,13 @@ LL |             assert_eq!(libc::pthread_mutex_lock(lock_copy.0.get() as *mut _
    = note: inside closure at tests/fail-dep/concurrency/libc_pthread_mutex_deadlock.rs:LL:CC
 
 error: the evaluated program deadlocked
-  --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
+  --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC
    |
 LL |         let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
    |                                                                  ^ this thread got stuck here
    |
    = 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::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.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`
diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.stderr
index 51533cd17a8..ae8b2b936a7 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.stderr
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.stderr
@@ -8,13 +8,13 @@ LL |             assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mu
    = note: inside closure at tests/fail-dep/concurrency/libc_pthread_rwlock_write_read_deadlock.rs:LL:CC
 
 error: the evaluated program deadlocked
-  --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
+  --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC
    |
 LL |         let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
    |                                                                  ^ this thread got stuck here
    |
    = 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::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.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`
diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.stderr
index 63fc7ce346e..dfa9a6e2583 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.stderr
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.stderr
@@ -8,13 +8,13 @@ LL |             assert_eq!(libc::pthread_rwlock_wrlock(lock_copy.0.get() as *mu
    = note: inside closure at tests/fail-dep/concurrency/libc_pthread_rwlock_write_write_deadlock.rs:LL:CC
 
 error: the evaluated program deadlocked
-  --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
+  --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC
    |
 LL |         let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
    |                                                                  ^ this thread got stuck here
    |
    = 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::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.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`
diff --git a/src/tools/miri/tests/fail-dep/concurrency/windows_join_detached.stderr b/src/tools/miri/tests/fail-dep/concurrency/windows_join_detached.stderr
index d8a3e058da9..47a0ebdcfef 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/windows_join_detached.stderr
+++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_detached.stderr
@@ -1,5 +1,5 @@
 error: Undefined Behavior: trying to join a detached thread
-  --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
+  --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC
    |
 LL |         let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) };
    |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
@@ -7,7 +7,7 @@ LL |         let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(
    = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
    = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
    = note: BACKTRACE:
-   = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
+   = note: inside `std::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.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`
diff --git a/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr b/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr
index 079f01c0e54..3b1181c92fd 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr
+++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_main.stderr
@@ -9,13 +9,13 @@ LL |             assert_eq!(WaitForSingleObject(MAIN_THREAD, INFINITE), WAIT_OBJ
    = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: the evaluated program deadlocked
-  --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
+  --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC
    |
 LL |         let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) };
    |                                                                                          ^ this thread got stuck here
    |
    = 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::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.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`
diff --git a/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr b/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr
index 70de94f56ba..6eefa2da1d8 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr
+++ b/src/tools/miri/tests/fail-dep/concurrency/windows_join_self.stderr
@@ -8,13 +8,13 @@ LL |             assert_eq!(WaitForSingleObject(native, INFINITE), WAIT_OBJECT_0
    = note: inside closure at tests/fail-dep/concurrency/windows_join_self.rs:LL:CC
 
 error: the evaluated program deadlocked
-  --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
+  --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC
    |
 LL |         let rc = unsafe { c::WaitForSingleObject(self.handle.as_raw_handle(), c::INFINITE) };
    |                                                                                          ^ this thread got stuck here
    |
    = 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::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.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`
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
index 4f3a56fef82..6dd6c36ab65 100644
--- 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
@@ -1,11 +1,11 @@
 error: the evaluated program deadlocked
-  --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
+  --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC
    |
 LL |         let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
    |                                                                  ^ this thread got stuck here
    |
    = 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::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.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`
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
index 5045badaa87..3154197b95e 100644
--- 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
@@ -1,11 +1,11 @@
 error: the evaluated program deadlocked
-  --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
+  --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC
    |
 LL |         let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
    |                                                                  ^ this thread got stuck here
    |
    = 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::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.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`
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
index 3713c8da392..1af44c107ff 100644
--- 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
@@ -5,13 +5,13 @@ error: the evaluated program deadlocked
    = note: BACKTRACE on thread `unnamed-ID`:
 
 error: the evaluated program deadlocked
-  --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
+  --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC
    |
 LL |         let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
    |                                                                  ^ this thread got stuck here
    |
    = 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::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.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`
diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair-close-while-blocked.stderr b/src/tools/miri/tests/fail-dep/libc/socketpair-close-while-blocked.stderr
index 47fc889b001..b85470225c6 100644
--- a/src/tools/miri/tests/fail-dep/libc/socketpair-close-while-blocked.stderr
+++ b/src/tools/miri/tests/fail-dep/libc/socketpair-close-while-blocked.stderr
@@ -1,11 +1,11 @@
 error: the evaluated program deadlocked
-  --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
+  --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC
    |
 LL |         let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
    |                                                                  ^ this thread got stuck here
    |
    = 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::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.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`
diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.stderr b/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.stderr
index 99d242ec7da..bbd2ab1c4d8 100644
--- a/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.stderr
+++ b/src/tools/miri/tests/fail-dep/libc/socketpair_block_read_twice.stderr
@@ -1,11 +1,11 @@
 error: the evaluated program deadlocked
-  --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
+  --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC
    |
 LL |         let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
    |                                                                  ^ this thread got stuck here
    |
    = 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::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.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`
diff --git a/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.stderr b/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.stderr
index f766500d331..c3cc2068173 100644
--- a/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.stderr
+++ b/src/tools/miri/tests/fail-dep/libc/socketpair_block_write_twice.stderr
@@ -1,11 +1,11 @@
 error: the evaluated program deadlocked
-  --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
+  --> RUSTLIB/std/src/sys/thread/PLATFORM.rs:LL:CC
    |
 LL |         let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
    |                                                                  ^ this thread got stuck here
    |
    = 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::sys::thread::PLATFORM::Thread::join` at RUSTLIB/std/src/sys/thread/PLATFORM.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`
diff --git a/src/tools/miri/tests/fail/alloc/alloc_error_handler.rs b/src/tools/miri/tests/fail/alloc/alloc_error_handler.rs
index 4a87411d755..35b8e20e56b 100644
--- a/src/tools/miri/tests/fail/alloc/alloc_error_handler.rs
+++ b/src/tools/miri/tests/fail/alloc/alloc_error_handler.rs
@@ -1,6 +1,4 @@
 //@error-in-other-file: aborted
-//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()"
-//@normalize-stderr-test: "\| +\^+" -> "| ^"
 #![feature(allocator_api)]
 
 use std::alloc::*;
diff --git a/src/tools/miri/tests/fail/alloc/alloc_error_handler.stderr b/src/tools/miri/tests/fail/alloc/alloc_error_handler.stderr
index d67dabd6935..bbf5d14a98a 100644
--- a/src/tools/miri/tests/fail/alloc/alloc_error_handler.stderr
+++ b/src/tools/miri/tests/fail/alloc/alloc_error_handler.stderr
@@ -1,13 +1,11 @@
 memory allocation of 4 bytes failed
 error: abnormal termination: the program aborted execution
-  --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC
+  --> RUSTLIB/std/src/alloc.rs:LL:CC
    |
-LL | ABORT()
-   | ^ abnormal termination occurred here
+LL |     crate::process::abort()
+   |     ^^^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here
    |
    = note: BACKTRACE:
-   = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC
-   = note: inside `std::process::abort` at RUSTLIB/std/src/process.rs:LL:CC
    = note: inside `std::alloc::rust_oom` at RUSTLIB/std/src/alloc.rs:LL:CC
    = note: inside `std::alloc::_::__rg_oom` at RUSTLIB/std/src/alloc.rs:LL:CC
    = note: inside `std::alloc::handle_alloc_error::rt_error` at RUSTLIB/alloc/src/alloc.rs:LL:CC
@@ -16,7 +14,7 @@ note: inside `main`
   --> tests/fail/alloc/alloc_error_handler.rs:LL:CC
    |
 LL |     handle_alloc_error(Layout::for_value(&0));
-   | ^
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
diff --git a/src/tools/miri/tests/fail/async-shared-mutable.tree.stderr b/src/tools/miri/tests/fail/async-shared-mutable.tree.stderr
index 17efb10ceb0..dc8b4f6665a 100644
--- a/src/tools/miri/tests/fail/async-shared-mutable.tree.stderr
+++ b/src/tools/miri/tests/fail/async-shared-mutable.tree.stderr
@@ -5,6 +5,7 @@ LL |             *x = 1;
    |             ^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> has state Frozen which forbids this child write access
 help: the accessed tag <TAG> was created here, in the initial state Reserved
   --> tests/fail/async-shared-mutable.rs:LL:CC
@@ -15,7 +16,7 @@ LL | |             Poll::<()>::Pending
 LL | |         })
 LL | |         .await
    | |______________^
-help: the accessed tag <TAG> later transitioned to Active due to a child write access at offsets [OFFSET]
+help: the accessed tag <TAG> later transitioned to Unique due to a child write access at offsets [OFFSET]
   --> tests/fail/async-shared-mutable.rs:LL:CC
    |
 LL |             *x = 1;
diff --git a/src/tools/miri/tests/fail/both_borrows/alias_through_mutation.tree.stderr b/src/tools/miri/tests/fail/both_borrows/alias_through_mutation.tree.stderr
index 9e992f88f0c..95b7e99dedc 100644
--- a/src/tools/miri/tests/fail/both_borrows/alias_through_mutation.tree.stderr
+++ b/src/tools/miri/tests/fail/both_borrows/alias_through_mutation.tree.stderr
@@ -5,6 +5,7 @@ LL |     let _val = *target_alias;
    |                ^^^^^^^^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> has state Disabled which forbids this child read access
 help: the accessed tag <TAG> was created here, in the initial state Frozen
   --> tests/fail/both_borrows/alias_through_mutation.rs:LL:CC
diff --git a/src/tools/miri/tests/fail/both_borrows/aliasing_mut1.tree.stderr b/src/tools/miri/tests/fail/both_borrows/aliasing_mut1.tree.stderr
index e331ff2406d..207ed3131af 100644
--- a/src/tools/miri/tests/fail/both_borrows/aliasing_mut1.tree.stderr
+++ b/src/tools/miri/tests/fail/both_borrows/aliasing_mut1.tree.stderr
@@ -5,6 +5,7 @@ LL |     *x = 1;
    |     ^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> has state Reserved (conflicted) which forbids this child write access
 help: the accessed tag <TAG> was created here, in the initial state Reserved
   --> tests/fail/both_borrows/aliasing_mut1.rs:LL:CC
diff --git a/src/tools/miri/tests/fail/both_borrows/aliasing_mut2.tree.stderr b/src/tools/miri/tests/fail/both_borrows/aliasing_mut2.tree.stderr
index a2191da0b4f..90b1b1294c7 100644
--- a/src/tools/miri/tests/fail/both_borrows/aliasing_mut2.tree.stderr
+++ b/src/tools/miri/tests/fail/both_borrows/aliasing_mut2.tree.stderr
@@ -5,6 +5,7 @@ LL |     *y = 2;
    |     ^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> has state Reserved (conflicted) which forbids this child write access
 help: the accessed tag <TAG> was created here, in the initial state Reserved
   --> tests/fail/both_borrows/aliasing_mut2.rs:LL:CC
diff --git a/src/tools/miri/tests/fail/both_borrows/aliasing_mut3.tree.stderr b/src/tools/miri/tests/fail/both_borrows/aliasing_mut3.tree.stderr
index e92cad1777b..73a50276463 100644
--- a/src/tools/miri/tests/fail/both_borrows/aliasing_mut3.tree.stderr
+++ b/src/tools/miri/tests/fail/both_borrows/aliasing_mut3.tree.stderr
@@ -5,6 +5,7 @@ LL |     *x = 1;
    |     ^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> has state Reserved (conflicted) which forbids this child write access
 help: the accessed tag <TAG> was created here, in the initial state Reserved
   --> tests/fail/both_borrows/aliasing_mut3.rs:LL:CC
diff --git a/src/tools/miri/tests/fail/both_borrows/aliasing_mut4.tree.stderr b/src/tools/miri/tests/fail/both_borrows/aliasing_mut4.tree.stderr
index e195b0a7e87..a6a6da3fa2a 100644
--- a/src/tools/miri/tests/fail/both_borrows/aliasing_mut4.tree.stderr
+++ b/src/tools/miri/tests/fail/both_borrows/aliasing_mut4.tree.stderr
@@ -5,6 +5,7 @@ LL |         crate::intrinsics::write_via_move(dest, src);
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> is foreign to the protected tag <TAG> (i.e., it is not a child)
    = help: this foreign write access would cause the protected tag <TAG> (currently Frozen) to become Disabled
    = help: protected tags must never be Disabled
diff --git a/src/tools/miri/tests/fail/both_borrows/box_exclusive_violation1.tree.stderr b/src/tools/miri/tests/fail/both_borrows/box_exclusive_violation1.tree.stderr
index 30832ae60ac..d4cfab024ba 100644
--- a/src/tools/miri/tests/fail/both_borrows/box_exclusive_violation1.tree.stderr
+++ b/src/tools/miri/tests/fail/both_borrows/box_exclusive_violation1.tree.stderr
@@ -5,6 +5,7 @@ LL |         *LEAK = 7;
    |         ^^^^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> has state Disabled which forbids this child write access
 help: the accessed tag <TAG> was created here, in the initial state Frozen
   --> tests/fail/both_borrows/box_exclusive_violation1.rs:LL:CC
diff --git a/src/tools/miri/tests/fail/both_borrows/box_noalias_violation.tree.stderr b/src/tools/miri/tests/fail/both_borrows/box_noalias_violation.tree.stderr
index d7b865d57a8..6a1f7761a41 100644
--- a/src/tools/miri/tests/fail/both_borrows/box_noalias_violation.tree.stderr
+++ b/src/tools/miri/tests/fail/both_borrows/box_noalias_violation.tree.stderr
@@ -5,8 +5,9 @@ LL |     *y
    |     ^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> is foreign to the protected tag <TAG> (i.e., it is not a child)
-   = help: this foreign read access would cause the protected tag <TAG> (currently Active) to become Disabled
+   = help: this foreign read access would cause the protected tag <TAG> (currently Unique) to become Disabled
    = help: protected tags must never be Disabled
 help: the accessed tag <TAG> was created here
   --> tests/fail/both_borrows/box_noalias_violation.rs:LL:CC
@@ -18,7 +19,7 @@ help: the protected tag <TAG> was created here, in the initial state Reserved
    |
 LL | unsafe fn test(mut x: Box<i32>, y: *const i32) -> i32 {
    |                ^^^^^
-help: the protected tag <TAG> later transitioned to Active due to a child write access at offsets [0x0..0x4]
+help: the protected tag <TAG> later transitioned to Unique due to a child write access at offsets [0x0..0x4]
   --> tests/fail/both_borrows/box_noalias_violation.rs:LL:CC
    |
 LL |     *x = 5;
diff --git a/src/tools/miri/tests/fail/both_borrows/buggy_as_mut_slice.tree.stderr b/src/tools/miri/tests/fail/both_borrows/buggy_as_mut_slice.tree.stderr
index ad5df3399c8..6588bc25df1 100644
--- a/src/tools/miri/tests/fail/both_borrows/buggy_as_mut_slice.tree.stderr
+++ b/src/tools/miri/tests/fail/both_borrows/buggy_as_mut_slice.tree.stderr
@@ -5,6 +5,7 @@ LL |     v2[1] = 7;
    |     ^^^^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> has state Disabled which forbids this child write access
 help: the accessed tag <TAG> was created here, in the initial state Reserved
   --> tests/fail/both_borrows/buggy_as_mut_slice.rs:LL:CC
diff --git a/src/tools/miri/tests/fail/both_borrows/buggy_split_at_mut.tree.stderr b/src/tools/miri/tests/fail/both_borrows/buggy_split_at_mut.tree.stderr
index 4ab49e75adb..6ef27515fcd 100644
--- a/src/tools/miri/tests/fail/both_borrows/buggy_split_at_mut.tree.stderr
+++ b/src/tools/miri/tests/fail/both_borrows/buggy_split_at_mut.tree.stderr
@@ -5,6 +5,7 @@ LL |     b[1] = 6;
    |     ^^^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> has state Disabled which forbids this child write access
 help: the accessed tag <TAG> was created here, in the initial state Reserved
   --> tests/fail/both_borrows/buggy_split_at_mut.rs:LL:CC
diff --git a/src/tools/miri/tests/fail/both_borrows/illegal_write1.tree.stderr b/src/tools/miri/tests/fail/both_borrows/illegal_write1.tree.stderr
index b718e19ff5a..4ffc7a75e66 100644
--- a/src/tools/miri/tests/fail/both_borrows/illegal_write1.tree.stderr
+++ b/src/tools/miri/tests/fail/both_borrows/illegal_write1.tree.stderr
@@ -5,6 +5,7 @@ LL |         unsafe { *x = 42 };
    |                  ^^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> has state Frozen which forbids this child write access
 help: the accessed tag <TAG> was created here, in the initial state Frozen
   --> tests/fail/both_borrows/illegal_write1.rs:LL:CC
diff --git a/src/tools/miri/tests/fail/both_borrows/illegal_write5.tree.stderr b/src/tools/miri/tests/fail/both_borrows/illegal_write5.tree.stderr
index d4c78c4bd33..fc5b12da70e 100644
--- a/src/tools/miri/tests/fail/both_borrows/illegal_write5.tree.stderr
+++ b/src/tools/miri/tests/fail/both_borrows/illegal_write5.tree.stderr
@@ -5,6 +5,7 @@ LL |     let _val = *xref;
    |                ^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> has state Disabled which forbids this child read access
 help: the accessed tag <TAG> was created here, in the initial state Reserved
   --> tests/fail/both_borrows/illegal_write5.rs:LL:CC
diff --git a/src/tools/miri/tests/fail/both_borrows/illegal_write6.tree.stderr b/src/tools/miri/tests/fail/both_borrows/illegal_write6.tree.stderr
index e537bc18c05..1547a6ca73a 100644
--- a/src/tools/miri/tests/fail/both_borrows/illegal_write6.tree.stderr
+++ b/src/tools/miri/tests/fail/both_borrows/illegal_write6.tree.stderr
@@ -5,8 +5,9 @@ LL |     unsafe { *y = 2 };
    |              ^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> is foreign to the protected tag <TAG> (i.e., it is not a child)
-   = help: this foreign write access would cause the protected tag <TAG> (currently Active) to become Disabled
+   = help: this foreign write access would cause the protected tag <TAG> (currently Unique) to become Disabled
    = help: protected tags must never be Disabled
 help: the accessed tag <TAG> was created here
   --> tests/fail/both_borrows/illegal_write6.rs:LL:CC
@@ -18,7 +19,7 @@ help: the protected tag <TAG> was created here, in the initial state Reserved
    |
 LL | fn foo(a: &mut u32, y: *mut u32) -> u32 {
    |        ^
-help: the protected tag <TAG> later transitioned to Active due to a child write access at offsets [0x0..0x4]
+help: the protected tag <TAG> later transitioned to Unique due to a child write access at offsets [0x0..0x4]
   --> tests/fail/both_borrows/illegal_write6.rs:LL:CC
    |
 LL |     *a = 1;
diff --git a/src/tools/miri/tests/fail/both_borrows/invalidate_against_protector2.tree.stderr b/src/tools/miri/tests/fail/both_borrows/invalidate_against_protector2.tree.stderr
index af90e75d384..90a89e48e61 100644
--- a/src/tools/miri/tests/fail/both_borrows/invalidate_against_protector2.tree.stderr
+++ b/src/tools/miri/tests/fail/both_borrows/invalidate_against_protector2.tree.stderr
@@ -5,6 +5,7 @@ LL |     unsafe { *x = 0 };
    |              ^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> is foreign to the protected tag <TAG> (i.e., it is not a child)
    = help: this foreign write access would cause the protected tag <TAG> (currently Frozen) to become Disabled
    = help: protected tags must never be Disabled
diff --git a/src/tools/miri/tests/fail/both_borrows/invalidate_against_protector3.tree.stderr b/src/tools/miri/tests/fail/both_borrows/invalidate_against_protector3.tree.stderr
index 5d5f5e59ead..8bac71dcd46 100644
--- a/src/tools/miri/tests/fail/both_borrows/invalidate_against_protector3.tree.stderr
+++ b/src/tools/miri/tests/fail/both_borrows/invalidate_against_protector3.tree.stderr
@@ -5,6 +5,7 @@ LL |     unsafe { *x = 0 };
    |              ^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> (root of the allocation) is foreign to the protected tag <TAG> (i.e., it is not a child)
    = help: this foreign write access would cause the protected tag <TAG> (currently Frozen) to become Disabled
    = help: protected tags must never be Disabled
diff --git a/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-1.rs b/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-1.rs
index dd7dae9cecf..d907c5de797 100644
--- a/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-1.rs
+++ b/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-1.rs
@@ -1,4 +1,6 @@
 //@revisions: stack tree
+// Ensure this even hits the aliasing model
+//@compile-flags: -Zmiri-disable-validation
 //@[tree]compile-flags: -Zmiri-tree-borrows
 //@error-in-other-file: pointer not dereferenceable
 
diff --git a/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-2.rs b/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-2.rs
index 5c947e64142..b54f27bb8b2 100644
--- a/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-2.rs
+++ b/src/tools/miri/tests/fail/both_borrows/issue-miri-1050-2.rs
@@ -1,4 +1,6 @@
 //@revisions: stack tree
+// Ensure this even hits the aliasing model
+//@compile-flags: -Zmiri-disable-validation
 //@[tree]compile-flags: -Zmiri-tree-borrows
 //@error-in-other-file: is a dangling pointer
 use std::ptr::NonNull;
diff --git a/src/tools/miri/tests/fail/both_borrows/load_invalid_shr.tree.stderr b/src/tools/miri/tests/fail/both_borrows/load_invalid_shr.tree.stderr
index 86c12603c07..e4bde2f7aea 100644
--- a/src/tools/miri/tests/fail/both_borrows/load_invalid_shr.tree.stderr
+++ b/src/tools/miri/tests/fail/both_borrows/load_invalid_shr.tree.stderr
@@ -5,6 +5,7 @@ LL |     let _val = *xref_in_mem;
    |                ^^^^^^^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> has state Disabled which forbids this reborrow (acting as a child read access)
 help: the accessed tag <TAG> was created here, in the initial state Frozen
   --> tests/fail/both_borrows/load_invalid_shr.rs:LL:CC
diff --git a/src/tools/miri/tests/fail/both_borrows/mut_exclusive_violation1.tree.stderr b/src/tools/miri/tests/fail/both_borrows/mut_exclusive_violation1.tree.stderr
index 5f14f23e8d6..f5c1dea69f0 100644
--- a/src/tools/miri/tests/fail/both_borrows/mut_exclusive_violation1.tree.stderr
+++ b/src/tools/miri/tests/fail/both_borrows/mut_exclusive_violation1.tree.stderr
@@ -5,6 +5,7 @@ LL |         *LEAK = 7;
    |         ^^^^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> has state Disabled which forbids this child write access
 help: the accessed tag <TAG> was created here, in the initial state Frozen
   --> tests/fail/both_borrows/mut_exclusive_violation1.rs:LL:CC
diff --git a/src/tools/miri/tests/fail/both_borrows/mut_exclusive_violation2.tree.stderr b/src/tools/miri/tests/fail/both_borrows/mut_exclusive_violation2.tree.stderr
index edc72b0abbf..c1b0821a9fe 100644
--- a/src/tools/miri/tests/fail/both_borrows/mut_exclusive_violation2.tree.stderr
+++ b/src/tools/miri/tests/fail/both_borrows/mut_exclusive_violation2.tree.stderr
@@ -5,6 +5,7 @@ LL |         *raw1 = 3;
    |         ^^^^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> has state Disabled which forbids this child write access
 help: the accessed tag <TAG> was created here, in the initial state Reserved
   --> tests/fail/both_borrows/mut_exclusive_violation2.rs:LL:CC
diff --git a/src/tools/miri/tests/fail/both_borrows/newtype_pair_retagging.tree.stderr b/src/tools/miri/tests/fail/both_borrows/newtype_pair_retagging.tree.stderr
index 1984a6a0746..aa07ef53b31 100644
--- a/src/tools/miri/tests/fail/both_borrows/newtype_pair_retagging.tree.stderr
+++ b/src/tools/miri/tests/fail/both_borrows/newtype_pair_retagging.tree.stderr
@@ -5,6 +5,7 @@ LL |                 self.1.deallocate(From::from(ptr.cast()), layout);
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> is foreign to the protected tag <TAG> (i.e., it is not a child)
    = help: this deallocation (acting as a foreign write access) would cause the protected tag <TAG> (currently Reserved (conflicted)) to become Disabled
    = help: protected tags must never be Disabled
diff --git a/src/tools/miri/tests/fail/both_borrows/newtype_retagging.tree.stderr b/src/tools/miri/tests/fail/both_borrows/newtype_retagging.tree.stderr
index 7bd0cd14d79..c8a72c59176 100644
--- a/src/tools/miri/tests/fail/both_borrows/newtype_retagging.tree.stderr
+++ b/src/tools/miri/tests/fail/both_borrows/newtype_retagging.tree.stderr
@@ -5,6 +5,7 @@ LL |                 self.1.deallocate(From::from(ptr.cast()), layout);
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> is foreign to the protected tag <TAG> (i.e., it is not a child)
    = help: this deallocation (acting as a foreign write access) would cause the protected tag <TAG> (currently Reserved (conflicted)) to become Disabled
    = help: protected tags must never be Disabled
diff --git a/src/tools/miri/tests/fail/both_borrows/outdated_local.tree.stderr b/src/tools/miri/tests/fail/both_borrows/outdated_local.tree.stderr
index 8cb44d2635f..5310f8b9d09 100644
--- a/src/tools/miri/tests/fail/both_borrows/outdated_local.tree.stderr
+++ b/src/tools/miri/tests/fail/both_borrows/outdated_local.tree.stderr
@@ -5,6 +5,7 @@ LL |     assert_eq!(unsafe { *y }, 1);
    |                         ^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> has state Disabled which forbids this child read access
 help: the accessed tag <TAG> was created here, in the initial state Frozen
   --> tests/fail/both_borrows/outdated_local.rs:LL:CC
diff --git a/src/tools/miri/tests/fail/both_borrows/pass_invalid_shr.tree.stderr b/src/tools/miri/tests/fail/both_borrows/pass_invalid_shr.tree.stderr
index 1bd760426da..3c813d35031 100644
--- a/src/tools/miri/tests/fail/both_borrows/pass_invalid_shr.tree.stderr
+++ b/src/tools/miri/tests/fail/both_borrows/pass_invalid_shr.tree.stderr
@@ -5,6 +5,7 @@ LL |     foo(xref);
    |         ^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> has state Disabled which forbids this reborrow (acting as a child read access)
 help: the accessed tag <TAG> was created here, in the initial state Frozen
   --> tests/fail/both_borrows/pass_invalid_shr.rs:LL:CC
diff --git a/src/tools/miri/tests/fail/both_borrows/pass_invalid_shr_option.tree.stderr b/src/tools/miri/tests/fail/both_borrows/pass_invalid_shr_option.tree.stderr
index 2f999a8aae0..4747c65ba83 100644
--- a/src/tools/miri/tests/fail/both_borrows/pass_invalid_shr_option.tree.stderr
+++ b/src/tools/miri/tests/fail/both_borrows/pass_invalid_shr_option.tree.stderr
@@ -5,6 +5,7 @@ LL |     foo(some_xref);
    |         ^^^^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> has state Disabled which forbids this reborrow (acting as a child read access)
 help: the accessed tag <TAG> was created here, in the initial state Frozen
   --> tests/fail/both_borrows/pass_invalid_shr_option.rs:LL:CC
diff --git a/src/tools/miri/tests/fail/both_borrows/pass_invalid_shr_tuple.tree.stderr b/src/tools/miri/tests/fail/both_borrows/pass_invalid_shr_tuple.tree.stderr
index b30bda598d2..da011cfd3f7 100644
--- a/src/tools/miri/tests/fail/both_borrows/pass_invalid_shr_tuple.tree.stderr
+++ b/src/tools/miri/tests/fail/both_borrows/pass_invalid_shr_tuple.tree.stderr
@@ -5,6 +5,7 @@ LL |     foo(pair_xref);
    |         ^^^^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> has state Disabled which forbids this reborrow (acting as a child read access)
 help: the accessed tag <TAG> was created here, in the initial state Frozen
   --> tests/fail/both_borrows/pass_invalid_shr_tuple.rs:LL:CC
diff --git a/src/tools/miri/tests/fail/both_borrows/return_invalid_shr.stack.stderr b/src/tools/miri/tests/fail/both_borrows/return_invalid_shr.stack.stderr
index d52143500c4..7c4fe748701 100644
--- a/src/tools/miri/tests/fail/both_borrows/return_invalid_shr.stack.stderr
+++ b/src/tools/miri/tests/fail/both_borrows/return_invalid_shr.stack.stderr
@@ -11,7 +11,7 @@ help: <TAG> was created by a SharedReadOnly retag at offsets [0x4..0x8]
    |
 LL |     let ret = unsafe { &(*xraw).1 };
    |                        ^^^^^^^^^^
-help: <TAG> was later invalidated at offsets [0x0..0x8] by a write access
+help: <TAG> was later invalidated at offsets [0x4..0x8] by a write access
   --> tests/fail/both_borrows/return_invalid_shr.rs:LL:CC
    |
 LL |     unsafe { *xraw = (42, 23) }; // unfreeze
diff --git a/src/tools/miri/tests/fail/both_borrows/return_invalid_shr.tree.stderr b/src/tools/miri/tests/fail/both_borrows/return_invalid_shr.tree.stderr
index 2b2650a254d..a8e3553aae2 100644
--- a/src/tools/miri/tests/fail/both_borrows/return_invalid_shr.tree.stderr
+++ b/src/tools/miri/tests/fail/both_borrows/return_invalid_shr.tree.stderr
@@ -5,13 +5,14 @@ LL |     ret
    |     ^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> has state Disabled which forbids this reborrow (acting as a child read access)
 help: the accessed tag <TAG> was created here, in the initial state Frozen
   --> tests/fail/both_borrows/return_invalid_shr.rs:LL:CC
    |
 LL |     let ret = unsafe { &(*xraw).1 };
    |                        ^^^^^^^^^^
-help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x8]
+help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x4..0x8]
   --> tests/fail/both_borrows/return_invalid_shr.rs:LL:CC
    |
 LL |     unsafe { *xraw = (42, 23) }; // unfreeze
diff --git a/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_option.stack.stderr b/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_option.stack.stderr
index d66c8eeb1af..8411437ea4c 100644
--- a/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_option.stack.stderr
+++ b/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_option.stack.stderr
@@ -14,7 +14,7 @@ help: <TAG> was created by a SharedReadOnly retag at offsets [0x4..0x8]
    |
 LL |     let ret = Some(unsafe { &(*xraw).1 });
    |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-help: <TAG> was later invalidated at offsets [0x0..0x8] by a write access
+help: <TAG> was later invalidated at offsets [0x4..0x8] by a write access
   --> tests/fail/both_borrows/return_invalid_shr_option.rs:LL:CC
    |
 LL |     unsafe { *xraw = (42, 23) }; // unfreeze
diff --git a/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_option.tree.stderr b/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_option.tree.stderr
index b8e963f87d9..39da45ad6db 100644
--- a/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_option.tree.stderr
+++ b/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_option.tree.stderr
@@ -5,13 +5,14 @@ LL |     ret
    |     ^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> has state Disabled which forbids this reborrow (acting as a child read access)
 help: the accessed tag <TAG> was created here, in the initial state Frozen
   --> tests/fail/both_borrows/return_invalid_shr_option.rs:LL:CC
    |
 LL |     let ret = Some(unsafe { &(*xraw).1 });
    |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x8]
+help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x4..0x8]
   --> tests/fail/both_borrows/return_invalid_shr_option.rs:LL:CC
    |
 LL |     unsafe { *xraw = (42, 23) }; // unfreeze
diff --git a/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_tuple.stack.stderr b/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_tuple.stack.stderr
index 727b52ec729..a7c422aa73f 100644
--- a/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_tuple.stack.stderr
+++ b/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_tuple.stack.stderr
@@ -14,7 +14,7 @@ help: <TAG> was created by a SharedReadOnly retag at offsets [0x4..0x8]
    |
 LL |     let ret = (unsafe { &(*xraw).1 },);
    |               ^^^^^^^^^^^^^^^^^^^^^^^^
-help: <TAG> was later invalidated at offsets [0x0..0x8] by a write access
+help: <TAG> was later invalidated at offsets [0x4..0x8] by a write access
   --> tests/fail/both_borrows/return_invalid_shr_tuple.rs:LL:CC
    |
 LL |     unsafe { *xraw = (42, 23) }; // unfreeze
diff --git a/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_tuple.tree.stderr b/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_tuple.tree.stderr
index 8e499369f08..66b03e57905 100644
--- a/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_tuple.tree.stderr
+++ b/src/tools/miri/tests/fail/both_borrows/return_invalid_shr_tuple.tree.stderr
@@ -5,13 +5,14 @@ LL |     ret
    |     ^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> has state Disabled which forbids this reborrow (acting as a child read access)
 help: the accessed tag <TAG> was created here, in the initial state Frozen
   --> tests/fail/both_borrows/return_invalid_shr_tuple.rs:LL:CC
    |
 LL |     let ret = (unsafe { &(*xraw).1 },);
    |               ^^^^^^^^^^^^^^^^^^^^^^^^
-help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x0..0x8]
+help: the accessed tag <TAG> later transitioned to Disabled due to a foreign write access at offsets [0x4..0x8]
   --> tests/fail/both_borrows/return_invalid_shr_tuple.rs:LL:CC
    |
 LL |     unsafe { *xraw = (42, 23) }; // unfreeze
diff --git a/src/tools/miri/tests/fail/both_borrows/shr_frozen_violation1.tree.stderr b/src/tools/miri/tests/fail/both_borrows/shr_frozen_violation1.tree.stderr
index a28754f5412..d9b75f65f75 100644
--- a/src/tools/miri/tests/fail/both_borrows/shr_frozen_violation1.tree.stderr
+++ b/src/tools/miri/tests/fail/both_borrows/shr_frozen_violation1.tree.stderr
@@ -5,6 +5,7 @@ LL |         *(x as *const i32 as *mut i32) = 7;
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> has state Frozen which forbids this child write access
 help: the accessed tag <TAG> was created here, in the initial state Frozen
   --> tests/fail/both_borrows/shr_frozen_violation1.rs:LL:CC
diff --git a/src/tools/miri/tests/fail/both_borrows/shr_frozen_violation2.tree.stderr b/src/tools/miri/tests/fail/both_borrows/shr_frozen_violation2.tree.stderr
index 0eb2dc3df77..c680c04137b 100644
--- a/src/tools/miri/tests/fail/both_borrows/shr_frozen_violation2.tree.stderr
+++ b/src/tools/miri/tests/fail/both_borrows/shr_frozen_violation2.tree.stderr
@@ -5,6 +5,7 @@ LL |         let _val = *frozen;
    |                    ^^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> has state Disabled which forbids this child read access
 help: the accessed tag <TAG> was created here, in the initial state Frozen
   --> tests/fail/both_borrows/shr_frozen_violation2.rs:LL:CC
diff --git a/src/tools/miri/tests/fail/both_borrows/zero-sized-protected.rs b/src/tools/miri/tests/fail/both_borrows/zero-sized-protected.rs
deleted file mode 100644
index df9a73a444e..00000000000
--- a/src/tools/miri/tests/fail/both_borrows/zero-sized-protected.rs
+++ /dev/null
@@ -1,18 +0,0 @@
-//@revisions: stack tree
-//@[tree]compile-flags: -Zmiri-tree-borrows
-use std::alloc::{Layout, alloc, dealloc};
-
-// `x` is strongly protected but covers zero bytes.
-// Let's see if deallocating the allocation x points to is UB:
-// in TB, it is UB, but in SB it is not.
-fn test(_x: &mut (), ptr: *mut u8, l: Layout) {
-    unsafe { dealloc(ptr, l) }; //~[tree] ERROR: /deallocation .* is forbidden/
-}
-
-fn main() {
-    let l = Layout::from_size_align(1, 1).unwrap();
-    let ptr = unsafe { alloc(l) };
-    unsafe { test(&mut *ptr.cast::<()>(), ptr, l) };
-    // In SB the test would pass if it weren't for this line.
-    unsafe { std::hint::unreachable_unchecked() }; //~[stack] ERROR: unreachable
-}
diff --git a/src/tools/miri/tests/fail/both_borrows/zero-sized-protected.tree.stderr b/src/tools/miri/tests/fail/both_borrows/zero-sized-protected.tree.stderr
deleted file mode 100644
index 2a9a9afb0f0..00000000000
--- a/src/tools/miri/tests/fail/both_borrows/zero-sized-protected.tree.stderr
+++ /dev/null
@@ -1,31 +0,0 @@
-error: Undefined Behavior: deallocation through <TAG> (root of the allocation) at ALLOC[0x0] is forbidden
-  --> tests/fail/both_borrows/zero-sized-protected.rs:LL:CC
-   |
-LL |     unsafe { dealloc(ptr, l) };
-   |              ^^^^^^^^^^^^^^^ Undefined Behavior occurred here
-   |
-   = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
-   = help: the allocation of the accessed tag <TAG> (root of the allocation) also contains the strongly protected tag <TAG>
-   = help: the strongly protected tag <TAG> disallows deallocations
-help: the accessed tag <TAG> was created here
-  --> tests/fail/both_borrows/zero-sized-protected.rs:LL:CC
-   |
-LL |     let ptr = unsafe { alloc(l) };
-   |                        ^^^^^^^^
-help: the strongly protected tag <TAG> was created here, in the initial state Reserved
-  --> tests/fail/both_borrows/zero-sized-protected.rs:LL:CC
-   |
-LL | fn test(_x: &mut (), ptr: *mut u8, l: Layout) {
-   |         ^^
-   = note: BACKTRACE (of the first span):
-   = note: inside `test` at tests/fail/both_borrows/zero-sized-protected.rs:LL:CC
-note: inside `main`
-  --> tests/fail/both_borrows/zero-sized-protected.rs:LL:CC
-   |
-LL |     unsafe { test(&mut *ptr.cast::<()>(), ptr, l) };
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-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/branchless-select-i128-pointer.rs b/src/tools/miri/tests/fail/branchless-select-i128-pointer.rs
index 2b861e5447b..7147813c4b6 100644
--- a/src/tools/miri/tests/fail/branchless-select-i128-pointer.rs
+++ b/src/tools/miri/tests/fail/branchless-select-i128-pointer.rs
@@ -1,3 +1,5 @@
+#![allow(integer_to_ptr_transmutes)]
+
 use std::mem::transmute;
 
 #[cfg(target_pointer_width = "32")]
diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias.rs b/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias.rs
new file mode 100644
index 00000000000..744d64b9b1e
--- /dev/null
+++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias.rs
@@ -0,0 +1,36 @@
+//! Ensure we detect aliasing of two in-place arguments for the tricky case where they do not
+//! live in memory.
+//@revisions: stack tree
+//@[tree]compile-flags: -Zmiri-tree-borrows
+// Validation forces more things into memory, which we can't have here.
+//@compile-flags: -Zmiri-disable-validation
+#![feature(custom_mir, core_intrinsics)]
+use std::intrinsics::mir::*;
+
+pub struct S(i32);
+
+#[custom_mir(dialect = "runtime", phase = "optimized")]
+fn main() {
+    mir! {
+        let _unit: ();
+        {
+            let staging = S(42); // This forces `staging` into memory...
+            let non_copy = staging; // ... so we move it to a non-inmemory local here.
+            // This specifically uses a type with scalar representation to tempt Miri to use the
+            // efficient way of storing local variables (outside adressable memory).
+            Call(_unit = callee(Move(non_copy), Move(non_copy)), ReturnTo(after_call), UnwindContinue())
+            //~[stack]^ ERROR: not granting access
+            //~[tree]| ERROR: /read access .* forbidden/
+        }
+        after_call = {
+            Return()
+        }
+    }
+}
+
+pub fn callee(x: S, mut y: S) {
+    // With the setup above, if `x` and `y` are both moved,
+    // then writing to `y` will change the value stored in `x`!
+    y.0 = 0;
+    assert_eq!(x.0, 42);
+}
diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias.stack.stderr b/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias.stack.stderr
new file mode 100644
index 00000000000..0c1100cae63
--- /dev/null
+++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias.stack.stderr
@@ -0,0 +1,25 @@
+error: Undefined Behavior: not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is strongly protected
+  --> tests/fail/function_calls/arg_inplace_locals_alias.rs:LL:CC
+   |
+LL |             Call(_unit = callee(Move(non_copy), Move(non_copy)), ReturnTo(after_call), UnwindContinue())
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
+   |
+   = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
+help: <TAG> was created here, as the root tag for ALLOC
+  --> tests/fail/function_calls/arg_inplace_locals_alias.rs:LL:CC
+   |
+LL |             Call(_unit = callee(Move(non_copy), Move(non_copy)), ReturnTo(after_call), UnwindContinue())
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: <TAG> is this argument
+  --> tests/fail/function_calls/arg_inplace_locals_alias.rs:LL:CC
+   |
+LL |     y.0 = 0;
+   |     ^^^^^^^
+   = note: BACKTRACE (of the first span):
+   = note: inside `main` at tests/fail/function_calls/arg_inplace_locals_alias.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/function_calls/arg_inplace_locals_alias.tree.stderr b/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias.tree.stderr
new file mode 100644
index 00000000000..2d9ce2aa1fb
--- /dev/null
+++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias.tree.stderr
@@ -0,0 +1,34 @@
+error: Undefined Behavior: read access through <TAG> (root of the allocation) at ALLOC[0x0] is forbidden
+  --> tests/fail/function_calls/arg_inplace_locals_alias.rs:LL:CC
+   |
+LL |             Call(_unit = callee(Move(non_copy), Move(non_copy)), ReturnTo(after_call), UnwindContinue())
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
+   |
+   = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
+   = help: the accessed tag <TAG> (root of the allocation) is foreign to the protected tag <TAG> (i.e., it is not a child)
+   = help: this foreign read access would cause the protected tag <TAG> (currently Unique) to become Disabled
+   = help: protected tags must never be Disabled
+help: the accessed tag <TAG> was created here
+  --> tests/fail/function_calls/arg_inplace_locals_alias.rs:LL:CC
+   |
+LL |             Call(_unit = callee(Move(non_copy), Move(non_copy)), ReturnTo(after_call), UnwindContinue())
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: the protected tag <TAG> was created here, in the initial state Reserved
+  --> tests/fail/function_calls/arg_inplace_locals_alias.rs:LL:CC
+   |
+LL |     y.0 = 0;
+   |     ^^^^^^^
+help: the protected tag <TAG> later transitioned to Unique due to a child write access at offsets [0x0..0x4]
+  --> tests/fail/function_calls/arg_inplace_locals_alias.rs:LL:CC
+   |
+LL |     y.0 = 0;
+   |     ^^^^^^^
+   = help: this transition corresponds to the first write to a 2-phase borrowed mutable reference
+   = note: BACKTRACE (of the first span):
+   = note: inside `main` at tests/fail/function_calls/arg_inplace_locals_alias.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/function_calls/arg_inplace_locals_alias_ret.rs b/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias_ret.rs
new file mode 100644
index 00000000000..dff724f8d96
--- /dev/null
+++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias_ret.rs
@@ -0,0 +1,34 @@
+//! Ensure we detect aliasing of a in-place argument with the return place for the tricky case where
+//! they do not live in memory.
+//@revisions: stack tree
+//@[tree]compile-flags: -Zmiri-tree-borrows
+// Validation forces more things into memory, which we can't have here.
+//@compile-flags: -Zmiri-disable-validation
+#![feature(custom_mir, core_intrinsics)]
+use std::intrinsics::mir::*;
+
+#[allow(unused)]
+pub struct S(i32);
+
+#[custom_mir(dialect = "runtime", phase = "optimized")]
+fn main() {
+    mir! {
+        let _unit: ();
+        {
+            let staging = S(42); // This forces `staging` into memory...
+            let _non_copy = staging; // ... so we move it to a non-inmemory local here.
+            // This specifically uses a type with scalar representation to tempt Miri to use the
+            // efficient way of storing local variables (outside adressable memory).
+            Call(_non_copy = callee(Move(_non_copy)), ReturnTo(after_call), UnwindContinue())
+            //~[stack]^ ERROR: not granting access
+            //~[tree]| ERROR: /reborrow .* forbidden/
+        }
+        after_call = {
+            Return()
+        }
+    }
+}
+
+pub fn callee(x: S) -> S {
+    x
+}
diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias_ret.stack.stderr b/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias_ret.stack.stderr
new file mode 100644
index 00000000000..fcd5b8752e7
--- /dev/null
+++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias_ret.stack.stderr
@@ -0,0 +1,25 @@
+error: Undefined Behavior: not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is strongly protected
+  --> tests/fail/function_calls/arg_inplace_locals_alias_ret.rs:LL:CC
+   |
+LL |             Call(_non_copy = callee(Move(_non_copy)), ReturnTo(after_call), UnwindContinue())
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
+   |
+   = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
+help: <TAG> was created here, as the root tag for ALLOC
+  --> tests/fail/function_calls/arg_inplace_locals_alias_ret.rs:LL:CC
+   |
+LL |             Call(_non_copy = callee(Move(_non_copy)), ReturnTo(after_call), UnwindContinue())
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: <TAG> is this argument
+  --> tests/fail/function_calls/arg_inplace_locals_alias_ret.rs:LL:CC
+   |
+LL |     x
+   |     ^
+   = note: BACKTRACE (of the first span):
+   = note: inside `main` at tests/fail/function_calls/arg_inplace_locals_alias_ret.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/function_calls/arg_inplace_locals_alias_ret.tree.stderr b/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias_ret.tree.stderr
new file mode 100644
index 00000000000..42e391b5daf
--- /dev/null
+++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_locals_alias_ret.tree.stderr
@@ -0,0 +1,34 @@
+error: Undefined Behavior: reborrow through <TAG> (root of the allocation) at ALLOC[0x0] is forbidden
+  --> tests/fail/function_calls/arg_inplace_locals_alias_ret.rs:LL:CC
+   |
+LL |             Call(_non_copy = callee(Move(_non_copy)), ReturnTo(after_call), UnwindContinue())
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
+   |
+   = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
+   = help: the accessed tag <TAG> (root of the allocation) is foreign to the protected tag <TAG> (i.e., it is not a child)
+   = help: this reborrow (acting as a foreign read access) would cause the protected tag <TAG> (currently Unique) to become Disabled
+   = help: protected tags must never be Disabled
+help: the accessed tag <TAG> was created here
+  --> tests/fail/function_calls/arg_inplace_locals_alias_ret.rs:LL:CC
+   |
+LL |             Call(_non_copy = callee(Move(_non_copy)), ReturnTo(after_call), UnwindContinue())
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: the protected tag <TAG> was created here, in the initial state Reserved
+  --> tests/fail/function_calls/arg_inplace_locals_alias_ret.rs:LL:CC
+   |
+LL |     x
+   |     ^
+help: the protected tag <TAG> later transitioned to Unique due to a child write access at offsets [0x0..0x4]
+  --> tests/fail/function_calls/arg_inplace_locals_alias_ret.rs:LL:CC
+   |
+LL |     x
+   |     ^
+   = help: this transition corresponds to the first write to a 2-phase borrowed mutable reference
+   = note: BACKTRACE (of the first span):
+   = note: inside `main` at tests/fail/function_calls/arg_inplace_locals_alias_ret.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/function_calls/arg_inplace_mutate.tree.stderr b/src/tools/miri/tests/fail/function_calls/arg_inplace_mutate.tree.stderr
index 2f1dc1988f5..74706d6b9f6 100644
--- a/src/tools/miri/tests/fail/function_calls/arg_inplace_mutate.tree.stderr
+++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_mutate.tree.stderr
@@ -5,8 +5,9 @@ LL |     unsafe { ptr.write(S(0)) };
    |              ^^^^^^^^^^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> (root of the allocation) is foreign to the protected tag <TAG> (i.e., it is not a child)
-   = help: this foreign write access would cause the protected tag <TAG> (currently Active) to become Disabled
+   = help: this foreign write access would cause the protected tag <TAG> (currently Unique) to become Disabled
    = help: protected tags must never be Disabled
 help: the accessed tag <TAG> was created here
   --> tests/fail/function_calls/arg_inplace_mutate.rs:LL:CC
@@ -23,7 +24,7 @@ help: the protected tag <TAG> was created here, in the initial state Reserved
    |
 LL |     unsafe { ptr.write(S(0)) };
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
-help: the protected tag <TAG> later transitioned to Active due to a child write access at offsets [0x0..0x4]
+help: the protected tag <TAG> later transitioned to Unique due to a child write access at offsets [0x0..0x4]
   --> tests/fail/function_calls/arg_inplace_mutate.rs:LL:CC
    |
 LL |     unsafe { ptr.write(S(0)) };
diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.tree.stderr b/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.tree.stderr
index 5db5a6cac4d..c8c0e5c37ef 100644
--- a/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.tree.stderr
+++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.tree.stderr
@@ -5,8 +5,9 @@ LL |     unsafe { ptr.read() };
    |              ^^^^^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> (root of the allocation) is foreign to the protected tag <TAG> (i.e., it is not a child)
-   = help: this foreign read access would cause the protected tag <TAG> (currently Active) to become Disabled
+   = help: this foreign read access would cause the protected tag <TAG> (currently Unique) to become Disabled
    = help: protected tags must never be Disabled
 help: the accessed tag <TAG> was created here
   --> tests/fail/function_calls/arg_inplace_observe_during.rs:LL:CC
@@ -23,7 +24,7 @@ help: the protected tag <TAG> was created here, in the initial state Reserved
    |
 LL |     x.0 = 0;
    |     ^^^^^^^
-help: the protected tag <TAG> later transitioned to Active due to a child write access at offsets [0x0..0x4]
+help: the protected tag <TAG> later transitioned to Unique due to a child write access at offsets [0x0..0x4]
   --> tests/fail/function_calls/arg_inplace_observe_during.rs:LL:CC
    |
 LL |     x.0 = 0;
diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr
index a1571173a53..95f79aae6c1 100644
--- a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr
+++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.both.stderr
@@ -9,13 +9,12 @@ panic in a function that cannot unwind
 stack backtrace:
 thread caused non-unwinding panic. aborting.
 error: abnormal termination: the program aborted execution
-  --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC
+  --> RUSTLIB/std/src/panicking.rs:LL:CC
    |
-LL | ABORT()
-   | ^ abnormal termination occurred here
+LL |         crate::process::abort();
+   |         ^^^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here
    |
    = note: BACKTRACE:
-   = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC
    = note: inside `std::panicking::panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC
    = note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC
    = note: inside `std::sys::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys/backtrace.rs:LL:CC
@@ -33,7 +32,7 @@ note: inside `main`
   --> tests/fail/function_calls/exported_symbol_bad_unwind2.rs:LL:CC
    |
 LL |     unsafe { nounwind() }
-   | ^
+   |              ^^^^^^^^^^
 
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr
index a1571173a53..95f79aae6c1 100644
--- a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr
+++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.definition.stderr
@@ -9,13 +9,12 @@ panic in a function that cannot unwind
 stack backtrace:
 thread caused non-unwinding panic. aborting.
 error: abnormal termination: the program aborted execution
-  --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC
+  --> RUSTLIB/std/src/panicking.rs:LL:CC
    |
-LL | ABORT()
-   | ^ abnormal termination occurred here
+LL |         crate::process::abort();
+   |         ^^^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here
    |
    = note: BACKTRACE:
-   = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC
    = note: inside `std::panicking::panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC
    = note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC
    = note: inside `std::sys::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys/backtrace.rs:LL:CC
@@ -33,7 +32,7 @@ note: inside `main`
   --> tests/fail/function_calls/exported_symbol_bad_unwind2.rs:LL:CC
    |
 LL |     unsafe { nounwind() }
-   | ^
+   |              ^^^^^^^^^^
 
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.extern_block.stderr b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.extern_block.stderr
index e755b262474..eaca0d3f012 100644
--- a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.extern_block.stderr
+++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.extern_block.stderr
@@ -7,7 +7,7 @@ error: Undefined Behavior: unwinding past a stack frame that does not allow unwi
   --> tests/fail/function_calls/exported_symbol_bad_unwind2.rs:LL:CC
    |
 LL |     unsafe { nounwind() }
-   | ^ Undefined Behavior occurred here
+   |              ^^^^^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
    = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
diff --git a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.rs b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.rs
index 9d993786d57..aa5a185f110 100644
--- a/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.rs
+++ b/src/tools/miri/tests/fail/function_calls/exported_symbol_bad_unwind2.rs
@@ -1,6 +1,4 @@
 //@revisions: extern_block definition both
-//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()"
-//@normalize-stderr-test: "\| +\^+" -> "| ^"
 //@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> ""
 //@normalize-stderr-test: "\n +at [^\n]+" -> ""
 //@[definition,both]error-in-other-file: aborted execution
diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_read.none.stderr b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_read.none.stderr
index 24091547258..d478568ceae 100644
--- a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_read.none.stderr
+++ b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_read.none.stderr
@@ -11,8 +11,8 @@ LL |     unsafe { ptr.read() };
 note: inside `main`
   --> tests/fail/function_calls/return_pointer_aliasing_read.rs:LL:CC
    |
-LL |             Call(*ptr = myfun(ptr), ReturnTo(after_call), UnwindContinue())
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |             Call(_x = myfun(ptr), ReturnTo(after_call), UnwindContinue())
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 Uninitialized memory occurred at ALLOC[0x0..0x4], in this allocation:
 ALLOC (stack variable, size: 4, align: 4) {
diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_read.rs b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_read.rs
index a6e0134bd17..dc22e129e18 100644
--- a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_read.rs
+++ b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_read.rs
@@ -10,11 +10,11 @@ use std::intrinsics::mir::*;
 pub fn main() {
     mir! {
         {
-            let x = 0;
-            let ptr = &raw mut x;
+            let _x = 0;
+            let ptr = &raw mut _x;
             // We arrange for `myfun` to have a pointer that aliases
             // its return place. Even just reading from that pointer is UB.
-            Call(*ptr = myfun(ptr), ReturnTo(after_call), UnwindContinue())
+            Call(_x = myfun(ptr), ReturnTo(after_call), UnwindContinue())
         }
 
         after_call = {
@@ -25,7 +25,7 @@ pub fn main() {
 
 fn myfun(ptr: *mut i32) -> i32 {
     unsafe { ptr.read() };
-    //~[stack]^ ERROR: not granting access
+    //~[stack]^ ERROR: does not exist in the borrow stack
     //~[tree]| ERROR: /read access .* forbidden/
     //~[none]| ERROR: uninitialized
     // Without an aliasing model, reads are "fine" but at least they return uninit data.
diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_read.stack.stderr b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_read.stack.stderr
index 77cc0332a1c..86adbab353b 100644
--- a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_read.stack.stderr
+++ b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_read.stack.stderr
@@ -1,8 +1,8 @@
-error: Undefined Behavior: not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is strongly protected
+error: Undefined Behavior: attempting a read access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
   --> tests/fail/function_calls/return_pointer_aliasing_read.rs:LL:CC
    |
 LL |     unsafe { ptr.read() };
-   |              ^^^^^^^^^^ Undefined Behavior occurred here
+   |              ^^^^^^^^^^ this error occurs as part of an access at ALLOC[0x0..0x4]
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
    = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
@@ -11,12 +11,12 @@ help: <TAG> was created by a SharedReadWrite retag at offsets [0x0..0x4]
    |
 LL | /     mir! {
 LL | |         {
-LL | |             let x = 0;
-LL | |             let ptr = &raw mut x;
+LL | |             let _x = 0;
+LL | |             let ptr = &raw mut _x;
 ...  |
 LL | |     }
    | |_____^
-help: <TAG> is this argument
+help: <TAG> was later invalidated at offsets [0x0..0x4] by a Unique in-place function argument/return passing protection
   --> tests/fail/function_calls/return_pointer_aliasing_read.rs:LL:CC
    |
 LL |     unsafe { ptr.read() };
@@ -26,8 +26,8 @@ LL |     unsafe { ptr.read() };
 note: inside `main`
   --> tests/fail/function_calls/return_pointer_aliasing_read.rs:LL:CC
    |
-LL |             Call(*ptr = myfun(ptr), ReturnTo(after_call), UnwindContinue())
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |             Call(_x = myfun(ptr), ReturnTo(after_call), UnwindContinue())
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_read.tree.stderr b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_read.tree.stderr
index bae3819c979..b43e19c3905 100644
--- a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_read.tree.stderr
+++ b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_read.tree.stderr
@@ -5,16 +5,17 @@ LL |     unsafe { ptr.read() };
    |              ^^^^^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> (root of the allocation) is foreign to the protected tag <TAG> (i.e., it is not a child)
-   = help: this foreign read access would cause the protected tag <TAG> (currently Active) to become Disabled
+   = help: this foreign read access would cause the protected tag <TAG> (currently Unique) to become Disabled
    = help: protected tags must never be Disabled
 help: the accessed tag <TAG> was created here
   --> tests/fail/function_calls/return_pointer_aliasing_read.rs:LL:CC
    |
 LL | /     mir! {
 LL | |         {
-LL | |             let x = 0;
-LL | |             let ptr = &raw mut x;
+LL | |             let _x = 0;
+LL | |             let ptr = &raw mut _x;
 ...  |
 LL | |     }
    | |_____^
@@ -23,7 +24,7 @@ help: the protected tag <TAG> was created here, in the initial state Reserved
    |
 LL |     unsafe { ptr.read() };
    |     ^^^^^^^^^^^^^^^^^^^^^
-help: the protected tag <TAG> later transitioned to Active due to a child write access at offsets [0x0..0x4]
+help: the protected tag <TAG> later transitioned to Unique due to a child write access at offsets [0x0..0x4]
   --> tests/fail/function_calls/return_pointer_aliasing_read.rs:LL:CC
    |
 LL |     unsafe { ptr.read() };
@@ -34,8 +35,8 @@ LL |     unsafe { ptr.read() };
 note: inside `main`
   --> tests/fail/function_calls/return_pointer_aliasing_read.rs:LL:CC
    |
-LL |             Call(*ptr = myfun(ptr), ReturnTo(after_call), UnwindContinue())
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |             Call(_x = myfun(ptr), ReturnTo(after_call), UnwindContinue())
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write.rs b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write.rs
index 6155e925c4b..2fddaf37235 100644
--- a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write.rs
+++ b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write.rs
@@ -14,7 +14,7 @@ pub fn main() {
             let ptr = &raw mut _x;
             // We arrange for `myfun` to have a pointer that aliases
             // its return place. Writing to that pointer is UB.
-            Call(*ptr = myfun(ptr), ReturnTo(after_call), UnwindContinue())
+            Call(_x = myfun(ptr), ReturnTo(after_call), UnwindContinue())
         }
 
         after_call = {
@@ -26,7 +26,7 @@ pub fn main() {
 fn myfun(ptr: *mut i32) -> i32 {
     // This overwrites the return place, which shouldn't be possible through another pointer.
     unsafe { ptr.write(0) };
-    //~[stack]^ ERROR: strongly protected
+    //~[stack]^ ERROR: does not exist in the borrow stack
     //~[tree]| ERROR: /write access .* forbidden/
     13
 }
diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write.stack.stderr b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write.stack.stderr
index 828b2339f8c..faae6172d75 100644
--- a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write.stack.stderr
+++ b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write.stack.stderr
@@ -1,8 +1,8 @@
-error: Undefined Behavior: not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is strongly protected
+error: Undefined Behavior: attempting a write access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
   --> tests/fail/function_calls/return_pointer_aliasing_write.rs:LL:CC
    |
 LL |     unsafe { ptr.write(0) };
-   |              ^^^^^^^^^^^^ Undefined Behavior occurred here
+   |              ^^^^^^^^^^^^ this error occurs as part of an access at ALLOC[0x0..0x4]
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
    = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
@@ -16,7 +16,7 @@ LL | |             let ptr = &raw mut _x;
 ...  |
 LL | |     }
    | |_____^
-help: <TAG> is this argument
+help: <TAG> was later invalidated at offsets [0x0..0x4] by a Unique in-place function argument/return passing protection
   --> tests/fail/function_calls/return_pointer_aliasing_write.rs:LL:CC
    |
 LL |     unsafe { ptr.write(0) };
@@ -26,8 +26,8 @@ LL |     unsafe { ptr.write(0) };
 note: inside `main`
   --> tests/fail/function_calls/return_pointer_aliasing_write.rs:LL:CC
    |
-LL |             Call(*ptr = myfun(ptr), ReturnTo(after_call), UnwindContinue())
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |             Call(_x = myfun(ptr), ReturnTo(after_call), UnwindContinue())
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write.tree.stderr b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write.tree.stderr
index e0df9370fee..deefb24b785 100644
--- a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write.tree.stderr
+++ b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write.tree.stderr
@@ -5,8 +5,9 @@ LL |     unsafe { ptr.write(0) };
    |              ^^^^^^^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> (root of the allocation) is foreign to the protected tag <TAG> (i.e., it is not a child)
-   = help: this foreign write access would cause the protected tag <TAG> (currently Active) to become Disabled
+   = help: this foreign write access would cause the protected tag <TAG> (currently Unique) to become Disabled
    = help: protected tags must never be Disabled
 help: the accessed tag <TAG> was created here
   --> tests/fail/function_calls/return_pointer_aliasing_write.rs:LL:CC
@@ -23,7 +24,7 @@ help: the protected tag <TAG> was created here, in the initial state Reserved
    |
 LL |     unsafe { ptr.write(0) };
    |     ^^^^^^^^^^^^^^^^^^^^^^^
-help: the protected tag <TAG> later transitioned to Active due to a child write access at offsets [0x0..0x4]
+help: the protected tag <TAG> later transitioned to Unique due to a child write access at offsets [0x0..0x4]
   --> tests/fail/function_calls/return_pointer_aliasing_write.rs:LL:CC
    |
 LL |     unsafe { ptr.write(0) };
@@ -34,8 +35,8 @@ LL |     unsafe { ptr.write(0) };
 note: inside `main`
   --> tests/fail/function_calls/return_pointer_aliasing_write.rs:LL:CC
    |
-LL |             Call(*ptr = myfun(ptr), ReturnTo(after_call), UnwindContinue())
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |             Call(_x = myfun(ptr), ReturnTo(after_call), UnwindContinue())
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write_tail_call.rs b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write_tail_call.rs
index 37ee7ae1b0d..5f3ecb65022 100644
--- a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write_tail_call.rs
+++ b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write_tail_call.rs
@@ -16,7 +16,7 @@ pub fn main() {
             let ptr = &raw mut _x;
             // We arrange for `myfun` to have a pointer that aliases
             // its return place. Writing to that pointer is UB.
-            Call(*ptr = myfun(ptr), ReturnTo(after_call), UnwindContinue())
+            Call(_x = myfun(ptr), ReturnTo(after_call), UnwindContinue())
         }
 
         after_call = {
@@ -32,7 +32,7 @@ fn myfun(ptr: *mut i32) -> i32 {
 fn myfun2(ptr: *mut i32) -> i32 {
     // This overwrites the return place, which shouldn't be possible through another pointer.
     unsafe { ptr.write(0) };
-    //~[stack]^ ERROR: strongly protected
+    //~[stack]^ ERROR: does not exist in the borrow stack
     //~[tree]| ERROR: /write access .* forbidden/
     13
 }
diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write_tail_call.stack.stderr b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write_tail_call.stack.stderr
index f5183cfaf5b..1a18857bb17 100644
--- a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write_tail_call.stack.stderr
+++ b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write_tail_call.stack.stderr
@@ -1,8 +1,8 @@
-error: Undefined Behavior: not granting access to tag <TAG> because that would remove [Unique for <TAG>] which is strongly protected
+error: Undefined Behavior: attempting a write access using <TAG> at ALLOC[0x0], but that tag does not exist in the borrow stack for this location
   --> tests/fail/function_calls/return_pointer_aliasing_write_tail_call.rs:LL:CC
    |
 LL |     unsafe { ptr.write(0) };
-   |              ^^^^^^^^^^^^ Undefined Behavior occurred here
+   |              ^^^^^^^^^^^^ this error occurs as part of an access at ALLOC[0x0..0x4]
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
    = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
@@ -16,18 +16,18 @@ LL | |             let ptr = &raw mut _x;
 ...  |
 LL | |     }
    | |_____^
-help: <TAG> is this argument
+help: <TAG> was later invalidated at offsets [0x0..0x4] by a Unique in-place function argument/return passing protection
   --> tests/fail/function_calls/return_pointer_aliasing_write_tail_call.rs:LL:CC
    |
-LL |     unsafe { ptr.write(0) };
-   |     ^^^^^^^^^^^^^^^^^^^^^^^
+LL |     become myfun2(ptr)
+   |     ^^^^^^^^^^^^^^^^^^
    = note: BACKTRACE (of the first span):
    = note: inside `myfun2` at tests/fail/function_calls/return_pointer_aliasing_write_tail_call.rs:LL:CC
 note: inside `main`
   --> tests/fail/function_calls/return_pointer_aliasing_write_tail_call.rs:LL:CC
    |
-LL |             Call(*ptr = myfun(ptr), ReturnTo(after_call), UnwindContinue())
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |             Call(_x = myfun(ptr), ReturnTo(after_call), UnwindContinue())
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write_tail_call.tree.stderr b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write_tail_call.tree.stderr
index fa03ed6869b..76ccf39744d 100644
--- a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write_tail_call.tree.stderr
+++ b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_write_tail_call.tree.stderr
@@ -5,8 +5,9 @@ LL |     unsafe { ptr.write(0) };
    |              ^^^^^^^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> (root of the allocation) is foreign to the protected tag <TAG> (i.e., it is not a child)
-   = help: this foreign write access would cause the protected tag <TAG> (currently Active) to become Disabled
+   = help: this foreign write access would cause the protected tag <TAG> (currently Unique) to become Disabled
    = help: protected tags must never be Disabled
 help: the accessed tag <TAG> was created here
   --> tests/fail/function_calls/return_pointer_aliasing_write_tail_call.rs:LL:CC
@@ -23,7 +24,7 @@ help: the protected tag <TAG> was created here, in the initial state Reserved
    |
 LL |     unsafe { ptr.write(0) };
    |     ^^^^^^^^^^^^^^^^^^^^^^^
-help: the protected tag <TAG> later transitioned to Active due to a child write access at offsets [0x0..0x4]
+help: the protected tag <TAG> later transitioned to Unique due to a child write access at offsets [0x0..0x4]
   --> tests/fail/function_calls/return_pointer_aliasing_write_tail_call.rs:LL:CC
    |
 LL |     unsafe { ptr.write(0) };
@@ -34,8 +35,8 @@ LL |     unsafe { ptr.write(0) };
 note: inside `main`
   --> tests/fail/function_calls/return_pointer_aliasing_write_tail_call.rs:LL:CC
    |
-LL |             Call(*ptr = myfun(ptr), ReturnTo(after_call), UnwindContinue())
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |             Call(_x = myfun(ptr), ReturnTo(after_call), UnwindContinue())
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    = note: this error originates in the macro `::core::intrinsics::mir::__internal_remove_let` which comes from the expansion of the macro `mir` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
diff --git a/src/tools/miri/tests/fail/intrinsics/funnel_shl.rs b/src/tools/miri/tests/fail/intrinsics/funnel_shl.rs
new file mode 100644
index 00000000000..40b61f65ebe
--- /dev/null
+++ b/src/tools/miri/tests/fail/intrinsics/funnel_shl.rs
@@ -0,0 +1,7 @@
+#![feature(core_intrinsics, funnel_shifts)]
+
+fn main() {
+    unsafe {
+        std::intrinsics::unchecked_funnel_shl(1_u32, 2, 32); //~ ERROR: Undefined Behavior
+    }
+}
diff --git a/src/tools/miri/tests/fail/intrinsics/funnel_shl.stderr b/src/tools/miri/tests/fail/intrinsics/funnel_shl.stderr
new file mode 100644
index 00000000000..fc828021b13
--- /dev/null
+++ b/src/tools/miri/tests/fail/intrinsics/funnel_shl.stderr
@@ -0,0 +1,15 @@
+error: Undefined Behavior: `assume` called with `false`
+  --> tests/fail/intrinsics/funnel_shl.rs:LL:CC
+   |
+LL |         std::intrinsics::unchecked_funnel_shl(1_u32, 2, 32);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
+   |
+   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
+   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+   = note: BACKTRACE:
+   = note: inside `main` at tests/fail/intrinsics/funnel_shl.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/intrinsics/funnel_shr.rs b/src/tools/miri/tests/fail/intrinsics/funnel_shr.rs
new file mode 100644
index 00000000000..95822656397
--- /dev/null
+++ b/src/tools/miri/tests/fail/intrinsics/funnel_shr.rs
@@ -0,0 +1,7 @@
+#![feature(core_intrinsics, funnel_shifts)]
+
+fn main() {
+    unsafe {
+        std::intrinsics::unchecked_funnel_shr(1_u32, 2, 32); //~ ERROR: Undefined Behavior
+    }
+}
diff --git a/src/tools/miri/tests/fail/intrinsics/funnel_shr.stderr b/src/tools/miri/tests/fail/intrinsics/funnel_shr.stderr
new file mode 100644
index 00000000000..7361df1b4c7
--- /dev/null
+++ b/src/tools/miri/tests/fail/intrinsics/funnel_shr.stderr
@@ -0,0 +1,15 @@
+error: Undefined Behavior: `assume` called with `false`
+  --> tests/fail/intrinsics/funnel_shr.rs:LL:CC
+   |
+LL |         std::intrinsics::unchecked_funnel_shr(1_u32, 2, 32);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
+   |
+   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
+   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+   = note: BACKTRACE:
+   = note: inside `main` at tests/fail/intrinsics/funnel_shr.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/overlapping_assignment_aggregate.rs b/src/tools/miri/tests/fail/overlapping_assignment_aggregate.rs
new file mode 100644
index 00000000000..8d7b1946242
--- /dev/null
+++ b/src/tools/miri/tests/fail/overlapping_assignment_aggregate.rs
@@ -0,0 +1,18 @@
+//! This is like `pass/overlapping_assignment_aggregate_scalar.rs` but with a non-scalar
+//! type, and that makes it definite UB.
+#![feature(custom_mir, core_intrinsics)]
+#![allow(internal_features)]
+
+use std::intrinsics::mir::*;
+
+#[custom_mir(dialect = "runtime")]
+fn main() {
+    mir! {
+        let _1: ([u8; 1],);
+        {
+            _1.0 = [0_u8; 1];
+            _1 = (_1.0, ); //~ERROR: overlapping ranges
+            Return()
+        }
+    }
+}
diff --git a/src/tools/miri/tests/fail/both_borrows/zero-sized-protected.stack.stderr b/src/tools/miri/tests/fail/overlapping_assignment_aggregate.stderr
index 3e4a7ccac36..f2a6d326b71 100644
--- a/src/tools/miri/tests/fail/both_borrows/zero-sized-protected.stack.stderr
+++ b/src/tools/miri/tests/fail/overlapping_assignment_aggregate.stderr
@@ -1,13 +1,13 @@
-error: Undefined Behavior: entering unreachable code
-  --> tests/fail/both_borrows/zero-sized-protected.rs:LL:CC
+error: Undefined Behavior: `copy_nonoverlapping` called on overlapping ranges
+  --> tests/fail/overlapping_assignment_aggregate.rs:LL:CC
    |
-LL |     unsafe { std::hint::unreachable_unchecked() };
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
+LL |             _1 = (_1.0, );
+   |             ^^^^^^^^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
    = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
    = note: BACKTRACE:
-   = note: inside `main` at tests/fail/both_borrows/zero-sized-protected.rs:LL:CC
+   = note: inside `main` at tests/fail/overlapping_assignment_aggregate.rs:LL:CC
 
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
diff --git a/src/tools/miri/tests/fail/panic/abort_unwind.rs b/src/tools/miri/tests/fail/panic/abort_unwind.rs
index bd819362da4..775cbf62229 100644
--- a/src/tools/miri/tests/fail/panic/abort_unwind.rs
+++ b/src/tools/miri/tests/fail/panic/abort_unwind.rs
@@ -1,6 +1,4 @@
 //@error-in-other-file: the program aborted execution
-//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()"
-//@normalize-stderr-test: "\| +\^+" -> "| ^"
 //@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> ""
 //@normalize-stderr-test: "\n +at [^\n]+" -> ""
 
diff --git a/src/tools/miri/tests/fail/panic/abort_unwind.stderr b/src/tools/miri/tests/fail/panic/abort_unwind.stderr
index 287f7d5432a..23dbc2fb8f3 100644
--- a/src/tools/miri/tests/fail/panic/abort_unwind.stderr
+++ b/src/tools/miri/tests/fail/panic/abort_unwind.stderr
@@ -9,13 +9,12 @@ panic in a function that cannot unwind
 stack backtrace:
 thread caused non-unwinding panic. aborting.
 error: abnormal termination: the program aborted execution
-  --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC
+  --> RUSTLIB/std/src/panicking.rs:LL:CC
    |
-LL | ABORT()
-   | ^ abnormal termination occurred here
+LL |         crate::process::abort();
+   |         ^^^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here
    |
    = note: BACKTRACE:
-   = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC
    = note: inside `std::panicking::panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC
    = note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC
    = note: inside `std::sys::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys/backtrace.rs:LL:CC
@@ -27,7 +26,7 @@ note: inside `main`
   --> tests/fail/panic/abort_unwind.rs:LL:CC
    |
 LL |     std::panic::abort_unwind(|| panic!("PANIC!!!"));
-   | ^
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
diff --git a/src/tools/miri/tests/fail/panic/double_panic.rs b/src/tools/miri/tests/fail/panic/double_panic.rs
index 4d8f4cb6fb7..88421e84529 100644
--- a/src/tools/miri/tests/fail/panic/double_panic.rs
+++ b/src/tools/miri/tests/fail/panic/double_panic.rs
@@ -1,5 +1,3 @@
-//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()"
-//@normalize-stderr-test: "\| +\^+" -> "| ^"
 //@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> ""
 //@normalize-stderr-test: "\n +at [^\n]+" -> ""
 //@error-in-other-file: aborted execution
diff --git a/src/tools/miri/tests/fail/panic/double_panic.stderr b/src/tools/miri/tests/fail/panic/double_panic.stderr
index b76ece7f1e5..edbc0d8fc57 100644
--- a/src/tools/miri/tests/fail/panic/double_panic.stderr
+++ b/src/tools/miri/tests/fail/panic/double_panic.stderr
@@ -12,13 +12,12 @@ thread 'main' ($TID) panicked at RUSTLIB/core/src/panicking.rs:LL:CC:
 panic in a destructor during cleanup
 thread caused non-unwinding panic. aborting.
 error: abnormal termination: the program aborted execution
-  --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC
+  --> RUSTLIB/std/src/panicking.rs:LL:CC
    |
-LL | ABORT()
-   | ^ abnormal termination occurred here
+LL |         crate::process::abort();
+   |         ^^^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here
    |
    = note: BACKTRACE:
-   = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC
    = note: inside `std::panicking::panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC
    = note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC
    = note: inside `std::sys::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys/backtrace.rs:LL:CC
diff --git a/src/tools/miri/tests/fail/panic/panic_abort1.rs b/src/tools/miri/tests/fail/panic/panic_abort1.rs
index 06cb673778a..511b8bddf97 100644
--- a/src/tools/miri/tests/fail/panic/panic_abort1.rs
+++ b/src/tools/miri/tests/fail/panic/panic_abort1.rs
@@ -1,6 +1,4 @@
 //@error-in-other-file: the program aborted execution
-//@normalize-stderr-test: "\| +\^+" -> "| ^"
-//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()"
 //@compile-flags: -C panic=abort
 
 fn main() {
diff --git a/src/tools/miri/tests/fail/panic/panic_abort1.stderr b/src/tools/miri/tests/fail/panic/panic_abort1.stderr
index c469d24287f..c389a9bc075 100644
--- a/src/tools/miri/tests/fail/panic/panic_abort1.stderr
+++ b/src/tools/miri/tests/fail/panic/panic_abort1.stderr
@@ -4,14 +4,12 @@ panicking from libstd
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
 note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 error: abnormal termination: the program aborted execution
-  --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC
+  --> RUSTLIB/std/src/rt.rs:LL:CC
    |
-LL | ABORT()
-   | ^ abnormal termination occurred here
+LL |     crate::process::abort();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here
    |
    = note: BACKTRACE:
-   = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC
-   = note: inside `std::process::abort` at RUSTLIB/std/src/process.rs:LL:CC
    = note: inside `std::rt::__rust_abort` at RUSTLIB/std/src/rt.rs:LL:CC
    = note: inside `panic_abort::__rust_start_panic` at RUSTLIB/panic_abort/src/lib.rs:LL:CC
    = note: inside `std::panicking::rust_panic` at RUSTLIB/std/src/panicking.rs:LL:CC
@@ -23,7 +21,7 @@ note: inside `main`
   --> tests/fail/panic/panic_abort1.rs:LL:CC
    |
 LL |     std::panic!("panicking from libstd");
-   | ^
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
diff --git a/src/tools/miri/tests/fail/panic/panic_abort2.rs b/src/tools/miri/tests/fail/panic/panic_abort2.rs
index c011b3ee7eb..e6c1a130ac9 100644
--- a/src/tools/miri/tests/fail/panic/panic_abort2.rs
+++ b/src/tools/miri/tests/fail/panic/panic_abort2.rs
@@ -1,6 +1,4 @@
 //@error-in-other-file: the program aborted execution
-//@normalize-stderr-test: "\| +\^+" -> "| ^"
-//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()"
 //@compile-flags: -C panic=abort
 
 fn main() {
diff --git a/src/tools/miri/tests/fail/panic/panic_abort2.stderr b/src/tools/miri/tests/fail/panic/panic_abort2.stderr
index bc7918f5f86..5fe2245cbe0 100644
--- a/src/tools/miri/tests/fail/panic/panic_abort2.stderr
+++ b/src/tools/miri/tests/fail/panic/panic_abort2.stderr
@@ -4,14 +4,12 @@ thread 'main' ($TID) panicked at tests/fail/panic/panic_abort2.rs:LL:CC:
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
 note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 error: abnormal termination: the program aborted execution
-  --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC
+  --> RUSTLIB/std/src/rt.rs:LL:CC
    |
-LL | ABORT()
-   | ^ abnormal termination occurred here
+LL |     crate::process::abort();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here
    |
    = note: BACKTRACE:
-   = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC
-   = note: inside `std::process::abort` at RUSTLIB/std/src/process.rs:LL:CC
    = note: inside `std::rt::__rust_abort` at RUSTLIB/std/src/rt.rs:LL:CC
    = note: inside `panic_abort::__rust_start_panic` at RUSTLIB/panic_abort/src/lib.rs:LL:CC
    = note: inside `std::panicking::rust_panic` at RUSTLIB/std/src/panicking.rs:LL:CC
@@ -23,7 +21,7 @@ note: inside `main`
   --> tests/fail/panic/panic_abort2.rs:LL:CC
    |
 LL |     std::panic!("{}-panicking from libstd", 42);
-   | ^
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
diff --git a/src/tools/miri/tests/fail/panic/panic_abort3.rs b/src/tools/miri/tests/fail/panic/panic_abort3.rs
index 911dc4a44ab..28a28c923fd 100644
--- a/src/tools/miri/tests/fail/panic/panic_abort3.rs
+++ b/src/tools/miri/tests/fail/panic/panic_abort3.rs
@@ -1,6 +1,4 @@
 //@error-in-other-file: the program aborted execution
-//@normalize-stderr-test: "\| +\^+" -> "| ^"
-//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()"
 //@compile-flags: -C panic=abort
 
 fn main() {
diff --git a/src/tools/miri/tests/fail/panic/panic_abort3.stderr b/src/tools/miri/tests/fail/panic/panic_abort3.stderr
index 553bfa61635..cac24ca41c7 100644
--- a/src/tools/miri/tests/fail/panic/panic_abort3.stderr
+++ b/src/tools/miri/tests/fail/panic/panic_abort3.stderr
@@ -4,14 +4,12 @@ panicking from libcore
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
 note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 error: abnormal termination: the program aborted execution
-  --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC
+  --> RUSTLIB/std/src/rt.rs:LL:CC
    |
-LL | ABORT()
-   | ^ abnormal termination occurred here
+LL |     crate::process::abort();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here
    |
    = note: BACKTRACE:
-   = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC
-   = note: inside `std::process::abort` at RUSTLIB/std/src/process.rs:LL:CC
    = note: inside `std::rt::__rust_abort` at RUSTLIB/std/src/rt.rs:LL:CC
    = note: inside `panic_abort::__rust_start_panic` at RUSTLIB/panic_abort/src/lib.rs:LL:CC
    = note: inside `std::panicking::rust_panic` at RUSTLIB/std/src/panicking.rs:LL:CC
@@ -23,7 +21,7 @@ note: inside `main`
   --> tests/fail/panic/panic_abort3.rs:LL:CC
    |
 LL |     core::panic!("panicking from libcore");
-   | ^
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
diff --git a/src/tools/miri/tests/fail/panic/panic_abort4.rs b/src/tools/miri/tests/fail/panic/panic_abort4.rs
index 696fdff7422..248064d14d5 100644
--- a/src/tools/miri/tests/fail/panic/panic_abort4.rs
+++ b/src/tools/miri/tests/fail/panic/panic_abort4.rs
@@ -1,6 +1,4 @@
 //@error-in-other-file: the program aborted execution
-//@normalize-stderr-test: "\| +\^+" -> "| ^"
-//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()"
 //@compile-flags: -C panic=abort
 
 fn main() {
diff --git a/src/tools/miri/tests/fail/panic/panic_abort4.stderr b/src/tools/miri/tests/fail/panic/panic_abort4.stderr
index 07ecab6661e..21195729ae8 100644
--- a/src/tools/miri/tests/fail/panic/panic_abort4.stderr
+++ b/src/tools/miri/tests/fail/panic/panic_abort4.stderr
@@ -4,14 +4,12 @@ thread 'main' ($TID) panicked at tests/fail/panic/panic_abort4.rs:LL:CC:
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
 note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 error: abnormal termination: the program aborted execution
-  --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC
+  --> RUSTLIB/std/src/rt.rs:LL:CC
    |
-LL | ABORT()
-   | ^ abnormal termination occurred here
+LL |     crate::process::abort();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here
    |
    = note: BACKTRACE:
-   = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC
-   = note: inside `std::process::abort` at RUSTLIB/std/src/process.rs:LL:CC
    = note: inside `std::rt::__rust_abort` at RUSTLIB/std/src/rt.rs:LL:CC
    = note: inside `panic_abort::__rust_start_panic` at RUSTLIB/panic_abort/src/lib.rs:LL:CC
    = note: inside `std::panicking::rust_panic` at RUSTLIB/std/src/panicking.rs:LL:CC
@@ -23,7 +21,7 @@ note: inside `main`
   --> tests/fail/panic/panic_abort4.rs:LL:CC
    |
 LL |     core::panic!("{}-panicking from libcore", 42);
-   | ^
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
diff --git a/src/tools/miri/tests/fail/provenance/mix-ptrs1.rs b/src/tools/miri/tests/fail/provenance/mix-ptrs1.rs
new file mode 100644
index 00000000000..0b7266a4da6
--- /dev/null
+++ b/src/tools/miri/tests/fail/provenance/mix-ptrs1.rs
@@ -0,0 +1,21 @@
+use std::{mem, ptr};
+
+const PTR_SIZE: usize = mem::size_of::<&i32>();
+
+fn main() {
+    unsafe {
+        let ptr = &0 as *const i32;
+        let arr = [ptr; 2];
+        // We want to do a scalar read of a pointer at offset PTR_SIZE/2 into this array. But we
+        // cannot use a packed struct or `read_unaligned`, as those use the memcpy code path in
+        // Miri. So instead we shift the entire array by a bit and then the actual read we want to
+        // do is perfectly aligned.
+        let mut target_arr = [ptr::null::<i32>(); 3];
+        let target = target_arr.as_mut_ptr().cast::<u8>();
+        target.add(PTR_SIZE / 2).cast::<[*const i32; 2]>().write_unaligned(arr);
+        // Now target_arr[1] is a mix of the two `ptr` we had stored in `arr`.
+        // They all have the same provenance, but not in the right order, so we reject this.
+        let strange_ptr = target_arr[1];
+        assert_eq!(*strange_ptr.with_addr(ptr.addr()), 0); //~ERROR: no provenance
+    }
+}
diff --git a/src/tools/miri/tests/fail/provenance/mix-ptrs1.stderr b/src/tools/miri/tests/fail/provenance/mix-ptrs1.stderr
new file mode 100644
index 00000000000..88d75135a76
--- /dev/null
+++ b/src/tools/miri/tests/fail/provenance/mix-ptrs1.stderr
@@ -0,0 +1,16 @@
+error: Undefined Behavior: pointer not dereferenceable: pointer must be dereferenceable for 4 bytes, but got $HEX[noalloc] which is a dangling pointer (it has no provenance)
+  --> tests/fail/provenance/mix-ptrs1.rs:LL:CC
+   |
+LL |         assert_eq!(*strange_ptr.with_addr(ptr.addr()), 0);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
+   |
+   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
+   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+   = note: BACKTRACE:
+   = note: inside `main` at RUSTLIB/core/src/macros/mod.rs:LL:CC
+   = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+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/provenance/mix-ptrs2.rs b/src/tools/miri/tests/fail/provenance/mix-ptrs2.rs
new file mode 100644
index 00000000000..9ef40c194ab
--- /dev/null
+++ b/src/tools/miri/tests/fail/provenance/mix-ptrs2.rs
@@ -0,0 +1,37 @@
+use std::mem;
+
+const PTR_SIZE: usize = mem::size_of::<&i32>();
+
+/// Overwrite one byte of a pointer, then restore it.
+fn main() {
+    unsafe fn ptr_bytes<'x>(ptr: &'x mut *const i32) -> &'x mut [mem::MaybeUninit<u8>; PTR_SIZE] {
+        mem::transmute(ptr)
+    }
+
+    // Returns a value with the same provenance as `x` but 0 for the integer value.
+    // `x` must be initialized.
+    unsafe fn zero_with_provenance(x: mem::MaybeUninit<u8>) -> mem::MaybeUninit<u8> {
+        let ptr = [x; PTR_SIZE];
+        let ptr: *const i32 = mem::transmute(ptr);
+        let mut ptr = ptr.with_addr(0);
+        ptr_bytes(&mut ptr)[0]
+    }
+
+    unsafe {
+        let ptr = &42;
+        let mut ptr = ptr as *const i32;
+        // Get a bytewise view of the pointer.
+        let ptr_bytes = ptr_bytes(&mut ptr);
+
+        // The highest bytes must be 0 for this to work.
+        let hi = if cfg!(target_endian = "little") { ptr_bytes.len() - 1 } else { 0 };
+        assert_eq!(*ptr_bytes[hi].as_ptr().cast::<u8>(), 0);
+        // Overwrite provenance on the last byte.
+        ptr_bytes[hi] = mem::MaybeUninit::new(0);
+        // Restore it from the another byte.
+        ptr_bytes[hi] = zero_with_provenance(ptr_bytes[1]);
+
+        // Now ptr is almost good, except the provenance fragment indices do not work out...
+        assert_eq!(*ptr, 42); //~ERROR: no provenance
+    }
+}
diff --git a/src/tools/miri/tests/fail/provenance/mix-ptrs2.stderr b/src/tools/miri/tests/fail/provenance/mix-ptrs2.stderr
new file mode 100644
index 00000000000..636378952ba
--- /dev/null
+++ b/src/tools/miri/tests/fail/provenance/mix-ptrs2.stderr
@@ -0,0 +1,16 @@
+error: Undefined Behavior: pointer not dereferenceable: pointer must be dereferenceable for 4 bytes, but got $HEX[noalloc] which is a dangling pointer (it has no provenance)
+  --> tests/fail/provenance/mix-ptrs2.rs:LL:CC
+   |
+LL |         assert_eq!(*ptr, 42);
+   |         ^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
+   |
+   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
+   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+   = note: BACKTRACE:
+   = note: inside `main` at RUSTLIB/core/src/macros/mod.rs:LL:CC
+   = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+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/provenance/provenance_transmute.rs b/src/tools/miri/tests/fail/provenance/provenance_transmute.rs
index d72f10530d7..60cb9a7f6bf 100644
--- a/src/tools/miri/tests/fail/provenance/provenance_transmute.rs
+++ b/src/tools/miri/tests/fail/provenance/provenance_transmute.rs
@@ -1,5 +1,7 @@
 //@compile-flags: -Zmiri-permissive-provenance
 
+#![allow(integer_to_ptr_transmutes)]
+
 use std::mem;
 
 // This is the example from
diff --git a/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.rs b/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.rs
index 6119e8604b4..0f7cf189f33 100644
--- a/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.rs
+++ b/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.rs
@@ -1,7 +1,5 @@
 //! This is a regression test for <https://github.com/rust-lang/miri/issues/4188>: The precondition
 //! check in `ptr::swap_nonoverlapping` was incorrectly disabled in Miri.
-//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()"
-//@normalize-stderr-test: "\| +\^+" -> "| ^"
 //@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> ""
 //@normalize-stderr-test: "\n +at [^\n]+" -> ""
 //@error-in-other-file: aborted execution
diff --git a/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.stderr b/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.stderr
index 70f5d498da3..c5f6e62b869 100644
--- a/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.stderr
+++ b/src/tools/miri/tests/fail/ptr_swap_nonoverlapping.stderr
@@ -7,13 +7,12 @@ note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
 note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
 thread caused non-unwinding panic. aborting.
 error: abnormal termination: the program aborted execution
-  --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC
+  --> RUSTLIB/std/src/panicking.rs:LL:CC
    |
-LL | ABORT()
-   | ^ abnormal termination occurred here
+LL |         crate::process::abort();
+   |         ^^^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here
    |
    = note: BACKTRACE:
-   = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC
    = note: inside `std::panicking::panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC
    = note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC
    = note: inside `std::sys::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys/backtrace.rs:LL:CC
@@ -22,7 +21,7 @@ note: inside `main`
   --> tests/fail/ptr_swap_nonoverlapping.rs:LL:CC
    |
 LL |         std::ptr::swap_nonoverlapping(ptr, ptr, 1);
-   | ^
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
diff --git a/src/tools/miri/tests/fail/terminate-terminator.rs b/src/tools/miri/tests/fail/terminate-terminator.rs
index 31ae829a2de..421cddca160 100644
--- a/src/tools/miri/tests/fail/terminate-terminator.rs
+++ b/src/tools/miri/tests/fail/terminate-terminator.rs
@@ -1,6 +1,4 @@
 //@compile-flags: -Zmir-opt-level=3 -Zinline-mir-hint-threshold=1000
-//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()"
-//@normalize-stderr-test: "\| +\^+" -> "| ^"
 //@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> ""
 //@normalize-stderr-test: "\n +at [^\n]+" -> ""
 //@error-in-other-file: aborted execution
diff --git a/src/tools/miri/tests/fail/terminate-terminator.stderr b/src/tools/miri/tests/fail/terminate-terminator.stderr
index 228e2d3de1f..8ae649a392b 100644
--- a/src/tools/miri/tests/fail/terminate-terminator.stderr
+++ b/src/tools/miri/tests/fail/terminate-terminator.stderr
@@ -11,13 +11,12 @@ panic in a function that cannot unwind
 stack backtrace:
 thread caused non-unwinding panic. aborting.
 error: abnormal termination: the program aborted execution
-  --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC
+  --> RUSTLIB/std/src/panicking.rs:LL:CC
    |
-LL | ABORT()
-   | ^ abnormal termination occurred here
+LL |         crate::process::abort();
+   |         ^^^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here
    |
    = note: BACKTRACE:
-   = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC
    = note: inside `std::panicking::panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC
    = note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC
    = note: inside `std::sys::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys/backtrace.rs:LL:CC
@@ -36,12 +35,12 @@ note: inside `panic_abort`
   --> tests/fail/terminate-terminator.rs:LL:CC
    |
 LL |     has_cleanup();
-   | ^
+   |     ^^^^^^^^^^^^^
 note: inside `main`
   --> tests/fail/terminate-terminator.rs:LL:CC
    |
 LL |     panic_abort();
-   | ^
+   |     ^^^^^^^^^^^^^
 
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
diff --git a/src/tools/miri/tests/fail/tree_borrows/alternate-read-write.stderr b/src/tools/miri/tests/fail/tree_borrows/alternate-read-write.stderr
index 63c6287f20d..aff482abfa0 100644
--- a/src/tools/miri/tests/fail/tree_borrows/alternate-read-write.stderr
+++ b/src/tools/miri/tests/fail/tree_borrows/alternate-read-write.stderr
@@ -5,13 +5,14 @@ LL |     *y += 1; // Failure
    |     ^^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> has state Frozen which forbids this child write access
 help: the accessed tag <TAG> was created here, in the initial state Reserved
   --> tests/fail/tree_borrows/alternate-read-write.rs:LL:CC
    |
 LL |     let y = unsafe { &mut *(x as *mut u8) };
    |                      ^^^^^^^^^^^^^^^^^^^^
-help: the accessed tag <TAG> later transitioned to Active due to a child write access at offsets [0x0..0x1]
+help: the accessed tag <TAG> later transitioned to Unique due to a child write access at offsets [0x0..0x1]
   --> tests/fail/tree_borrows/alternate-read-write.rs:LL:CC
    |
 LL |     *y += 1; // Success
diff --git a/src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.stderr b/src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.stderr
index 81d5aba6b2e..5e43d174ed9 100644
--- a/src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.stderr
+++ b/src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.stderr
@@ -11,6 +11,7 @@ LL |         (*a).field1 = 88;
    |         ^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> (a) has state Frozen which forbids this child write access
 help: the accessed tag <TAG> was created here, in the initial state Cell
   --> tests/fail/tree_borrows/cell-inside-struct.rs:LL:CC
diff --git a/src/tools/miri/tests/fail/tree_borrows/error-range.stderr b/src/tools/miri/tests/fail/tree_borrows/error-range.stderr
index 9a52f68d9b6..3dc120a20d2 100644
--- a/src/tools/miri/tests/fail/tree_borrows/error-range.stderr
+++ b/src/tools/miri/tests/fail/tree_borrows/error-range.stderr
@@ -5,6 +5,7 @@ LL |         rmut[5] += 1;
    |         ^^^^^^^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> has state Disabled which forbids this child read access
 help: the accessed tag <TAG> was created here, in the initial state Reserved
   --> tests/fail/tree_borrows/error-range.rs:LL:CC
diff --git a/src/tools/miri/tests/fail/tree_borrows/fnentry_invalidation.stderr b/src/tools/miri/tests/fail/tree_borrows/fnentry_invalidation.stderr
index ed345aae38f..bfd6854514e 100644
--- a/src/tools/miri/tests/fail/tree_borrows/fnentry_invalidation.stderr
+++ b/src/tools/miri/tests/fail/tree_borrows/fnentry_invalidation.stderr
@@ -5,13 +5,14 @@ LL |         *z = 2;
    |         ^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> has state Frozen which forbids this child write access
 help: the accessed tag <TAG> was created here, in the initial state Reserved
   --> tests/fail/tree_borrows/fnentry_invalidation.rs:LL:CC
    |
 LL |     let z = &mut x as *mut i32;
    |             ^^^^^^
-help: the accessed tag <TAG> later transitioned to Active due to a child write access at offsets [0x0..0x4]
+help: the accessed tag <TAG> later transitioned to Unique due to a child write access at offsets [0x0..0x4]
   --> tests/fail/tree_borrows/fnentry_invalidation.rs:LL:CC
    |
 LL |         *z = 1;
diff --git a/src/tools/miri/tests/fail/tree_borrows/frozen-lazy-write-to-surrounding.rs b/src/tools/miri/tests/fail/tree_borrows/frozen-lazy-write-to-surrounding.rs
new file mode 100644
index 00000000000..7d51050f32b
--- /dev/null
+++ b/src/tools/miri/tests/fail/tree_borrows/frozen-lazy-write-to-surrounding.rs
@@ -0,0 +1,9 @@
+//@compile-flags: -Zmiri-tree-borrows
+
+fn main() {
+    // Since the "inside" part is `!Freeze`, the permission to mutate is gone.
+    let pair = ((), 1);
+    let x = &pair.0;
+    let ptr = (&raw const *x).cast::<i32>().cast_mut();
+    unsafe { ptr.write(0) }; //~ERROR: /write access .* forbidden/
+}
diff --git a/src/tools/miri/tests/fail/tree_borrows/frozen-lazy-write-to-surrounding.stderr b/src/tools/miri/tests/fail/tree_borrows/frozen-lazy-write-to-surrounding.stderr
new file mode 100644
index 00000000000..e9800468c57
--- /dev/null
+++ b/src/tools/miri/tests/fail/tree_borrows/frozen-lazy-write-to-surrounding.stderr
@@ -0,0 +1,21 @@
+error: Undefined Behavior: write access through <TAG> at ALLOC[0x0] is forbidden
+  --> tests/fail/tree_borrows/frozen-lazy-write-to-surrounding.rs:LL:CC
+   |
+LL |     unsafe { ptr.write(0) };
+   |              ^^^^^^^^^^^^ Undefined Behavior occurred here
+   |
+   = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
+   = help: the accessed tag <TAG> has state Frozen which forbids this child write access
+help: the accessed tag <TAG> was created here, in the initial state Frozen
+  --> tests/fail/tree_borrows/frozen-lazy-write-to-surrounding.rs:LL:CC
+   |
+LL |     let x = &pair.0;
+   |             ^^^^^^^
+   = note: BACKTRACE (of the first span):
+   = note: inside `main` at tests/fail/tree_borrows/frozen-lazy-write-to-surrounding.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/tree_borrows/outside-range.stderr b/src/tools/miri/tests/fail/tree_borrows/outside-range.stderr
index bbd33dc3560..7960f42faa5 100644
--- a/src/tools/miri/tests/fail/tree_borrows/outside-range.stderr
+++ b/src/tools/miri/tests/fail/tree_borrows/outside-range.stderr
@@ -5,6 +5,7 @@ LL |     *y.add(3) = 42;
    |     ^^^^^^^^^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> is foreign to the protected tag <TAG> (i.e., it is not a child)
    = help: this foreign write access would cause the protected tag <TAG> (currently Reserved) to become Disabled
    = help: protected tags must never be Disabled
diff --git a/src/tools/miri/tests/fail/tree_borrows/parent_read_freezes_raw_mut.stderr b/src/tools/miri/tests/fail/tree_borrows/parent_read_freezes_raw_mut.stderr
index a4119bc0178..7a713abcbc4 100644
--- a/src/tools/miri/tests/fail/tree_borrows/parent_read_freezes_raw_mut.stderr
+++ b/src/tools/miri/tests/fail/tree_borrows/parent_read_freezes_raw_mut.stderr
@@ -5,13 +5,14 @@ LL |         *ptr = 0;
    |         ^^^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> has state Frozen which forbids this child write access
 help: the accessed tag <TAG> was created here, in the initial state Reserved
   --> tests/fail/tree_borrows/parent_read_freezes_raw_mut.rs:LL:CC
    |
 LL |         let mref = &mut root;
    |                    ^^^^^^^^^
-help: the accessed tag <TAG> later transitioned to Active due to a child write access at offsets [0x0..0x1]
+help: the accessed tag <TAG> later transitioned to Unique due to a child write access at offsets [0x0..0x1]
   --> tests/fail/tree_borrows/parent_read_freezes_raw_mut.rs:LL:CC
    |
 LL |         *ptr = 0; // Write
diff --git a/src/tools/miri/tests/fail/tree_borrows/pass_invalid_mut.stderr b/src/tools/miri/tests/fail/tree_borrows/pass_invalid_mut.stderr
index de01a9f0e80..9a70d248aa0 100644
--- a/src/tools/miri/tests/fail/tree_borrows/pass_invalid_mut.stderr
+++ b/src/tools/miri/tests/fail/tree_borrows/pass_invalid_mut.stderr
@@ -5,6 +5,7 @@ LL |     *nope = 31;
    |     ^^^^^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> is a child of the conflicting tag <TAG>
    = help: the conflicting tag <TAG> has state Frozen which forbids this child write access
 help: the accessed tag <TAG> was created here
@@ -17,7 +18,7 @@ help: the conflicting tag <TAG> was created here, in the initial state Reserved
    |
 LL |     let xref = unsafe { &mut *xraw };
    |                         ^^^^^^^^^^
-help: the conflicting tag <TAG> later transitioned to Active due to a child write access at offsets [0x0..0x4]
+help: the conflicting tag <TAG> later transitioned to Unique due to a child write access at offsets [0x0..0x4]
   --> tests/fail/tree_borrows/pass_invalid_mut.rs:LL:CC
    |
 LL |     *xref = 18; // activate xref
diff --git a/src/tools/miri/tests/fail/tree_borrows/protector-write-lazy.stderr b/src/tools/miri/tests/fail/tree_borrows/protector-write-lazy.stderr
index af226b9e3be..7cebd7f32a0 100644
--- a/src/tools/miri/tests/fail/tree_borrows/protector-write-lazy.stderr
+++ b/src/tools/miri/tests/fail/tree_borrows/protector-write-lazy.stderr
@@ -5,6 +5,7 @@ LL |     unsafe { println!("Value of funky: {}", *funky_ptr_lazy_on_fst_elem) }
    |                                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> has state Disabled which forbids this reborrow (acting as a child read access)
 help: the accessed tag <TAG> was created here, in the initial state Reserved
   --> tests/fail/tree_borrows/protector-write-lazy.rs:LL:CC
diff --git a/src/tools/miri/tests/fail/tree_borrows/repeated_foreign_read_lazy_conflicted.stderr b/src/tools/miri/tests/fail/tree_borrows/repeated_foreign_read_lazy_conflicted.stderr
index 8421df5ab79..012d7caef50 100644
--- a/src/tools/miri/tests/fail/tree_borrows/repeated_foreign_read_lazy_conflicted.stderr
+++ b/src/tools/miri/tests/fail/tree_borrows/repeated_foreign_read_lazy_conflicted.stderr
@@ -5,6 +5,7 @@ LL |     *(x as *mut u8).byte_sub(1) = 42;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> has state Reserved (conflicted) which forbids this child write access
 help: the accessed tag <TAG> was created here, in the initial state Reserved
   --> tests/fail/tree_borrows/repeated_foreign_read_lazy_conflicted.rs:LL:CC
diff --git a/src/tools/miri/tests/fail/tree_borrows/reserved/cell-protected-write.stderr b/src/tools/miri/tests/fail/tree_borrows/reserved/cell-protected-write.stderr
index 00d5c3e6b67..b1c3ffe86ef 100644
--- a/src/tools/miri/tests/fail/tree_borrows/reserved/cell-protected-write.stderr
+++ b/src/tools/miri/tests/fail/tree_borrows/reserved/cell-protected-write.stderr
@@ -15,6 +15,7 @@ LL |             *y = 1;
    |             ^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> (y, callee:y, caller:y) is foreign to the protected tag <TAG> (callee:x) (i.e., it is not a child)
    = help: this foreign write access would cause the protected tag <TAG> (callee:x) (currently Reserved) to become Disabled
    = help: protected tags must never be Disabled
diff --git a/src/tools/miri/tests/fail/tree_borrows/reserved/int-protected-write.stderr b/src/tools/miri/tests/fail/tree_borrows/reserved/int-protected-write.stderr
index c003938db33..5f3129b9dc0 100644
--- a/src/tools/miri/tests/fail/tree_borrows/reserved/int-protected-write.stderr
+++ b/src/tools/miri/tests/fail/tree_borrows/reserved/int-protected-write.stderr
@@ -15,6 +15,7 @@ LL |             *y = 0;
    |             ^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> (y, callee:y, caller:y) is foreign to the protected tag <TAG> (callee:x) (i.e., it is not a child)
    = help: this foreign write access would cause the protected tag <TAG> (callee:x) (currently Reserved) to become Disabled
    = help: protected tags must never be Disabled
diff --git a/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.rs b/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.rs
index 024a14600b1..3e5d83911ee 100644
--- a/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.rs
+++ b/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.rs
@@ -60,8 +60,7 @@ fn main() {
         fn inner(x: &mut u8, b: IdxBarrier) {
             *x = 42; // activate immediately
             synchronized!(b, "[lazy] retag y (&mut, protect, IM)");
-            // A spurious write should be valid here because `x` is
-            // `Active` and protected.
+            // A spurious write should be valid here because `x` is `Unique` and protected.
             if cfg!(with) {
                 synchronized!(b, "spurious write x (executed)");
                 *x = 64;
diff --git a/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.with.stderr b/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.with.stderr
index a8db7070333..7565fa6203f 100644
--- a/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.with.stderr
+++ b/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.with.stderr
@@ -19,6 +19,7 @@ LL |         unsafe { *y = 13 }
    |                  ^^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> has state Disabled which forbids this child write access
 help: the accessed tag <TAG> was created here, in the initial state Reserved
   --> tests/fail/tree_borrows/reservedim_spurious_write.rs:LL:CC
diff --git a/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.without.stderr b/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.without.stderr
index 5eeefd450c6..7f791c88dde 100644
--- a/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.without.stderr
+++ b/src/tools/miri/tests/fail/tree_borrows/reservedim_spurious_write.without.stderr
@@ -19,6 +19,7 @@ LL |         unsafe { *y = 13 }
    |                  ^^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> has state Disabled which forbids this child write access
 help: the accessed tag <TAG> was created here, in the initial state Reserved
   --> tests/fail/tree_borrows/reservedim_spurious_write.rs:LL:CC
diff --git a/src/tools/miri/tests/fail/tree_borrows/return_invalid_mut.stderr b/src/tools/miri/tests/fail/tree_borrows/return_invalid_mut.stderr
index a0ebf0ecbd2..4b6308847bb 100644
--- a/src/tools/miri/tests/fail/tree_borrows/return_invalid_mut.stderr
+++ b/src/tools/miri/tests/fail/tree_borrows/return_invalid_mut.stderr
@@ -5,6 +5,7 @@ LL |     *ret = 3;
    |     ^^^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> is a child of the conflicting tag <TAG>
    = help: the conflicting tag <TAG> has state Frozen which forbids this child write access
 help: the accessed tag <TAG> was created here
@@ -17,7 +18,7 @@ help: the conflicting tag <TAG> was created here, in the initial state Reserved
    |
 LL |     let ret = unsafe { &mut (*xraw).1 };
    |                        ^^^^^^^^^^^^^^
-help: the conflicting tag <TAG> later transitioned to Active due to a child write access at offsets [0x4..0x8]
+help: the conflicting tag <TAG> later transitioned to Unique due to a child write access at offsets [0x4..0x8]
   --> tests/fail/tree_borrows/return_invalid_mut.rs:LL:CC
    |
 LL |     *ret = *ret; // activate
diff --git a/src/tools/miri/tests/fail/tree_borrows/spurious_read.stderr b/src/tools/miri/tests/fail/tree_borrows/spurious_read.stderr
index a3b0d5f13ad..8f2534d6b6e 100644
--- a/src/tools/miri/tests/fail/tree_borrows/spurious_read.stderr
+++ b/src/tools/miri/tests/fail/tree_borrows/spurious_read.stderr
@@ -18,6 +18,7 @@ LL |                 *y = 2;
    |                 ^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> has state Reserved (conflicted) which forbids this child write access
 help: the accessed tag <TAG> was created here, in the initial state Reserved
   --> tests/fail/tree_borrows/spurious_read.rs:LL:CC
diff --git a/src/tools/miri/tests/fail/tree_borrows/strongly-protected.stderr b/src/tools/miri/tests/fail/tree_borrows/strongly-protected.stderr
index 56617f6d6a6..685abee3292 100644
--- a/src/tools/miri/tests/fail/tree_borrows/strongly-protected.stderr
+++ b/src/tools/miri/tests/fail/tree_borrows/strongly-protected.stderr
@@ -5,6 +5,7 @@ LL |                 self.1.deallocate(From::from(ptr.cast()), layout);
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the allocation of the accessed tag <TAG> also contains the strongly protected tag <TAG>
    = help: the strongly protected tag <TAG> disallows deallocations
 help: the accessed tag <TAG> was created here
diff --git a/src/tools/miri/tests/fail/tree_borrows/subtree_traversal_skipping_diagnostics.rs b/src/tools/miri/tests/fail/tree_borrows/subtree_traversal_skipping_diagnostics.rs
index 6514334b09d..94a3bb80544 100644
--- a/src/tools/miri/tests/fail/tree_borrows/subtree_traversal_skipping_diagnostics.rs
+++ b/src/tools/miri/tests/fail/tree_borrows/subtree_traversal_skipping_diagnostics.rs
@@ -5,7 +5,7 @@
 
 // When this method is called, the tree will be a single line and look like this,
 // with other_ptr being the root at the top
-// other_ptr = root : Active
+// other_ptr = root : Unique
 // intermediary     : Frozen // an intermediary node
 // m                : Reserved
 fn write_to_mut(m: &mut u8, other_ptr: *const u8) {
diff --git a/src/tools/miri/tests/fail/tree_borrows/subtree_traversal_skipping_diagnostics.stderr b/src/tools/miri/tests/fail/tree_borrows/subtree_traversal_skipping_diagnostics.stderr
index 8669a14ecc7..b98d2fafcf4 100644
--- a/src/tools/miri/tests/fail/tree_borrows/subtree_traversal_skipping_diagnostics.stderr
+++ b/src/tools/miri/tests/fail/tree_borrows/subtree_traversal_skipping_diagnostics.stderr
@@ -5,6 +5,7 @@ LL |     *m = 42;
    |     ^^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> is a child of the conflicting tag <TAG>
    = help: the conflicting tag <TAG> has state Frozen which forbids this child write access
 help: the accessed tag <TAG> was created here
diff --git a/src/tools/miri/tests/fail/tree_borrows/unique.default.stderr b/src/tools/miri/tests/fail/tree_borrows/unique.default.stderr
deleted file mode 100644
index c7d72f70f40..00000000000
--- a/src/tools/miri/tests/fail/tree_borrows/unique.default.stderr
+++ /dev/null
@@ -1,32 +0,0 @@
-error: Undefined Behavior: write access through <TAG> at ALLOC[0x0] is forbidden
-  --> tests/fail/tree_borrows/unique.rs:LL:CC
-   |
-LL |         *uniq.as_ptr() = 3;
-   |         ^^^^^^^^^^^^^^^^^^ write access through <TAG> at ALLOC[0x0] is forbidden
-   |
-   = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
-   = help: the accessed tag <TAG> has state Frozen which forbids this child write access
-help: the accessed tag <TAG> was created here, in the initial state Reserved
-  --> tests/fail/tree_borrows/unique.rs:LL:CC
-   |
-LL |     let refmut = &mut data;
-   |                  ^^^^^^^^^
-help: the accessed tag <TAG> later transitioned to Active due to a child write access at offsets [0x0..0x1]
-  --> tests/fail/tree_borrows/unique.rs:LL:CC
-   |
-LL |         *uniq.as_ptr() = 1; // activation
-   |         ^^^^^^^^^^^^^^^^^^
-   = help: this transition corresponds to the first write to a 2-phase borrowed mutable reference
-help: the accessed tag <TAG> later transitioned to Frozen due to a foreign read access at offsets [0x0..0x1]
-  --> tests/fail/tree_borrows/unique.rs:LL:CC
-   |
-LL |         let _definitely_parent = data; // definitely Frozen by now
-   |                                  ^^^^
-   = help: this transition corresponds to a loss of write permissions
-   = note: BACKTRACE (of the first span):
-   = note: inside `main` at tests/fail/tree_borrows/unique.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/tree_borrows/write-during-2phase.stderr b/src/tools/miri/tests/fail/tree_borrows/write-during-2phase.stderr
index 693d1853550..7f55e06a6bb 100644
--- a/src/tools/miri/tests/fail/tree_borrows/write-during-2phase.stderr
+++ b/src/tools/miri/tests/fail/tree_borrows/write-during-2phase.stderr
@@ -5,6 +5,7 @@ LL |     fn add(&mut self, n: u64) -> u64 {
    |            ^^^^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a potential bug in the program: it performed an invalid operation, but the Tree Borrows rules it violated are still experimental
+   = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/tree-borrows.md for further information
    = help: the accessed tag <TAG> has state Disabled which forbids this reborrow (acting as a child read access)
 help: the accessed tag <TAG> was created here, in the initial state Reserved
   --> tests/fail/tree_borrows/write-during-2phase.rs:LL:CC
diff --git a/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.rs b/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.rs
index 1cf70d9d9a3..78a5098d21a 100644
--- a/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.rs
+++ b/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.rs
@@ -9,11 +9,10 @@ use std::alloc::{Layout, alloc, dealloc};
 use std::mem::{self, MaybeUninit};
 use std::slice::from_raw_parts;
 
-fn byte_with_provenance<T>(val: u8, prov: *const T) -> MaybeUninit<u8> {
-    let ptr = prov.with_addr(val as usize);
+fn byte_with_provenance<T>(val: u8, prov: *const T, frag_idx: usize) -> MaybeUninit<u8> {
+    let ptr = prov.with_addr(usize::from_ne_bytes([val; _]));
     let bytes: [MaybeUninit<u8>; mem::size_of::<*const ()>()] = unsafe { mem::transmute(ptr) };
-    let lsb = if cfg!(target_endian = "little") { 0 } else { bytes.len() - 1 };
-    bytes[lsb]
+    bytes[frag_idx]
 }
 
 fn main() {
@@ -21,10 +20,10 @@ fn main() {
     unsafe {
         let ptr = alloc(layout);
         let ptr_raw = ptr.cast::<MaybeUninit<u8>>();
-        *ptr_raw.add(0) = byte_with_provenance(0x42, &42u8);
+        *ptr_raw.add(0) = byte_with_provenance(0x42, &42u8, 0);
         *ptr.add(1) = 0x12;
         *ptr.add(2) = 0x13;
-        *ptr_raw.add(3) = byte_with_provenance(0x43, &0u8);
+        *ptr_raw.add(3) = byte_with_provenance(0x43, &0u8, 1);
         let slice1 = from_raw_parts(ptr, 8);
         let slice2 = from_raw_parts(ptr.add(8), 8);
         drop(slice1.cmp(slice2));
diff --git a/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.stderr b/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.stderr
index d3253317fae..305d8d4fbed 100644
--- a/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.stderr
+++ b/src/tools/miri/tests/fail/uninit/uninit_alloc_diagnostic_with_provenance.stderr
@@ -17,7 +17,7 @@ LL |         drop(slice1.cmp(slice2));
 
 Uninitialized memory occurred at ALLOC[0x4..0x8], in this allocation:
 ALLOC (Rust heap, size: 16, align: 8) {
-    ╾42[ALLOC]<TAG> (1 ptr byte)╼ 12 13 ╾43[ALLOC]<TAG> (1 ptr byte)╼ __ __ __ __ __ __ __ __ __ __ __ __ │ ━..━░░░░░░░░░░░░
+    ╾42[ALLOC]<TAG> (ptr fragment 0)╼ 12 13 ╾43[ALLOC]<TAG> (ptr fragment 1)╼ __ __ __ __ __ __ __ __ __ __ __ __ │ ━..━░░░░░░░░░░░░
 }
 ALLOC (global (static or const), size: 1, align: 1) {
     2a                                              │ *
diff --git a/src/tools/miri/tests/fail/unwind-action-terminate.rs b/src/tools/miri/tests/fail/unwind-action-terminate.rs
index f0fbcfd8867..e1c8909fffa 100644
--- a/src/tools/miri/tests/fail/unwind-action-terminate.rs
+++ b/src/tools/miri/tests/fail/unwind-action-terminate.rs
@@ -1,6 +1,4 @@
 //@error-in-other-file: aborted execution
-//@normalize-stderr-test: "\|.*::abort\(\).*" -> "| ABORT()"
-//@normalize-stderr-test: "\| +\^+" -> "| ^"
 //@normalize-stderr-test: "\n +[0-9]+:[^\n]+" -> ""
 //@normalize-stderr-test: "\n +at [^\n]+" -> ""
 extern "C" fn panic_abort() {
diff --git a/src/tools/miri/tests/fail/unwind-action-terminate.stderr b/src/tools/miri/tests/fail/unwind-action-terminate.stderr
index 1c0f8cb7ec6..cf41c88ce37 100644
--- a/src/tools/miri/tests/fail/unwind-action-terminate.stderr
+++ b/src/tools/miri/tests/fail/unwind-action-terminate.stderr
@@ -9,13 +9,12 @@ panic in a function that cannot unwind
 stack backtrace:
 thread caused non-unwinding panic. aborting.
 error: abnormal termination: the program aborted execution
-  --> RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC
+  --> RUSTLIB/std/src/panicking.rs:LL:CC
    |
-LL | ABORT()
-   | ^ abnormal termination occurred here
+LL |         crate::process::abort();
+   |         ^^^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here
    |
    = note: BACKTRACE:
-   = note: inside `std::sys::pal::PLATFORM::abort_internal` at RUSTLIB/std/src/sys/pal/PLATFORM/mod.rs:LL:CC
    = note: inside `std::panicking::panic_with_hook` at RUSTLIB/std/src/panicking.rs:LL:CC
    = note: inside closure at RUSTLIB/std/src/panicking.rs:LL:CC
    = note: inside `std::sys::backtrace::__rust_end_short_backtrace::<{closure@std::panicking::panic_handler::{closure#0}}, !>` at RUSTLIB/std/src/sys/backtrace.rs:LL:CC
@@ -33,7 +32,7 @@ note: inside `main`
   --> tests/fail/unwind-action-terminate.rs:LL:CC
    |
 LL |     panic_abort();
-   | ^
+   |     ^^^^^^^^^^^^^
 
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
diff --git a/src/tools/miri/tests/fail/validity/dangling_ref1.rs b/src/tools/miri/tests/fail/validity/dangling_ref1.rs
index fc3a9f34463..57ba1117e76 100644
--- a/src/tools/miri/tests/fail/validity/dangling_ref1.rs
+++ b/src/tools/miri/tests/fail/validity/dangling_ref1.rs
@@ -1,5 +1,8 @@
 // Make sure we catch this even without Stacked Borrows
 //@compile-flags: -Zmiri-disable-stacked-borrows
+
+#![allow(integer_to_ptr_transmutes)]
+
 use std::mem;
 
 fn main() {
diff --git a/src/tools/miri/tests/fail/validity/nonzero.stderr b/src/tools/miri/tests/fail/validity/nonzero.stderr
index 0c3a35d6b9f..7be3ef46639 100644
--- a/src/tools/miri/tests/fail/validity/nonzero.stderr
+++ b/src/tools/miri/tests/fail/validity/nonzero.stderr
@@ -2,7 +2,7 @@ error: Undefined Behavior: constructing invalid value: encountered 0, but expect
   --> tests/fail/validity/nonzero.rs:LL:CC
    |
 LL |     let _x = Some(unsafe { NonZero(0) });
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
+   |                            ^^^^^^^^^^ Undefined Behavior occurred here
    |
    = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
    = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
diff --git a/src/tools/miri/tests/genmc/fail/data_race/mpu2_rels_rlx.rs b/src/tools/miri/tests/genmc/fail/data_race/mpu2_rels_rlx.rs
new file mode 100644
index 00000000000..32954a643b3
--- /dev/null
+++ b/src/tools/miri/tests/genmc/fail/data_race/mpu2_rels_rlx.rs
@@ -0,0 +1,45 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+
+// Translated from GenMC's test `wrong/racy/MPU2+rels+rlx`.
+// Test if Miri with GenMC can detect the data race on `X`.
+// The data race only occurs if thread 1 finishes, then threads 3 and 4 run, then thread 2.
+//
+// This data race is hard to detect for Miri without GenMC, requiring -Zmiri-many-seeds=0..1024 at the time this test was created.
+
+// FIXME(genmc): once Miri-GenMC error reporting is improved, ensure that it correctly points to the two spans involved in the data race.
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+
+use std::sync::atomic::AtomicUsize;
+use std::sync::atomic::Ordering::*;
+
+use genmc::spawn_pthread_closure;
+static mut X: u64 = 0;
+static Y: AtomicUsize = AtomicUsize::new(0);
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    unsafe {
+        let _t1 = spawn_pthread_closure(|| {
+            X = 1;
+            Y.store(0, Release);
+            Y.store(1, Relaxed);
+        });
+        let _t2 = spawn_pthread_closure(|| {
+            if Y.load(Relaxed) > 2 {
+                X = 2; //~ ERROR: Undefined Behavior: Non-atomic race
+            }
+        });
+        let _t3 = spawn_pthread_closure(|| {
+            let _ = Y.compare_exchange(2, 3, Relaxed, Relaxed);
+        });
+
+        let _t4 = spawn_pthread_closure(|| {
+            let _ = Y.compare_exchange(1, 2, Relaxed, Relaxed);
+        });
+    }
+    0
+}
diff --git a/src/tools/miri/tests/genmc/fail/data_race/mpu2_rels_rlx.stderr b/src/tools/miri/tests/genmc/fail/data_race/mpu2_rels_rlx.stderr
new file mode 100644
index 00000000000..1ffb55f22e6
--- /dev/null
+++ b/src/tools/miri/tests/genmc/fail/data_race/mpu2_rels_rlx.stderr
@@ -0,0 +1,22 @@
+Running GenMC Verification...
+error: Undefined Behavior: Non-atomic race
+  --> tests/genmc/fail/data_race/mpu2_rels_rlx.rs:LL:CC
+   |
+LL |                 X = 2;
+   |                 ^^^^^ Undefined Behavior occurred here
+   |
+   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
+   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+   = note: BACKTRACE on thread `unnamed-ID`:
+   = note: inside closure at tests/genmc/fail/data_race/mpu2_rels_rlx.rs:LL:CC
+   = note: inside `<std::boxed::Box<{closure@tests/genmc/fail/data_race/mpu2_rels_rlx.rs:LL:CC}> as std::ops::FnOnce<()>>::call_once` at RUSTLIB/alloc/src/boxed.rs:LL:CC
+note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/fail/data_race/mpu2_rels_rlx.rs:LL:CC}>`
+  --> tests/genmc/fail/data_race/../../../utils/genmc.rs:LL:CC
+   |
+LL |         f();
+   |         ^^^
+
+note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rel_rlx.stderr b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rel_rlx.stderr
new file mode 100644
index 00000000000..b0037c211c6
--- /dev/null
+++ b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rel_rlx.stderr
@@ -0,0 +1,22 @@
+Running GenMC Verification...
+error: Undefined Behavior: Non-atomic race
+  --> tests/genmc/fail/data_race/weak_orderings.rs:LL:CC
+   |
+LL |                 X = 2;
+   |                 ^^^^^ Undefined Behavior occurred here
+   |
+   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
+   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+   = note: BACKTRACE on thread `unnamed-ID`:
+   = note: inside closure at tests/genmc/fail/data_race/weak_orderings.rs:LL:CC
+   = note: inside `<std::boxed::Box<{closure@tests/genmc/fail/data_race/weak_orderings.rs:LL:CC}> as std::ops::FnOnce<()>>::call_once` at RUSTLIB/alloc/src/boxed.rs:LL:CC
+note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/fail/data_race/weak_orderings.rs:LL:CC}>`
+  --> tests/genmc/fail/data_race/../../../utils/genmc.rs:LL:CC
+   |
+LL |         f();
+   |         ^^^
+
+note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_acq.stderr b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_acq.stderr
new file mode 100644
index 00000000000..b0037c211c6
--- /dev/null
+++ b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_acq.stderr
@@ -0,0 +1,22 @@
+Running GenMC Verification...
+error: Undefined Behavior: Non-atomic race
+  --> tests/genmc/fail/data_race/weak_orderings.rs:LL:CC
+   |
+LL |                 X = 2;
+   |                 ^^^^^ Undefined Behavior occurred here
+   |
+   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
+   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+   = note: BACKTRACE on thread `unnamed-ID`:
+   = note: inside closure at tests/genmc/fail/data_race/weak_orderings.rs:LL:CC
+   = note: inside `<std::boxed::Box<{closure@tests/genmc/fail/data_race/weak_orderings.rs:LL:CC}> as std::ops::FnOnce<()>>::call_once` at RUSTLIB/alloc/src/boxed.rs:LL:CC
+note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/fail/data_race/weak_orderings.rs:LL:CC}>`
+  --> tests/genmc/fail/data_race/../../../utils/genmc.rs:LL:CC
+   |
+LL |         f();
+   |         ^^^
+
+note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_rlx.stderr b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_rlx.stderr
new file mode 100644
index 00000000000..b0037c211c6
--- /dev/null
+++ b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rlx_rlx.stderr
@@ -0,0 +1,22 @@
+Running GenMC Verification...
+error: Undefined Behavior: Non-atomic race
+  --> tests/genmc/fail/data_race/weak_orderings.rs:LL:CC
+   |
+LL |                 X = 2;
+   |                 ^^^^^ Undefined Behavior occurred here
+   |
+   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
+   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+   = note: BACKTRACE on thread `unnamed-ID`:
+   = note: inside closure at tests/genmc/fail/data_race/weak_orderings.rs:LL:CC
+   = note: inside `<std::boxed::Box<{closure@tests/genmc/fail/data_race/weak_orderings.rs:LL:CC}> as std::ops::FnOnce<()>>::call_once` at RUSTLIB/alloc/src/boxed.rs:LL:CC
+note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/fail/data_race/weak_orderings.rs:LL:CC}>`
+  --> tests/genmc/fail/data_race/../../../utils/genmc.rs:LL:CC
+   |
+LL |         f();
+   |         ^^^
+
+note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rs b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rs
new file mode 100644
index 00000000000..1568a302f85
--- /dev/null
+++ b/src/tools/miri/tests/genmc/fail/data_race/weak_orderings.rs
@@ -0,0 +1,40 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+//@revisions: rlx_rlx rlx_acq rel_rlx
+
+// Translated from GenMC's test `wrong/racy/MP+rel+rlx`, `MP+rlx+acq` and `MP+rlx+rlx`.
+// Test if Miri with GenMC can detect the data race on `X`.
+// Relaxed orderings on an atomic store-load pair should not synchronize the non-atomic write to X, leading to a data race.
+
+// FIXME(genmc): once Miri-GenMC error reporting is improved, ensure that it correctly points to the two spans involved in the data race.
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+
+use std::sync::atomic::AtomicUsize;
+use std::sync::atomic::Ordering::{self, *};
+
+use genmc::spawn_pthread_closure;
+
+static mut X: u64 = 0;
+static Y: AtomicUsize = AtomicUsize::new(0);
+
+const STORE_ORD: Ordering = if cfg!(rel_rlx) { Release } else { Relaxed };
+const LOAD_ORD: Ordering = if cfg!(rlx_acq) { Acquire } else { Relaxed };
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    unsafe {
+        spawn_pthread_closure(|| {
+            X = 1;
+            Y.store(1, STORE_ORD);
+        });
+        spawn_pthread_closure(|| {
+            if Y.load(LOAD_ORD) != 0 {
+                X = 2; //~ ERROR: Undefined Behavior: Non-atomic race
+            }
+        });
+    }
+    0
+}
diff --git a/src/tools/miri/tests/genmc/fail/loom/buggy_inc.rs b/src/tools/miri/tests/genmc/fail/loom/buggy_inc.rs
new file mode 100644
index 00000000000..508eae756f3
--- /dev/null
+++ b/src/tools/miri/tests/genmc/fail/loom/buggy_inc.rs
@@ -0,0 +1,66 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+
+// SPDX-License-Identifier: MIT
+// SPDX-FileCopyrightText: Copyright (c) 2019 Carl Lerche
+
+// This is the test `checks_fail` from loom/test/smoke.rs adapted for Miri-GenMC.
+// https://github.com/tokio-rs/loom/blob/dbf32b04bae821c64be44405a0bb72ca08741558/tests/smoke.rs
+
+// This test checks that an incorrect implementation of an incrementing counter is detected.
+// The counter behaves wrong if two threads try to increment at the same time (increments can be lost).
+
+#![no_main]
+
+#[cfg(not(any(non_genmc_std, genmc_std)))]
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+
+use std::sync::atomic::AtomicUsize;
+use std::sync::atomic::Ordering::*;
+
+use crate::genmc::*;
+
+struct BuggyInc {
+    num: AtomicUsize,
+}
+
+impl BuggyInc {
+    const fn new() -> BuggyInc {
+        BuggyInc { num: AtomicUsize::new(0) }
+    }
+
+    fn inc(&self) {
+        // The bug is here:
+        // Another thread can increment `self.num` between the next two lines,
+        // which is then overridden by this thread.
+        let curr = self.num.load(Acquire);
+        self.num.store(curr + 1, Release);
+    }
+}
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    unsafe {
+        static BUGGY_INC: BuggyInc = BuggyInc::new();
+        // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses.
+        BUGGY_INC.num.store(0, Relaxed);
+
+        let ids = [
+            spawn_pthread_closure(|| {
+                BUGGY_INC.inc();
+            }),
+            spawn_pthread_closure(|| {
+                BUGGY_INC.inc();
+            }),
+        ];
+        // Join so we can read the final values.
+        join_pthreads(ids);
+
+        // We check that we can detect the incorrect counter implementation:
+        if 2 != BUGGY_INC.num.load(Relaxed) {
+            std::process::abort(); //~ ERROR: abnormal termination
+        }
+
+        0
+    }
+}
diff --git a/src/tools/miri/tests/genmc/fail/loom/buggy_inc.stderr b/src/tools/miri/tests/genmc/fail/loom/buggy_inc.stderr
new file mode 100644
index 00000000000..290cdf90a08
--- /dev/null
+++ b/src/tools/miri/tests/genmc/fail/loom/buggy_inc.stderr
@@ -0,0 +1,16 @@
+Running GenMC Verification...
+error: abnormal termination: the program aborted execution
+  --> tests/genmc/fail/loom/buggy_inc.rs:LL:CC
+   |
+LL |             std::process::abort();
+   |             ^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here
+   |
+   = note: BACKTRACE:
+   = note: inside `miri_start` at tests/genmc/fail/loom/buggy_inc.rs:LL:CC
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/genmc/fail/loom/store_buffering.genmc.stderr b/src/tools/miri/tests/genmc/fail/loom/store_buffering.genmc.stderr
new file mode 100644
index 00000000000..7742790e333
--- /dev/null
+++ b/src/tools/miri/tests/genmc/fail/loom/store_buffering.genmc.stderr
@@ -0,0 +1,16 @@
+Running GenMC Verification...
+error: abnormal termination: the program aborted execution
+  --> tests/genmc/fail/loom/store_buffering.rs:LL:CC
+   |
+LL |             std::process::abort();
+   |             ^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here
+   |
+   = note: BACKTRACE:
+   = note: inside `miri_start` at tests/genmc/fail/loom/store_buffering.rs:LL:CC
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/genmc/fail/loom/store_buffering.non_genmc.stderr b/src/tools/miri/tests/genmc/fail/loom/store_buffering.non_genmc.stderr
new file mode 100644
index 00000000000..a6c3ed7055e
--- /dev/null
+++ b/src/tools/miri/tests/genmc/fail/loom/store_buffering.non_genmc.stderr
@@ -0,0 +1,13 @@
+error: abnormal termination: the program aborted execution
+  --> tests/genmc/fail/loom/store_buffering.rs:LL:CC
+   |
+LL |             std::process::abort();
+   |             ^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here
+   |
+   = note: BACKTRACE:
+   = note: inside `miri_start` at tests/genmc/fail/loom/store_buffering.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/genmc/fail/loom/store_buffering.rs b/src/tools/miri/tests/genmc/fail/loom/store_buffering.rs
new file mode 100644
index 00000000000..4955dfd8a77
--- /dev/null
+++ b/src/tools/miri/tests/genmc/fail/loom/store_buffering.rs
@@ -0,0 +1,57 @@
+//@ revisions: non_genmc genmc
+//@[genmc] compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+
+// SPDX-License-Identifier: MIT
+// SPDX-FileCopyrightText: Copyright (c) 2019 Carl Lerche
+
+// This is the test `store_buffering` from `loom/test/litmus.rs`, adapted for Miri-GenMC.
+// https://github.com/tokio-rs/loom/blob/dbf32b04bae821c64be44405a0bb72ca08741558/tests/litmus.rs
+
+// This test shows the comparison between running Miri with or without GenMC.
+// Without GenMC, Miri requires multiple iterations of the loop to detect the error.
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+
+use std::sync::atomic::AtomicUsize;
+use std::sync::atomic::Ordering::*;
+
+use crate::genmc::*;
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    // For normal Miri, we need multiple repetitions, but GenMC should find the bug with only 1.
+    const REPS: usize = if cfg!(non_genmc) { 128 } else { 1 };
+    for _ in 0..REPS {
+        // New atomics every iterations, so they don't influence each other.
+        let x = AtomicUsize::new(0);
+        let y = AtomicUsize::new(0);
+
+        // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses.
+        x.store(0, Relaxed);
+        y.store(0, Relaxed);
+
+        let mut a: usize = 1234;
+        let mut b: usize = 1234;
+        unsafe {
+            let ids = [
+                spawn_pthread_closure(|| {
+                    x.store(1, Relaxed);
+                    a = y.load(Relaxed)
+                }),
+                spawn_pthread_closure(|| {
+                    y.store(1, Relaxed);
+                    b = x.load(Relaxed)
+                }),
+            ];
+            join_pthreads(ids);
+        }
+        if (a, b) == (0, 0) {
+            std::process::abort(); //~ ERROR: abnormal termination
+        }
+    }
+
+    0
+}
diff --git a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.relaxed4.stderr b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.relaxed4.stderr
new file mode 100644
index 00000000000..80ac7206644
--- /dev/null
+++ b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.relaxed4.stderr
@@ -0,0 +1,16 @@
+Running GenMC Verification...
+error: abnormal termination: the program aborted execution
+  --> tests/genmc/fail/simple/2w2w_weak.rs:LL:CC
+   |
+LL |             std::process::abort();
+   |             ^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here
+   |
+   = note: BACKTRACE:
+   = note: inside `miri_start` at tests/genmc/fail/simple/2w2w_weak.rs:LL:CC
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.release4.stderr b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.release4.stderr
new file mode 100644
index 00000000000..80ac7206644
--- /dev/null
+++ b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.release4.stderr
@@ -0,0 +1,16 @@
+Running GenMC Verification...
+error: abnormal termination: the program aborted execution
+  --> tests/genmc/fail/simple/2w2w_weak.rs:LL:CC
+   |
+LL |             std::process::abort();
+   |             ^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here
+   |
+   = note: BACKTRACE:
+   = note: inside `miri_start` at tests/genmc/fail/simple/2w2w_weak.rs:LL:CC
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.rs b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.rs
new file mode 100644
index 00000000000..baf3584966e
--- /dev/null
+++ b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.rs
@@ -0,0 +1,72 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+//@revisions: sc3_rel1 release4 relaxed4
+
+// The pass tests "2w2w_3sc_1rel.rs", "2w2w_4rel" and "2w2w_4sc" and the fail test "2w2w_weak.rs" are related.
+//
+// This test has multiple variants using different memory orderings.
+// When using any combination of orderings except using all 4 `SeqCst`, the memory model allows the program to result in (X, Y) == (1, 1).
+// The "pass" variants only check that we get the expected number of executions (3 for all SC, 4 otherwise),
+// and a valid outcome every execution, but do not check that we get all allowed results.
+// This "fail" variant ensures we can explore the execution resulting in (1, 1), with an incorrect assumption that the result (1, 1) is impossible.
+//
+// Miri without GenMC is unable to produce this program execution and thus detect the incorrect assumption, even with `-Zmiri-many-seeds`.
+//
+// To get good coverage, we test the combination with the strongest orderings allowing this result (3 `SeqCst`, 1 `Release`),
+// the weakest orderings (4 `Relaxed`), and one in between (4 `Release`).
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+
+use std::sync::atomic::AtomicU64;
+use std::sync::atomic::Ordering::{self, *};
+
+use crate::genmc::{join_pthreads, spawn_pthread_closure};
+
+static X: AtomicU64 = AtomicU64::new(0);
+static Y: AtomicU64 = AtomicU64::new(0);
+
+// Strongest orderings allowing result (1, 1).
+#[cfg(seqcst_rel)]
+const STORE_ORD_3: Ordering = SeqCst;
+#[cfg(seqcst_rel)]
+const STORE_ORD_1: Ordering = Release;
+
+// 4 * `Release`.
+#[cfg(acqrel)]
+const STORE_ORD_3: Ordering = Release;
+#[cfg(acqrel)]
+const STORE_ORD_1: Ordering = Release;
+
+// Weakest orderings (4 * `Relaxed`).
+#[cfg(not(any(acqrel, seqcst_rel)))]
+const STORE_ORD_3: Ordering = Relaxed;
+#[cfg(not(any(acqrel, seqcst_rel)))]
+const STORE_ORD_1: Ordering = Relaxed;
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    unsafe {
+        let ids = [
+            spawn_pthread_closure(|| {
+                X.store(1, STORE_ORD_3);
+                Y.store(2, STORE_ORD_3);
+            }),
+            spawn_pthread_closure(|| {
+                Y.store(1, STORE_ORD_1);
+                X.store(2, STORE_ORD_3);
+            }),
+        ];
+        // Join so we can read the final values.
+        join_pthreads(ids);
+
+        // We incorrectly assume that the result (1, 1) as unreachable.
+        let result = (X.load(Relaxed), Y.load(Relaxed));
+        if result == (1, 1) {
+            std::process::abort(); //~ ERROR: abnormal termination
+        }
+
+        0
+    }
+}
diff --git a/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.sc3_rel1.stderr b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.sc3_rel1.stderr
new file mode 100644
index 00000000000..80ac7206644
--- /dev/null
+++ b/src/tools/miri/tests/genmc/fail/simple/2w2w_weak.sc3_rel1.stderr
@@ -0,0 +1,16 @@
+Running GenMC Verification...
+error: abnormal termination: the program aborted execution
+  --> tests/genmc/fail/simple/2w2w_weak.rs:LL:CC
+   |
+LL |             std::process::abort();
+   |             ^^^^^^^^^^^^^^^^^^^^^ abnormal termination occurred here
+   |
+   = note: BACKTRACE:
+   = note: inside `miri_start` at tests/genmc/fail/simple/2w2w_weak.rs:LL:CC
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+note: add `-Zmiri-genmc-print-genmc-output` to MIRIFLAGS to see the detailed GenMC error report
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.rs b/src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.rs
new file mode 100644
index 00000000000..8a77d54a64f
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.rs
@@ -0,0 +1,70 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -Zmiri-ignore-leaks
+
+// Adapted from: `impl LazyKey`, `fn lazy_init`: rust/library/std/src/sys/thread_local/key/racy.rs
+// Two threads race to initialize a key, which is just an index into an array in this test.
+// The (Acquire, Release) orderings on the compare_exchange prevent any data races for reading from `VALUES[key]`.
+
+// FIXME(genmc): GenMC does not model the failure ordering of compare_exchange currently.
+// Miri thus upgrades the success ordering to prevent showing any false data races in cases like this one.
+// Once GenMC supports the failure ordering, this test should work without the upgrading.
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+
+use std::sync::atomic::AtomicUsize;
+use std::sync::atomic::Ordering::*;
+
+use crate::genmc::*;
+
+const KEY_SENTVAL: usize = usize::MAX;
+
+static KEY: AtomicUsize = AtomicUsize::new(KEY_SENTVAL);
+
+static mut VALUES: [usize; 2] = [0, 0];
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses.
+    KEY.store(KEY_SENTVAL, Relaxed);
+
+    unsafe {
+        let mut a = 0;
+        let mut b = 0;
+        let ids = [
+            spawn_pthread_closure(|| {
+                VALUES[0] = 42;
+                let key = get_or_init(0);
+                if key > 2 {
+                    std::process::abort();
+                }
+                a = VALUES[key];
+            }),
+            spawn_pthread_closure(|| {
+                VALUES[1] = 1234;
+                let key = get_or_init(1);
+                if key > 2 {
+                    std::process::abort();
+                }
+                b = VALUES[key];
+            }),
+        ];
+        join_pthreads(ids);
+        if a != b {
+            std::process::abort();
+        }
+    }
+    0
+}
+
+fn get_or_init(key: usize) -> usize {
+    match KEY.compare_exchange(KEY_SENTVAL, key, Release, Acquire) {
+        // The CAS succeeded, so we've created the actual key.
+        Ok(_) => key,
+        // If someone beat us to the punch, use their key instead.
+        // The `Acquire` failure ordering means we synchronized with the `Release` compare_exchange in the thread that wrote the other key.
+        // We can thus read from `VALUES[other_key]` without a data race.
+        Err(other_key) => other_key,
+    }
+}
diff --git a/src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.stderr b/src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.stderr
new file mode 100644
index 00000000000..31438f9352f
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.stderr
@@ -0,0 +1,22 @@
+Running GenMC Verification...
+warning: GenMC currently does not model the failure ordering for `compare_exchange`. Success ordering 'Release' was upgraded to 'AcqRel' to match failure ordering 'Acquire'. Miri with GenMC might miss bugs related to this memory access.
+  --> tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.rs:LL:CC
+   |
+LL |     match KEY.compare_exchange(KEY_SENTVAL, key, Release, Acquire) {
+   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GenMC might miss possible behaviors of this code
+   |
+   = note: BACKTRACE on thread `unnamed-ID`:
+   = note: inside `get_or_init` at tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.rs:LL:CC
+note: inside closure
+  --> tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.rs:LL:CC
+   |
+LL |                 let key = get_or_init(0);
+   |                           ^^^^^^^^^^^^^^
+   = note: inside `<std::boxed::Box<{closure@tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.rs:LL:CC}> as std::ops::FnOnce<()>>::call_once` at RUSTLIB/alloc/src/boxed.rs:LL:CC
+note: inside `genmc::spawn_pthread_closure::thread_func::<{closure@tests/genmc/pass/atomics/cas_failure_ord_racy_key_init.rs:LL:CC}>`
+  --> tests/genmc/pass/atomics/../../../utils/genmc.rs:LL:CC
+   |
+LL |         f();
+   |         ^^^
+
+Verification complete with 2 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/atomics/cas_simple.rs b/src/tools/miri/tests/genmc/pass/atomics/cas_simple.rs
new file mode 100644
index 00000000000..e32c7cdf80c
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/atomics/cas_simple.rs
@@ -0,0 +1,34 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+
+// Test the basic functionality of compare_exchange.
+
+#![no_main]
+
+use std::sync::atomic::AtomicUsize;
+use std::sync::atomic::Ordering::*;
+
+static VALUE: AtomicUsize = AtomicUsize::new(0);
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    VALUE.store(1, SeqCst);
+
+    // Expect success:
+    if VALUE.compare_exchange(1, 2, SeqCst, SeqCst) != Ok(1) {
+        std::process::abort();
+    }
+    // New value should be written:
+    if 2 != VALUE.load(SeqCst) {
+        std::process::abort()
+    }
+
+    // Expect failure:
+    if VALUE.compare_exchange(1234, 42, SeqCst, SeqCst) != Err(2) {
+        std::process::abort();
+    }
+    // Value should be unchanged:
+    if 2 != VALUE.load(SeqCst) {
+        std::process::abort()
+    }
+    0
+}
diff --git a/src/tools/miri/tests/genmc/pass/atomics/cas_simple.stderr b/src/tools/miri/tests/genmc/pass/atomics/cas_simple.stderr
new file mode 100644
index 00000000000..7867be2dbe8
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/atomics/cas_simple.stderr
@@ -0,0 +1,2 @@
+Running GenMC Verification...
+Verification complete with 1 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/atomics/rmw_ops.rs b/src/tools/miri/tests/genmc/pass/atomics/rmw_ops.rs
new file mode 100644
index 00000000000..f48466e8ce1
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/atomics/rmw_ops.rs
@@ -0,0 +1,91 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+
+// This test check for correct handling of atomic read-modify-write operations for all integer sizes.
+// Atomic max and min should return the previous value, and store the result in the atomic.
+// Atomic addition and subtraction should have wrapping semantics.
+// `and`, `nand`, `or`, `xor` should behave like their non-atomic counterparts.
+
+// FIXME(genmc): add 128 bit atomics for platforms that support it, once GenMC gets 128 bit atomic support
+
+#![no_main]
+
+use std::sync::atomic::*;
+
+const ORD: Ordering = Ordering::SeqCst;
+
+fn assert_eq<T: Eq>(x: T, y: T) {
+    if x != y {
+        std::process::abort();
+    }
+}
+
+macro_rules! test_rmw_edge_cases {
+    ($int:ty, $atomic:ty) => {{
+        let x = <$atomic>::new(123);
+        // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses.
+        x.store(123, ORD);
+
+        // MAX, ADD
+        assert_eq(123, x.fetch_max(0, ORD)); // `max` keeps existing value
+        assert_eq(123, x.fetch_max(<$int>::MAX, ORD)); // `max` stores the new value
+        assert_eq(<$int>::MAX, x.fetch_add(10, ORD)); // `fetch_add` should be wrapping
+        assert_eq(<$int>::MAX.wrapping_add(10), x.load(ORD));
+
+        // MIN, SUB
+        x.store(42, ORD);
+        assert_eq(42, x.fetch_min(<$int>::MAX, ORD)); // `min` keeps existing value
+        assert_eq(42, x.fetch_min(<$int>::MIN, ORD)); // `min` stores the new value
+        assert_eq(<$int>::MIN, x.fetch_sub(10, ORD)); // `fetch_sub` should be wrapping
+        assert_eq(<$int>::MIN.wrapping_sub(10), x.load(ORD));
+
+        // Small enough pattern to work for all integer sizes.
+        let pattern = 0b01010101;
+
+        // AND
+        x.store(!0, ORD);
+        assert_eq(!0, x.fetch_and(pattern, ORD));
+        assert_eq(!0 & pattern, x.load(ORD));
+
+        // NAND
+        x.store(!0, ORD);
+        assert_eq(!0, x.fetch_nand(pattern, ORD));
+        assert_eq(!(!0 & pattern), x.load(ORD));
+
+        // OR
+        x.store(!0, ORD);
+        assert_eq(!0, x.fetch_or(pattern, ORD));
+        assert_eq(!0 | pattern, x.load(ORD));
+
+        // XOR
+        x.store(!0, ORD);
+        assert_eq(!0, x.fetch_xor(pattern, ORD));
+        assert_eq(!0 ^ pattern, x.load(ORD));
+
+        // SWAP
+        x.store(!0, ORD);
+        assert_eq(!0, x.swap(pattern, ORD));
+        assert_eq(pattern, x.load(ORD));
+
+        // Check correct behavior of atomic min/max combined with overflowing add/sub.
+        x.store(10, ORD);
+        assert_eq(10, x.fetch_add(<$int>::MAX, ORD)); // definitely overflows, so new value of x is smaller than 10
+        assert_eq(<$int>::MAX.wrapping_add(10), x.fetch_max(10, ORD)); // new value of x should be 10
+        // assert_eq(10, x.load(ORD)); // FIXME(genmc,#4572): enable this check once GenMC correctly handles min/max truncation.
+    }};
+}
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    test_rmw_edge_cases!(u8, AtomicU8);
+    test_rmw_edge_cases!(u16, AtomicU16);
+    test_rmw_edge_cases!(u32, AtomicU32);
+    test_rmw_edge_cases!(u64, AtomicU64);
+    test_rmw_edge_cases!(usize, AtomicUsize);
+    test_rmw_edge_cases!(i8, AtomicI8);
+    test_rmw_edge_cases!(i16, AtomicI16);
+    test_rmw_edge_cases!(i32, AtomicI32);
+    test_rmw_edge_cases!(i64, AtomicI64);
+    test_rmw_edge_cases!(isize, AtomicIsize);
+
+    0
+}
diff --git a/src/tools/miri/tests/genmc/pass/atomics/rmw_ops.stderr b/src/tools/miri/tests/genmc/pass/atomics/rmw_ops.stderr
new file mode 100644
index 00000000000..7867be2dbe8
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/atomics/rmw_ops.stderr
@@ -0,0 +1,2 @@
+Running GenMC Verification...
+Verification complete with 1 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/litmus/2cowr.rs b/src/tools/miri/tests/genmc/pass/litmus/2cowr.rs
new file mode 100644
index 00000000000..d3fdb470ed3
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/2cowr.rs
@@ -0,0 +1,51 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+
+// Translated from GenMC's test "2CoWR".
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+
+use std::sync::atomic::AtomicU64;
+use std::sync::atomic::Ordering::*;
+
+use crate::genmc::*;
+
+static X: AtomicU64 = AtomicU64::new(0);
+static Y: AtomicU64 = AtomicU64::new(0);
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses.
+    X.store(0, Relaxed);
+    Y.store(0, Relaxed);
+
+    unsafe {
+        let mut a = 1234;
+        let mut b = 1234;
+        let ids = [
+            spawn_pthread_closure(|| {
+                a = Y.load(Acquire);
+            }),
+            spawn_pthread_closure(|| {
+                b = X.load(Acquire);
+            }),
+            spawn_pthread_closure(|| {
+                X.store(1, Release);
+            }),
+            spawn_pthread_closure(|| {
+                Y.store(1, Release);
+            }),
+        ];
+        // Join so we can read the final values.
+        join_pthreads(ids);
+
+        // Check that we don't get any unexpected values:
+        if !matches!((a, b), (0, 0) | (0, 1) | (1, 0) | (1, 1)) {
+            std::process::abort();
+        }
+
+        0
+    }
+}
diff --git a/src/tools/miri/tests/genmc/pass/litmus/2cowr.stderr b/src/tools/miri/tests/genmc/pass/litmus/2cowr.stderr
new file mode 100644
index 00000000000..a67635dee1b
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/2cowr.stderr
@@ -0,0 +1,2 @@
+Running GenMC Verification...
+Verification complete with 4 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_2sc_scf.rs b/src/tools/miri/tests/genmc/pass/litmus/2w2w_2sc_scf.rs
new file mode 100644
index 00000000000..3b3fca02285
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_2sc_scf.rs
@@ -0,0 +1,33 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+
+// Translated from GenMC's test "2+2W+2sc+scf".
+// It tests correct handling of SeqCst fences combined with relaxed accesses.
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+
+use std::sync::atomic::AtomicU64;
+use std::sync::atomic::Ordering::*;
+
+use crate::genmc::*;
+
+static X: AtomicU64 = AtomicU64::new(0);
+static Y: AtomicU64 = AtomicU64::new(0);
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    unsafe {
+        spawn_pthread_closure(|| {
+            X.store(1, SeqCst);
+            Y.store(2, SeqCst);
+        });
+        spawn_pthread_closure(|| {
+            Y.store(1, Relaxed);
+            std::sync::atomic::fence(SeqCst);
+            X.store(2, Relaxed);
+        });
+        0
+    }
+}
diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_2sc_scf.stderr b/src/tools/miri/tests/genmc/pass/litmus/2w2w_2sc_scf.stderr
new file mode 100644
index 00000000000..485142e945a
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_2sc_scf.stderr
@@ -0,0 +1,2 @@
+Running GenMC Verification...
+Verification complete with 3 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.release1.stderr b/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.release1.stderr
new file mode 100644
index 00000000000..a67635dee1b
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.release1.stderr
@@ -0,0 +1,2 @@
+Running GenMC Verification...
+Verification complete with 4 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.release2.stderr b/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.release2.stderr
new file mode 100644
index 00000000000..a67635dee1b
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.release2.stderr
@@ -0,0 +1,2 @@
+Running GenMC Verification...
+Verification complete with 4 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.rs b/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.rs
new file mode 100644
index 00000000000..22fe9524c37
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_3sc_1rel.rs
@@ -0,0 +1,54 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+//@revisions: release1 release2
+
+// Translated from GenMC's test "2+2W+3sc+rel1" and "2+2W+3sc+rel2" (two variants that swap which store is `Release`).
+//
+// The pass tests "2w2w_3sc_1rel.rs", "2w2w_4rel" and "2w2w_4sc" and the fail test "2w2w_weak.rs" are related.
+// Check "2w2w_weak.rs" for a more detailed description.
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+
+use std::sync::atomic::AtomicU64;
+use std::sync::atomic::Ordering::*;
+
+use crate::genmc::*;
+
+static X: AtomicU64 = AtomicU64::new(0);
+static Y: AtomicU64 = AtomicU64::new(0);
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    unsafe {
+        let ids = [
+            spawn_pthread_closure(|| {
+                X.store(1, SeqCst);
+                Y.store(2, SeqCst);
+            }),
+            // Variant 1: `Release` goes first.
+            #[cfg(release1)]
+            spawn_pthread_closure(|| {
+                Y.store(1, Release);
+                X.store(2, SeqCst);
+            }),
+            // Variant 2: `Release` goes second.
+            #[cfg(not(release1))]
+            spawn_pthread_closure(|| {
+                Y.store(1, SeqCst);
+                X.store(2, Release);
+            }),
+        ];
+        // Join so we can read the final values.
+        join_pthreads(ids);
+
+        // Check that we don't get any unexpected values:
+        let result = (X.load(Relaxed), Y.load(Relaxed));
+        if !matches!(result, (1, 2) | (1, 1) | (2, 2) | (2, 1)) {
+            std::process::abort();
+        }
+
+        0
+    }
+}
diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.rs b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.rs
new file mode 100644
index 00000000000..f47f5a11c5c
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.rs
@@ -0,0 +1,49 @@
+//@revisions: weak sc
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+//@[sc]compile-flags: -Zmiri-disable-weak-memory-emulation
+
+// Translated from GenMC's test "2+2W".
+//
+// The pass tests "2w2w_3sc_1rel.rs", "2w2w_4rel" and "2w2w_4sc" and the fail test "2w2w_weak.rs" are related.
+// Check "2w2w_weak.rs" for a more detailed description.
+//
+// The `sc` variant of this test checks that without weak memory emulation, only 3 instead of 4 executions are explored (like the `2w2w_4sc.rs` test).
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+
+use std::sync::atomic::AtomicU64;
+use std::sync::atomic::Ordering::*;
+
+use crate::genmc::*;
+
+static X: AtomicU64 = AtomicU64::new(0);
+static Y: AtomicU64 = AtomicU64::new(0);
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    unsafe {
+        let ids = [
+            spawn_pthread_closure(|| {
+                Y.store(1, Release);
+                X.store(2, Release);
+            }),
+            spawn_pthread_closure(|| {
+                X.store(1, Release);
+                Y.store(2, Release);
+            }),
+        ];
+        // Join so we can read the final values.
+        join_pthreads(ids);
+
+        // Check that we don't get any unexpected values:
+        let result = (X.load(Relaxed), Y.load(Relaxed));
+        if !matches!(result, (1, 2) | (1, 1) | (2, 2) | (2, 1)) {
+            std::process::abort();
+        }
+
+        0
+    }
+}
diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.sc.stderr b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.sc.stderr
new file mode 100644
index 00000000000..485142e945a
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.sc.stderr
@@ -0,0 +1,2 @@
+Running GenMC Verification...
+Verification complete with 3 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.weak.stderr b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.weak.stderr
new file mode 100644
index 00000000000..a67635dee1b
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4rel.weak.stderr
@@ -0,0 +1,2 @@
+Running GenMC Verification...
+Verification complete with 4 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_4sc.rs b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4sc.rs
new file mode 100644
index 00000000000..c5711ba04fc
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4sc.rs
@@ -0,0 +1,45 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+
+// Translated from GenMC's test "2+2W+4c".
+//
+// The pass tests "2w2w_3sc_1rel.rs", "2w2w_4rel" and "2w2w_4sc" and the fail test "2w2w_weak.rs" are related.
+// Check "2w2w_weak.rs" for a more detailed description.
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+
+use std::sync::atomic::AtomicU64;
+use std::sync::atomic::Ordering::*;
+
+use crate::genmc::*;
+
+static X: AtomicU64 = AtomicU64::new(0);
+static Y: AtomicU64 = AtomicU64::new(0);
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    unsafe {
+        let ids = [
+            spawn_pthread_closure(|| {
+                X.store(1, SeqCst);
+                Y.store(2, SeqCst);
+            }),
+            spawn_pthread_closure(|| {
+                Y.store(1, SeqCst);
+                X.store(2, SeqCst);
+            }),
+        ];
+        // Join so we can read the final values.
+        join_pthreads(ids);
+
+        // Check that we don't get any unexpected values:
+        let result = (X.load(Relaxed), Y.load(Relaxed));
+        if !matches!(result, (2, 1) | (2, 2) | (1, 2)) {
+            std::process::abort();
+        }
+
+        0
+    }
+}
diff --git a/src/tools/miri/tests/genmc/pass/litmus/2w2w_4sc.stderr b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4sc.stderr
new file mode 100644
index 00000000000..485142e945a
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/2w2w_4sc.stderr
@@ -0,0 +1,2 @@
+Running GenMC Verification...
+Verification complete with 3 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.rs b/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.rs
new file mode 100644
index 00000000000..1ec62ff2134
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.rs
@@ -0,0 +1,64 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+
+// Translated from GenMC's "litmus/IRIW-acq-sc" test.
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+
+use std::sync::atomic::AtomicU64;
+use std::sync::atomic::Ordering::*;
+
+use crate::genmc::*;
+
+static X: AtomicU64 = AtomicU64::new(0);
+static Y: AtomicU64 = AtomicU64::new(0);
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses.
+    X.store(0, Relaxed);
+    Y.store(0, Relaxed);
+
+    unsafe {
+        let mut a = 1234;
+        let mut b = 1234;
+        let mut c = 1234;
+        let ids = [
+            spawn_pthread_closure(|| {
+                X.store(1, SeqCst);
+            }),
+            spawn_pthread_closure(|| {
+                a = X.load(Acquire);
+                Y.load(SeqCst);
+            }),
+            spawn_pthread_closure(|| {
+                b = Y.load(Acquire);
+                c = X.load(SeqCst);
+            }),
+            spawn_pthread_closure(|| {
+                Y.store(1, SeqCst);
+            }),
+        ];
+        // Join so we can read the final values.
+        join_pthreads(ids);
+
+        // Check that we don't get any unexpected values:
+        if !matches!(
+            (a, b, c),
+            (0, 0, 0)
+                | (0, 0, 1)
+                | (0, 1, 0)
+                | (0, 1, 1)
+                | (1, 0, 0)
+                | (1, 0, 1)
+                | (1, 1, 0)
+                | (1, 1, 1)
+        ) {
+            std::process::abort();
+        }
+
+        0
+    }
+}
diff --git a/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.stderr b/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.stderr
new file mode 100644
index 00000000000..d6ec73a8c66
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/IRIW-acq-sc.stderr
@@ -0,0 +1,2 @@
+Running GenMC Verification...
+Verification complete with 16 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/litmus/IRIWish.rs b/src/tools/miri/tests/genmc/pass/litmus/IRIWish.rs
new file mode 100644
index 00000000000..c81573d59d1
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/IRIWish.rs
@@ -0,0 +1,64 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+
+// Translated from GenMC's "litmus/IRIWish" test.
+// This test prints the values read by the different threads to check that we get all the values we expect.
+
+// NOTE: the order of the lines in the output may change with changes to GenMC.
+// Before blessing the new output, ensure that only the order of lines in the output changed, and none of the outputs are missing.
+
+// NOTE: GenMC supports instruction caching and does not need replay completed threads.
+// This means that an identical test in GenMC may output fewer lines (disable instruction caching to see all results).
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+#[path = "../../../utils/mod.rs"]
+mod utils;
+
+use std::fmt::Write;
+use std::sync::atomic::AtomicU64;
+use std::sync::atomic::Ordering::*;
+
+use crate::genmc::*;
+use crate::utils::*;
+
+static X: AtomicU64 = AtomicU64::new(0);
+static Y: AtomicU64 = AtomicU64::new(0);
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses.
+    X.store(0, Relaxed);
+    Y.store(0, Relaxed);
+
+    unsafe {
+        let mut results = [1234; 5];
+        let ids = [
+            spawn_pthread_closure(|| {
+                X.store(1, Relaxed);
+            }),
+            spawn_pthread_closure(|| {
+                let r1 = X.load(Relaxed);
+                Y.store(r1, Release);
+                results[0] = r1;
+            }),
+            spawn_pthread_closure(|| {
+                results[1] = X.load(Relaxed);
+                std::sync::atomic::fence(AcqRel);
+                results[2] = Y.load(Relaxed);
+            }),
+            spawn_pthread_closure(|| {
+                results[3] = Y.load(Relaxed);
+                std::sync::atomic::fence(AcqRel);
+                results[4] = X.load(Relaxed);
+            }),
+        ];
+        join_pthreads(ids);
+
+        // Print the values to check that we get all of them:
+        writeln!(MiriStderr, "{results:?}").unwrap_or_else(|_| std::process::abort());
+
+        0
+    }
+}
diff --git a/src/tools/miri/tests/genmc/pass/litmus/IRIWish.stderr b/src/tools/miri/tests/genmc/pass/litmus/IRIWish.stderr
new file mode 100644
index 00000000000..7ea2dd50851
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/IRIWish.stderr
@@ -0,0 +1,30 @@
+Running GenMC Verification...
+[1, 1, 1, 1, 1]
+[1, 1, 1, 0, 1]
+[1, 1, 1, 0, 0]
+[1, 1, 0, 1, 1]
+[1, 1, 0, 0, 1]
+[1, 1, 0, 0, 0]
+[1, 0, 1, 1, 1]
+[1, 0, 1, 0, 1]
+[1, 0, 1, 0, 0]
+[1, 0, 0, 1, 1]
+[1, 0, 0, 0, 1]
+[1, 0, 0, 0, 0]
+[0, 1, 0, 0, 1]
+[0, 1, 0, 0, 0]
+[0, 1, 0, 0, 1]
+[0, 1, 0, 0, 0]
+[0, 1, 0, 0, 1]
+[0, 1, 0, 0, 0]
+[0, 1, 0, 0, 1]
+[0, 1, 0, 0, 0]
+[0, 0, 0, 0, 1]
+[0, 0, 0, 0, 0]
+[0, 0, 0, 0, 1]
+[0, 0, 0, 0, 0]
+[0, 0, 0, 0, 1]
+[0, 0, 0, 0, 0]
+[0, 0, 0, 0, 1]
+[0, 0, 0, 0, 0]
+Verification complete with 28 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/litmus/LB.rs b/src/tools/miri/tests/genmc/pass/litmus/LB.rs
new file mode 100644
index 00000000000..1cee3230b12
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/LB.rs
@@ -0,0 +1,47 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+
+// Translated from GenMC's "litmus/LB" test.
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+
+use std::sync::atomic::AtomicU64;
+use std::sync::atomic::Ordering::*;
+
+use crate::genmc::*;
+
+static X: AtomicU64 = AtomicU64::new(0);
+static Y: AtomicU64 = AtomicU64::new(0);
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses.
+    X.store(0, Relaxed);
+    Y.store(0, Relaxed);
+
+    unsafe {
+        let mut a = 1234;
+        let mut b = 1234;
+        let ids = [
+            spawn_pthread_closure(|| {
+                a = Y.load(Acquire);
+                X.store(2, Release);
+            }),
+            spawn_pthread_closure(|| {
+                b = X.load(Acquire);
+                Y.store(1, Release);
+            }),
+        ];
+        // Join so we can read the final values.
+        join_pthreads(ids);
+
+        // Check that we don't get any unexpected values:
+        if !matches!((a, b), (0, 0) | (0, 2) | (1, 0)) {
+            std::process::abort();
+        }
+
+        0
+    }
+}
diff --git a/src/tools/miri/tests/genmc/pass/litmus/LB.stderr b/src/tools/miri/tests/genmc/pass/litmus/LB.stderr
new file mode 100644
index 00000000000..485142e945a
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/LB.stderr
@@ -0,0 +1,2 @@
+Running GenMC Verification...
+Verification complete with 3 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/litmus/LB_incMPs.rs b/src/tools/miri/tests/genmc/pass/litmus/LB_incMPs.rs
new file mode 100644
index 00000000000..e43d92fc6c5
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/LB_incMPs.rs
@@ -0,0 +1,41 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+
+// Translated from GenMC's "litmus/LB+incMPs" test.
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+
+use std::sync::atomic::AtomicU64;
+use std::sync::atomic::Ordering::*;
+
+use crate::genmc::*;
+
+static X: AtomicU64 = AtomicU64::new(0);
+static Y: AtomicU64 = AtomicU64::new(0);
+static W: AtomicU64 = AtomicU64::new(0);
+static Z: AtomicU64 = AtomicU64::new(0);
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    unsafe {
+        spawn_pthread_closure(|| {
+            X.load(Acquire);
+            Z.fetch_add(1, AcqRel);
+        });
+        spawn_pthread_closure(|| {
+            Z.fetch_add(1, AcqRel);
+            Y.store(1, Release);
+        });
+        spawn_pthread_closure(|| {
+            Y.load(Acquire);
+            W.fetch_add(1, AcqRel);
+        });
+        spawn_pthread_closure(|| {
+            W.fetch_add(1, AcqRel);
+            X.store(1, Release);
+        });
+        0
+    }
+}
diff --git a/src/tools/miri/tests/genmc/pass/litmus/LB_incMPs.stderr b/src/tools/miri/tests/genmc/pass/litmus/LB_incMPs.stderr
new file mode 100644
index 00000000000..e414cd5e064
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/LB_incMPs.stderr
@@ -0,0 +1,2 @@
+Running GenMC Verification...
+Verification complete with 15 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/litmus/MP.rs b/src/tools/miri/tests/genmc/pass/litmus/MP.rs
new file mode 100644
index 00000000000..e245cdd15ee
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/MP.rs
@@ -0,0 +1,47 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+
+// Translated from GenMC's "litmus/MP" test.
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+
+use std::sync::atomic::AtomicU64;
+use std::sync::atomic::Ordering::*;
+
+use crate::genmc::*;
+
+static X: AtomicU64 = AtomicU64::new(0);
+static Y: AtomicU64 = AtomicU64::new(0);
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses.
+    X.store(0, Relaxed);
+    Y.store(0, Relaxed);
+
+    unsafe {
+        let mut a = 1234;
+        let mut b = 1234;
+        let ids = [
+            spawn_pthread_closure(|| {
+                X.store(1, Release);
+                Y.store(1, Release);
+            }),
+            spawn_pthread_closure(|| {
+                a = Y.load(Acquire);
+                b = X.load(Acquire);
+            }),
+        ];
+        // Join so we can read the final values.
+        join_pthreads(ids);
+
+        // Check that we don't get any unexpected values:
+        if !matches!((a, b), (0, 0) | (0, 1) | (1, 1)) {
+            std::process::abort();
+        }
+
+        0
+    }
+}
diff --git a/src/tools/miri/tests/genmc/pass/litmus/MP.stderr b/src/tools/miri/tests/genmc/pass/litmus/MP.stderr
new file mode 100644
index 00000000000..485142e945a
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/MP.stderr
@@ -0,0 +1,2 @@
+Running GenMC Verification...
+Verification complete with 3 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.rs b/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.rs
new file mode 100644
index 00000000000..9bb156a9997
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.rs
@@ -0,0 +1,67 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+
+// Translated from GenMC's "litmus/MPU2+rels+acqf" test.
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+#[path = "../../../utils/mod.rs"]
+mod utils;
+
+use std::fmt::Write;
+use std::sync::atomic::AtomicU64;
+use std::sync::atomic::Ordering::*;
+
+use crate::genmc::*;
+use crate::utils::*;
+
+// Note: the GenMC equivalent of this test (genmc/tests/correct/litmus/MPU2+rels+acqf/mpu2+rels+acqf.c) uses non-atomic accesses for `X` with disabled race detection.
+static X: AtomicU64 = AtomicU64::new(0);
+static Y: AtomicU64 = AtomicU64::new(0);
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses.
+    X.store(0, Relaxed);
+    Y.store(0, Relaxed);
+
+    unsafe {
+        let mut a = Ok(1234);
+        let mut b = Ok(1234);
+        let mut c = 1234;
+        let ids = [
+            spawn_pthread_closure(|| {
+                X.store(1, Relaxed);
+
+                Y.store(0, Release);
+                Y.store(1, Relaxed);
+            }),
+            spawn_pthread_closure(|| {
+                a = Y.compare_exchange(2, 3, Relaxed, Relaxed);
+            }),
+            spawn_pthread_closure(|| {
+                b = Y.compare_exchange(1, 2, Relaxed, Relaxed);
+            }),
+            spawn_pthread_closure(|| {
+                c = Y.load(Acquire);
+                if c > 2 {
+                    std::sync::atomic::fence(Acquire);
+                    X.store(2, Relaxed);
+                }
+            }),
+        ];
+        join_pthreads(ids);
+
+        // Print the values to check that we get all of them:
+        writeln!(
+            MiriStderr,
+            "X={}, Y={}, a={a:?}, b={b:?}, c={c}",
+            X.load(Relaxed),
+            Y.load(Relaxed)
+        )
+        .unwrap_or_else(|_| std::process::abort());
+
+        0
+    }
+}
diff --git a/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.stderr b/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.stderr
new file mode 100644
index 00000000000..29b59ce3bc1
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/MPU2_rels_acqf.stderr
@@ -0,0 +1,38 @@
+Running GenMC Verification...
+X=1, Y=2, a=Err(1), b=Ok(1), c=2
+X=1, Y=2, a=Err(1), b=Ok(1), c=1
+X=1, Y=2, a=Err(1), b=Ok(1), c=0
+X=1, Y=2, a=Err(1), b=Ok(1), c=0
+X=2, Y=3, a=Ok(2), b=Ok(1), c=3
+X=1, Y=3, a=Ok(2), b=Ok(1), c=3
+X=1, Y=3, a=Ok(2), b=Ok(1), c=2
+X=1, Y=3, a=Ok(2), b=Ok(1), c=1
+X=1, Y=3, a=Ok(2), b=Ok(1), c=0
+X=1, Y=3, a=Ok(2), b=Ok(1), c=0
+X=1, Y=1, a=Err(1), b=Err(0), c=1
+X=1, Y=1, a=Err(1), b=Err(0), c=0
+X=1, Y=1, a=Err(1), b=Err(0), c=0
+X=1, Y=1, a=Err(1), b=Err(0), c=1
+X=1, Y=1, a=Err(1), b=Err(0), c=0
+X=1, Y=1, a=Err(1), b=Err(0), c=0
+X=1, Y=2, a=Err(0), b=Ok(1), c=2
+X=1, Y=2, a=Err(0), b=Ok(1), c=1
+X=1, Y=2, a=Err(0), b=Ok(1), c=0
+X=1, Y=2, a=Err(0), b=Ok(1), c=0
+X=1, Y=1, a=Err(0), b=Err(0), c=1
+X=1, Y=1, a=Err(0), b=Err(0), c=0
+X=1, Y=1, a=Err(0), b=Err(0), c=0
+X=1, Y=1, a=Err(0), b=Err(0), c=1
+X=1, Y=1, a=Err(0), b=Err(0), c=0
+X=1, Y=1, a=Err(0), b=Err(0), c=0
+X=1, Y=2, a=Err(0), b=Ok(1), c=2
+X=1, Y=2, a=Err(0), b=Ok(1), c=1
+X=1, Y=2, a=Err(0), b=Ok(1), c=0
+X=1, Y=2, a=Err(0), b=Ok(1), c=0
+X=1, Y=1, a=Err(0), b=Err(0), c=1
+X=1, Y=1, a=Err(0), b=Err(0), c=0
+X=1, Y=1, a=Err(0), b=Err(0), c=0
+X=1, Y=1, a=Err(0), b=Err(0), c=1
+X=1, Y=1, a=Err(0), b=Err(0), c=0
+X=1, Y=1, a=Err(0), b=Err(0), c=0
+Verification complete with 36 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/litmus/MPU_rels_acq.rs b/src/tools/miri/tests/genmc/pass/litmus/MPU_rels_acq.rs
new file mode 100644
index 00000000000..b36c8a288f4
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/MPU_rels_acq.rs
@@ -0,0 +1,42 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+
+// Translated from GenMC's "litmus/MPU+rels+acq" test.
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+
+use std::sync::atomic::AtomicU64;
+use std::sync::atomic::Ordering::*;
+
+// Note: the GenMC equivalent of this test (genmc/tests/correct/litmus/MPU+rels+acq/mpu+rels+acq.c) uses non-atomic accesses for `X` with disabled race detection.
+static X: AtomicU64 = AtomicU64::new(0);
+static Y: AtomicU64 = AtomicU64::new(0);
+
+use crate::genmc::*;
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses.
+    X.store(0, Relaxed);
+    Y.store(0, Relaxed);
+
+    unsafe {
+        spawn_pthread_closure(|| {
+            X.store(1, Relaxed);
+
+            Y.store(0, Release);
+            Y.store(1, Relaxed);
+        });
+        spawn_pthread_closure(|| {
+            Y.fetch_add(1, Relaxed);
+        });
+        spawn_pthread_closure(|| {
+            if Y.load(Acquire) > 1 {
+                X.store(2, Relaxed);
+            }
+        });
+        0
+    }
+}
diff --git a/src/tools/miri/tests/genmc/pass/litmus/MPU_rels_acq.stderr b/src/tools/miri/tests/genmc/pass/litmus/MPU_rels_acq.stderr
new file mode 100644
index 00000000000..423ee6dc399
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/MPU_rels_acq.stderr
@@ -0,0 +1,2 @@
+Running GenMC Verification...
+Verification complete with 13 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/litmus/MP_incMPs.rs b/src/tools/miri/tests/genmc/pass/litmus/MP_incMPs.rs
new file mode 100644
index 00000000000..a08b7de27d1
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/MP_incMPs.rs
@@ -0,0 +1,36 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+
+// Translated from GenMC's "litmus/MP+incMP" test.
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+
+use std::sync::atomic::AtomicU64;
+use std::sync::atomic::Ordering::*;
+
+use crate::genmc::*;
+
+static X: AtomicU64 = AtomicU64::new(0);
+static Y: AtomicU64 = AtomicU64::new(0);
+static Z: AtomicU64 = AtomicU64::new(0);
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    unsafe {
+        spawn_pthread_closure(|| {
+            Y.load(Acquire);
+            Z.fetch_add(1, AcqRel);
+        });
+        spawn_pthread_closure(|| {
+            Z.fetch_add(1, AcqRel);
+            X.load(Acquire);
+        });
+        spawn_pthread_closure(|| {
+            X.store(1, Release);
+            Y.store(1, Release);
+        });
+        0
+    }
+}
diff --git a/src/tools/miri/tests/genmc/pass/litmus/MP_incMPs.stderr b/src/tools/miri/tests/genmc/pass/litmus/MP_incMPs.stderr
new file mode 100644
index 00000000000..f527b612023
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/MP_incMPs.stderr
@@ -0,0 +1,2 @@
+Running GenMC Verification...
+Verification complete with 7 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/litmus/MP_rels_acqf.rs b/src/tools/miri/tests/genmc/pass/litmus/MP_rels_acqf.rs
new file mode 100644
index 00000000000..cf9f5f2dbfa
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/MP_rels_acqf.rs
@@ -0,0 +1,40 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+
+// Translated from GenMC's "litmus/MP+rels+acqf" test.
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+
+use std::sync::atomic::AtomicU64;
+use std::sync::atomic::Ordering::*;
+
+// Note: the GenMC equivalent of this test (genmc/tests/correct/litmus/MP+rels+acqf/mp+rels+acqf.c) uses non-atomic accesses for `X` with disabled race detection.
+static X: AtomicU64 = AtomicU64::new(0);
+static Y: AtomicU64 = AtomicU64::new(0);
+
+use crate::genmc::*;
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses.
+    X.store(0, Relaxed);
+    Y.store(0, Relaxed);
+
+    unsafe {
+        spawn_pthread_closure(|| {
+            X.store(1, Relaxed);
+
+            Y.store(0, Release);
+            Y.store(1, Relaxed);
+        });
+        spawn_pthread_closure(|| {
+            if Y.load(Relaxed) != 0 {
+                std::sync::atomic::fence(Acquire);
+                let _x = X.load(Relaxed);
+            }
+        });
+        0
+    }
+}
diff --git a/src/tools/miri/tests/genmc/pass/litmus/MP_rels_acqf.stderr b/src/tools/miri/tests/genmc/pass/litmus/MP_rels_acqf.stderr
new file mode 100644
index 00000000000..a67635dee1b
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/MP_rels_acqf.stderr
@@ -0,0 +1,2 @@
+Running GenMC Verification...
+Verification complete with 4 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/litmus/SB.rs b/src/tools/miri/tests/genmc/pass/litmus/SB.rs
new file mode 100644
index 00000000000..e592fe05c4e
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/SB.rs
@@ -0,0 +1,47 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+
+// Translated from GenMC's "litmus/SB" test.
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+
+use std::sync::atomic::AtomicU64;
+use std::sync::atomic::Ordering::*;
+
+use crate::genmc::*;
+
+static X: AtomicU64 = AtomicU64::new(0);
+static Y: AtomicU64 = AtomicU64::new(0);
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses.
+    X.store(0, Relaxed);
+    Y.store(0, Relaxed);
+
+    unsafe {
+        let mut a = 1234;
+        let mut b = 1234;
+        let ids = [
+            spawn_pthread_closure(|| {
+                X.store(1, Release);
+                a = Y.load(Acquire);
+            }),
+            spawn_pthread_closure(|| {
+                Y.store(1, Release);
+                b = X.load(Acquire);
+            }),
+        ];
+        // Join so we can read the final values.
+        join_pthreads(ids);
+
+        // Check that we don't get any unexpected values:
+        if !matches!((a, b), (0, 0) | (0, 1) | (1, 0) | (1, 1)) {
+            std::process::abort();
+        }
+
+        0
+    }
+}
diff --git a/src/tools/miri/tests/genmc/pass/litmus/SB.stderr b/src/tools/miri/tests/genmc/pass/litmus/SB.stderr
new file mode 100644
index 00000000000..a67635dee1b
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/SB.stderr
@@ -0,0 +1,2 @@
+Running GenMC Verification...
+Verification complete with 4 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/litmus/SB_2sc_scf.rs b/src/tools/miri/tests/genmc/pass/litmus/SB_2sc_scf.rs
new file mode 100644
index 00000000000..ffc44de1bc7
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/SB_2sc_scf.rs
@@ -0,0 +1,32 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+
+// Translated from GenMC's "litmus/SB+2sc+scf" test.
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+
+use std::sync::atomic::AtomicU64;
+use std::sync::atomic::Ordering::*;
+
+use crate::genmc::*;
+
+static X: AtomicU64 = AtomicU64::new(0);
+static Y: AtomicU64 = AtomicU64::new(0);
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    unsafe {
+        spawn_pthread_closure(|| {
+            X.store(1, SeqCst);
+            Y.load(SeqCst);
+        });
+        spawn_pthread_closure(|| {
+            Y.store(1, Relaxed);
+            std::sync::atomic::fence(SeqCst);
+            X.load(Relaxed);
+        });
+        0
+    }
+}
diff --git a/src/tools/miri/tests/genmc/pass/litmus/SB_2sc_scf.stderr b/src/tools/miri/tests/genmc/pass/litmus/SB_2sc_scf.stderr
new file mode 100644
index 00000000000..485142e945a
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/SB_2sc_scf.stderr
@@ -0,0 +1,2 @@
+Running GenMC Verification...
+Verification complete with 3 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/litmus/Z6_U.rs b/src/tools/miri/tests/genmc/pass/litmus/Z6_U.rs
new file mode 100644
index 00000000000..f96679b23a5
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/Z6_U.rs
@@ -0,0 +1,62 @@
+//@revisions: weak sc
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+//@[sc]compile-flags: -Zmiri-disable-weak-memory-emulation
+
+// Translated from GenMC's "litmus/Z6.U" test.
+//
+// The `sc` variant of this test checks that we get fewer executions when weak memory emulation is disabled.
+
+// NOTE: the order of the lines in the output may change with changes to GenMC.
+// Before blessing the new output, ensure that only the order of lines in the output changed, and none of the outputs are missing.
+
+// NOTE: GenMC supports instruction caching and does not need replay completed threads.
+// This means that an identical test in GenMC may output fewer lines (disable instruction caching to see all results).
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+#[path = "../../../utils/mod.rs"]
+mod utils;
+
+use std::fmt::Write;
+use std::sync::atomic::AtomicU64;
+use std::sync::atomic::Ordering::*;
+
+use crate::genmc::*;
+use crate::utils::*;
+
+static X: AtomicU64 = AtomicU64::new(0);
+static Y: AtomicU64 = AtomicU64::new(0);
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses.
+    X.store(0, Relaxed);
+    Y.store(0, Relaxed);
+
+    unsafe {
+        let mut a = 1234;
+        let mut b = 1234;
+        let ids = [
+            spawn_pthread_closure(|| {
+                X.store(1, SeqCst);
+                Y.store(1, Release);
+            }),
+            spawn_pthread_closure(|| {
+                Y.fetch_add(1, SeqCst);
+                a = Y.load(Relaxed);
+            }),
+            spawn_pthread_closure(|| {
+                Y.store(3, SeqCst);
+                b = X.load(SeqCst);
+            }),
+        ];
+        join_pthreads(ids);
+
+        // Print the values to check that we get all of them:
+        writeln!(MiriStderr, "a={a}, b={b}, X={}, Y={}", X.load(Relaxed), Y.load(Relaxed))
+            .unwrap_or_else(|_| std::process::abort());
+        0
+    }
+}
diff --git a/src/tools/miri/tests/genmc/pass/litmus/Z6_U.sc.stderr b/src/tools/miri/tests/genmc/pass/litmus/Z6_U.sc.stderr
new file mode 100644
index 00000000000..c8fbb8951a3
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/Z6_U.sc.stderr
@@ -0,0 +1,20 @@
+Running GenMC Verification...
+a=2, b=1, X=1, Y=3
+a=4, b=1, X=1, Y=4
+a=3, b=1, X=1, Y=3
+a=2, b=1, X=1, Y=2
+a=2, b=0, X=1, Y=2
+a=1, b=1, X=1, Y=1
+a=1, b=0, X=1, Y=1
+a=4, b=1, X=1, Y=1
+a=4, b=0, X=1, Y=1
+a=1, b=1, X=1, Y=3
+a=3, b=1, X=1, Y=3
+a=1, b=1, X=1, Y=1
+a=1, b=0, X=1, Y=1
+a=3, b=1, X=1, Y=1
+a=3, b=0, X=1, Y=1
+a=1, b=1, X=1, Y=3
+a=1, b=1, X=1, Y=1
+a=1, b=0, X=1, Y=1
+Verification complete with 18 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/litmus/Z6_U.weak.stderr b/src/tools/miri/tests/genmc/pass/litmus/Z6_U.weak.stderr
new file mode 100644
index 00000000000..72c59d33f77
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/Z6_U.weak.stderr
@@ -0,0 +1,24 @@
+Running GenMC Verification...
+a=2, b=1, X=1, Y=3
+a=4, b=1, X=1, Y=4
+a=4, b=0, X=1, Y=4
+a=3, b=1, X=1, Y=3
+a=2, b=1, X=1, Y=2
+a=2, b=0, X=1, Y=2
+a=1, b=1, X=1, Y=1
+a=1, b=0, X=1, Y=1
+a=4, b=1, X=1, Y=1
+a=4, b=0, X=1, Y=1
+a=1, b=1, X=1, Y=3
+a=1, b=0, X=1, Y=3
+a=3, b=1, X=1, Y=3
+a=3, b=0, X=1, Y=3
+a=1, b=1, X=1, Y=1
+a=1, b=0, X=1, Y=1
+a=3, b=1, X=1, Y=1
+a=3, b=0, X=1, Y=1
+a=1, b=1, X=1, Y=3
+a=1, b=0, X=1, Y=3
+a=1, b=1, X=1, Y=1
+a=1, b=0, X=1, Y=1
+Verification complete with 22 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/litmus/Z6_acq.rs b/src/tools/miri/tests/genmc/pass/litmus/Z6_acq.rs
new file mode 100644
index 00000000000..b00f3a59ce6
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/Z6_acq.rs
@@ -0,0 +1,38 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+
+// Translated from GenMC's "litmus/Z6+acq" test.
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+
+use std::sync::atomic::AtomicU64;
+use std::sync::atomic::Ordering::*;
+
+use crate::genmc::*;
+
+static X: AtomicU64 = AtomicU64::new(0);
+static Y: AtomicU64 = AtomicU64::new(0);
+static Z: AtomicU64 = AtomicU64::new(0);
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    unsafe {
+        spawn_pthread_closure(|| {
+            X.store(1, Relaxed);
+            std::sync::atomic::fence(SeqCst);
+            Y.store(1, Relaxed);
+        });
+        spawn_pthread_closure(|| {
+            Y.load(Acquire);
+            Z.store(1, Relaxed);
+        });
+        spawn_pthread_closure(|| {
+            Z.store(2, Relaxed);
+            std::sync::atomic::fence(SeqCst);
+            X.load(Relaxed);
+        });
+        0
+    }
+}
diff --git a/src/tools/miri/tests/genmc/pass/litmus/Z6_acq.stderr b/src/tools/miri/tests/genmc/pass/litmus/Z6_acq.stderr
new file mode 100644
index 00000000000..f527b612023
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/Z6_acq.stderr
@@ -0,0 +1,2 @@
+Running GenMC Verification...
+Verification complete with 7 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/litmus/atomicpo.rs b/src/tools/miri/tests/genmc/pass/litmus/atomicpo.rs
new file mode 100644
index 00000000000..75be89893da
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/atomicpo.rs
@@ -0,0 +1,32 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+
+// Translated from GenMC's test "litmus/atomicpo".
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+
+use std::sync::atomic::AtomicU64;
+use std::sync::atomic::Ordering::*;
+
+use crate::genmc::*;
+
+static X: AtomicU64 = AtomicU64::new(0);
+static Y: AtomicU64 = AtomicU64::new(0);
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    unsafe {
+        spawn_pthread_closure(|| {
+            X.store(1, Relaxed);
+            std::sync::atomic::fence(AcqRel);
+            Y.store(1, Relaxed);
+        });
+        spawn_pthread_closure(|| {
+            Y.swap(1, Relaxed);
+            X.swap(1, Relaxed);
+        });
+        0
+    }
+}
diff --git a/src/tools/miri/tests/genmc/pass/litmus/atomicpo.stderr b/src/tools/miri/tests/genmc/pass/litmus/atomicpo.stderr
new file mode 100644
index 00000000000..a67635dee1b
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/atomicpo.stderr
@@ -0,0 +1,2 @@
+Running GenMC Verification...
+Verification complete with 4 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/litmus/casdep.rs b/src/tools/miri/tests/genmc/pass/litmus/casdep.rs
new file mode 100644
index 00000000000..c376d96b111
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/casdep.rs
@@ -0,0 +1,37 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+
+// Translated from GenMC's test "litmus/casdep".
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+
+use std::sync::atomic::AtomicU64;
+use std::sync::atomic::Ordering::*;
+
+use crate::genmc::*;
+
+static X: AtomicU64 = AtomicU64::new(0);
+static Y: AtomicU64 = AtomicU64::new(0);
+static Z: AtomicU64 = AtomicU64::new(0);
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses.
+    X.store(0, Relaxed);
+    Y.store(0, Relaxed);
+    Z.store(0, Relaxed);
+
+    unsafe {
+        spawn_pthread_closure(|| {
+            let a = X.load(Relaxed);
+            let _b = Y.compare_exchange(a, 1, Relaxed, Relaxed);
+            Z.store(a, Relaxed);
+        });
+        spawn_pthread_closure(|| {
+            Y.store(2, Relaxed);
+        });
+        0
+    }
+}
diff --git a/src/tools/miri/tests/genmc/pass/litmus/casdep.stderr b/src/tools/miri/tests/genmc/pass/litmus/casdep.stderr
new file mode 100644
index 00000000000..bde951866d0
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/casdep.stderr
@@ -0,0 +1,2 @@
+Running GenMC Verification...
+Verification complete with 2 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/litmus/ccr.rs b/src/tools/miri/tests/genmc/pass/litmus/ccr.rs
new file mode 100644
index 00000000000..865895b4dcd
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/ccr.rs
@@ -0,0 +1,34 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+
+// Translated from GenMC's test "litmus/ccr".
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+
+use std::sync::atomic::AtomicU64;
+use std::sync::atomic::Ordering::*;
+
+use crate::genmc::*;
+
+static X: AtomicU64 = AtomicU64::new(0);
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses.
+    X.store(0, Relaxed);
+
+    unsafe {
+        spawn_pthread_closure(|| {
+            let expected = 0;
+            let _ = X.compare_exchange(expected, 42, Relaxed, Relaxed);
+        });
+        spawn_pthread_closure(|| {
+            let expected = 0;
+            let _ = X.compare_exchange(expected, 17, Relaxed, Relaxed);
+            X.load(Relaxed);
+        });
+        0
+    }
+}
diff --git a/src/tools/miri/tests/genmc/pass/litmus/ccr.stderr b/src/tools/miri/tests/genmc/pass/litmus/ccr.stderr
new file mode 100644
index 00000000000..bde951866d0
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/ccr.stderr
@@ -0,0 +1,2 @@
+Running GenMC Verification...
+Verification complete with 2 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/litmus/cii.rs b/src/tools/miri/tests/genmc/pass/litmus/cii.rs
new file mode 100644
index 00000000000..663c806e667
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/cii.rs
@@ -0,0 +1,35 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+
+// Translated from GenMC's test "litmus/cii".
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+
+use std::sync::atomic::AtomicU64;
+use std::sync::atomic::Ordering::*;
+
+use crate::genmc::*;
+
+static X: AtomicU64 = AtomicU64::new(0);
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses.
+    X.store(0, Relaxed);
+
+    unsafe {
+        spawn_pthread_closure(|| {
+            let expected = 1;
+            let _ = X.compare_exchange(expected, 2, Relaxed, Relaxed);
+        });
+        spawn_pthread_closure(|| {
+            X.fetch_add(1, Relaxed);
+        });
+        spawn_pthread_closure(|| {
+            X.fetch_add(1, Relaxed);
+        });
+        0
+    }
+}
diff --git a/src/tools/miri/tests/genmc/pass/litmus/cii.stderr b/src/tools/miri/tests/genmc/pass/litmus/cii.stderr
new file mode 100644
index 00000000000..b9bdf2245ae
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/cii.stderr
@@ -0,0 +1,2 @@
+Running GenMC Verification...
+Verification complete with 6 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/litmus/corr.rs b/src/tools/miri/tests/genmc/pass/litmus/corr.rs
new file mode 100644
index 00000000000..d6c95100fc2
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/corr.rs
@@ -0,0 +1,45 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+
+// Translated from GenMC's "CoRR" test.
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+
+use std::sync::atomic::AtomicU64;
+use std::sync::atomic::Ordering::*;
+
+use crate::genmc::*;
+
+static X: AtomicU64 = AtomicU64::new(0);
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses.
+    X.store(0, Relaxed);
+
+    unsafe {
+        let mut a = 1234;
+        let mut b = 1234;
+        let ids = [
+            spawn_pthread_closure(|| {
+                X.store(1, Release);
+                X.store(2, Release);
+            }),
+            spawn_pthread_closure(|| {
+                a = X.load(Acquire);
+                b = X.load(Acquire);
+            }),
+        ];
+        // Join so we can read the final values.
+        join_pthreads(ids);
+
+        // Check that we don't get any unexpected values:
+        if !matches!((a, b), (0, 0) | (0, 1) | (0, 2) | (1, 1) | (1, 2) | (2, 2)) {
+            std::process::abort();
+        }
+
+        0
+    }
+}
diff --git a/src/tools/miri/tests/genmc/pass/litmus/corr.stderr b/src/tools/miri/tests/genmc/pass/litmus/corr.stderr
new file mode 100644
index 00000000000..b9bdf2245ae
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/corr.stderr
@@ -0,0 +1,2 @@
+Running GenMC Verification...
+Verification complete with 6 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/litmus/corr0.rs b/src/tools/miri/tests/genmc/pass/litmus/corr0.rs
new file mode 100644
index 00000000000..f722131fda8
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/corr0.rs
@@ -0,0 +1,46 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+
+// Translated from GenMC's "CoRR0" test.
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+
+use std::sync::atomic::AtomicU64;
+use std::sync::atomic::Ordering::*;
+
+use crate::genmc::*;
+
+static X: AtomicU64 = AtomicU64::new(0);
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses.
+    X.store(0, Relaxed);
+
+    unsafe {
+        let mut a = 1234;
+        let mut b = 1234;
+        let ids = [
+            spawn_pthread_closure(|| {
+                X.store(1, Release);
+            }),
+            spawn_pthread_closure(|| {
+                a = X.load(Acquire);
+            }),
+            spawn_pthread_closure(|| {
+                b = X.load(Acquire);
+            }),
+        ];
+        // Join so we can read the final values.
+        join_pthreads(ids);
+
+        // Check that we don't get any unexpected values:
+        if !matches!((a, b), (0, 0) | (0, 1) | (1, 0) | (1, 1)) {
+            std::process::abort();
+        }
+
+        0
+    }
+}
diff --git a/src/tools/miri/tests/genmc/pass/litmus/corr0.stderr b/src/tools/miri/tests/genmc/pass/litmus/corr0.stderr
new file mode 100644
index 00000000000..a67635dee1b
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/corr0.stderr
@@ -0,0 +1,2 @@
+Running GenMC Verification...
+Verification complete with 4 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/litmus/corr1.rs b/src/tools/miri/tests/genmc/pass/litmus/corr1.rs
new file mode 100644
index 00000000000..a4e8249bac3
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/corr1.rs
@@ -0,0 +1,58 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+
+// Translated from GenMC's "CoRR1" test.
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+
+use std::sync::atomic::AtomicU64;
+use std::sync::atomic::Ordering::*;
+
+use crate::genmc::*;
+
+static X: AtomicU64 = AtomicU64::new(0);
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses.
+    X.store(0, Relaxed);
+
+    unsafe {
+        let mut a = 1234;
+        let mut b = 1234;
+        let mut c = 1234;
+        let ids = [
+            spawn_pthread_closure(|| {
+                X.store(1, Release);
+            }),
+            spawn_pthread_closure(|| {
+                X.store(2, Release);
+            }),
+            spawn_pthread_closure(|| {
+                a = X.load(Acquire);
+                b = X.load(Acquire);
+            }),
+            spawn_pthread_closure(|| {
+                c = X.load(Acquire);
+            }),
+        ];
+        // Join so we can read the final values.
+        join_pthreads(ids);
+
+        // Check that we don't get any unexpected values (only 0, 1, 2 are allowed):
+        if !(matches!(a, 0..=2) && matches!(b, 0..=2) && matches!(c, 0..=2)) {
+            std::process::abort();
+        }
+        // The 36 possible program executions can have 21 different results for (a, b, c).
+        // Of the 27 = 3*3*3 total results for (a, b, c),
+        // those where `a != 0` and `b == 0` are not allowed by the memory model.
+        // Once the load for `a` reads either 1 or 2, the load for `b` must see that store too, so it cannot read 0.
+        if a != 0 && b == 0 {
+            std::process::abort();
+        }
+
+        0
+    }
+}
diff --git a/src/tools/miri/tests/genmc/pass/litmus/corr1.stderr b/src/tools/miri/tests/genmc/pass/litmus/corr1.stderr
new file mode 100644
index 00000000000..384425fc43c
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/corr1.stderr
@@ -0,0 +1,2 @@
+Running GenMC Verification...
+Verification complete with 36 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/litmus/corr2.rs b/src/tools/miri/tests/genmc/pass/litmus/corr2.rs
new file mode 100644
index 00000000000..2f490d36377
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/corr2.rs
@@ -0,0 +1,70 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+
+// Translated from GenMC's "CoRR2" test.
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+
+use std::sync::atomic::AtomicU64;
+use std::sync::atomic::Ordering::*;
+
+use crate::genmc::*;
+
+static X: AtomicU64 = AtomicU64::new(0);
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses.
+    X.store(0, Relaxed);
+
+    unsafe {
+        let mut a = 1234;
+        let mut b = 1234;
+        let mut c = 1234;
+        let mut d = 1234;
+        let ids = [
+            spawn_pthread_closure(|| {
+                X.store(1, Release);
+            }),
+            spawn_pthread_closure(|| {
+                X.store(2, Release);
+            }),
+            spawn_pthread_closure(|| {
+                a = X.load(Acquire);
+                b = X.load(Acquire);
+            }),
+            spawn_pthread_closure(|| {
+                c = X.load(Acquire);
+                d = X.load(Acquire);
+            }),
+        ];
+        // Join so we can read the final values.
+        join_pthreads(ids);
+
+        // Check that we don't get any unexpected values (only 0, 1, 2 are allowed):
+        if !(matches!(a, 0..=2) && matches!(b, 0..=2) && matches!(c, 0..=2) && matches!(d, 0..=2)) {
+            std::process::abort();
+        }
+
+        // The 72 possible program executions can have 47 different results for (a, b, c, d).
+        // Of the 81 = 3*3*3*3 total results for (a, b, c, d),
+        // those where `a != 0` and `b == 0` are not allowed by the memory model.
+        // Once the load for `a` reads either 1 or 2, the load for `b` must see that store too, so it cannot read 0.
+        // The same applies to `c, d` in the other thread.
+        //
+        // Additionally, if one thread reads `1, 2` or `2, 1`, the other thread cannot see the opposite order.
+        if a != 0 && b == 0 {
+            std::process::abort();
+        } else if c != 0 && d == 0 {
+            std::process::abort();
+        } else if (a, b) == (1, 2) && (c, d) == (2, 1) {
+            std::process::abort();
+        } else if (a, b) == (2, 1) && (c, d) == (1, 2) {
+            std::process::abort();
+        }
+
+        0
+    }
+}
diff --git a/src/tools/miri/tests/genmc/pass/litmus/corr2.stderr b/src/tools/miri/tests/genmc/pass/litmus/corr2.stderr
new file mode 100644
index 00000000000..07fd2d4de18
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/corr2.stderr
@@ -0,0 +1,2 @@
+Running GenMC Verification...
+Verification complete with 72 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/litmus/corw.rs b/src/tools/miri/tests/genmc/pass/litmus/corw.rs
new file mode 100644
index 00000000000..7acc20822a4
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/corw.rs
@@ -0,0 +1,43 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+
+// Translated from GenMC's "CoRW" test.
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+
+use std::sync::atomic::AtomicU64;
+use std::sync::atomic::Ordering::*;
+
+use crate::genmc::*;
+
+static X: AtomicU64 = AtomicU64::new(0);
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses.
+    X.store(0, Relaxed);
+
+    unsafe {
+        let mut a = 1234;
+        let ids = [
+            spawn_pthread_closure(|| {
+                a = X.load(Acquire);
+                X.store(1, Release);
+            }),
+            spawn_pthread_closure(|| {
+                X.store(2, Release);
+            }),
+        ];
+        // Join so we can read the final values.
+        join_pthreads(ids);
+
+        // Check that we don't get any unexpected values (the load cannot read `1`):
+        if !matches!(a, 0 | 2) {
+            std::process::abort();
+        }
+
+        0
+    }
+}
diff --git a/src/tools/miri/tests/genmc/pass/litmus/corw.stderr b/src/tools/miri/tests/genmc/pass/litmus/corw.stderr
new file mode 100644
index 00000000000..485142e945a
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/corw.stderr
@@ -0,0 +1,2 @@
+Running GenMC Verification...
+Verification complete with 3 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/litmus/cowr.rs b/src/tools/miri/tests/genmc/pass/litmus/cowr.rs
new file mode 100644
index 00000000000..1c51f23a09c
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/cowr.rs
@@ -0,0 +1,40 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+
+// Translated from GenMC's "CoWR" test.
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+
+use std::sync::atomic::AtomicU64;
+use std::sync::atomic::Ordering::*;
+
+use crate::genmc::*;
+
+static X: AtomicU64 = AtomicU64::new(0);
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    unsafe {
+        let mut a = 1234;
+        let ids = [
+            spawn_pthread_closure(|| {
+                X.store(1, Release);
+                a = X.load(Acquire);
+            }),
+            spawn_pthread_closure(|| {
+                X.store(2, Release);
+            }),
+        ];
+        // Join so we can read the final values.
+        join_pthreads(ids);
+
+        // Check that we don't get any unexpected values (the load cannot read `0`):
+        if !matches!(a, 1 | 2) {
+            std::process::abort();
+        }
+
+        0
+    }
+}
diff --git a/src/tools/miri/tests/genmc/pass/litmus/cowr.stderr b/src/tools/miri/tests/genmc/pass/litmus/cowr.stderr
new file mode 100644
index 00000000000..485142e945a
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/cowr.stderr
@@ -0,0 +1,2 @@
+Running GenMC Verification...
+Verification complete with 3 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/litmus/cumul-release.rs b/src/tools/miri/tests/genmc/pass/litmus/cumul-release.rs
new file mode 100644
index 00000000000..2387976a8ca
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/cumul-release.rs
@@ -0,0 +1,58 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+
+// Translated from GenMC's test "litmus/cumul-release".
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+
+use std::sync::atomic::AtomicU64;
+use std::sync::atomic::Ordering::*;
+
+use crate::genmc::*;
+
+static X: AtomicU64 = AtomicU64::new(0);
+static Y: AtomicU64 = AtomicU64::new(0);
+static Z: AtomicU64 = AtomicU64::new(0);
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses.
+    X.store(0, Relaxed);
+    Y.store(0, Relaxed);
+    Z.store(0, Relaxed);
+
+    unsafe {
+        let mut a = 1234;
+        let mut b = 1234;
+        let mut c = 1234;
+        let ids = [
+            spawn_pthread_closure(|| {
+                X.store(1, Relaxed);
+                Y.store(1, Release);
+            }),
+            spawn_pthread_closure(|| {
+                a = Y.load(Relaxed);
+                Z.store(a, Relaxed);
+            }),
+            spawn_pthread_closure(|| {
+                b = Z.load(Relaxed);
+                std::sync::atomic::fence(AcqRel);
+                c = X.load(Relaxed);
+            }),
+        ];
+        // Join so we can read the final values.
+        join_pthreads(ids);
+
+        // Check that we don't get any unexpected values:
+        if !matches!(
+            (a, b, c),
+            (0, 0, 0) | (0, 0, 1) | (1, 0, 0) | (1, 0, 1) | (1, 1, 0) | (1, 1, 1)
+        ) {
+            std::process::abort();
+        }
+
+        0
+    }
+}
diff --git a/src/tools/miri/tests/genmc/pass/litmus/cumul-release.stderr b/src/tools/miri/tests/genmc/pass/litmus/cumul-release.stderr
new file mode 100644
index 00000000000..a031dfc8977
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/cumul-release.stderr
@@ -0,0 +1,2 @@
+Running GenMC Verification...
+Verification complete with 8 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/litmus/default.rs b/src/tools/miri/tests/genmc/pass/litmus/default.rs
new file mode 100644
index 00000000000..55fb1ac34ac
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/default.rs
@@ -0,0 +1,47 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+
+// Translated from GenMC's "litmus/default" test.
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+
+use std::sync::atomic::AtomicU64;
+use std::sync::atomic::Ordering::*;
+
+use crate::genmc::*;
+
+static X: AtomicU64 = AtomicU64::new(0);
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses.
+    X.store(0, Relaxed);
+
+    unsafe {
+        let mut a = 1234;
+        let mut b = 1234;
+        let ids = [
+            spawn_pthread_closure(|| {
+                a = X.load(Acquire);
+                b = X.load(Acquire);
+            }),
+            spawn_pthread_closure(|| {
+                X.store(1, Release);
+            }),
+            spawn_pthread_closure(|| {
+                X.store(2, Release);
+            }),
+        ];
+        // Join so we can read the final values.
+        join_pthreads(ids);
+
+        // Check that we don't get any unexpected values:
+        if !matches!((a, b), (0, 0) | (0, 1) | (0, 2) | (1, 1) | (1, 2) | (2, 1) | (2, 2)) {
+            std::process::abort();
+        }
+
+        0
+    }
+}
diff --git a/src/tools/miri/tests/genmc/pass/litmus/default.stderr b/src/tools/miri/tests/genmc/pass/litmus/default.stderr
new file mode 100644
index 00000000000..87d38d25041
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/default.stderr
@@ -0,0 +1,2 @@
+Running GenMC Verification...
+Verification complete with 12 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/litmus/detour.join.stderr b/src/tools/miri/tests/genmc/pass/litmus/detour.join.stderr
new file mode 100644
index 00000000000..5006f11fefb
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/detour.join.stderr
@@ -0,0 +1,2 @@
+Running GenMC Verification...
+Verification complete with 9 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/litmus/detour.no_join.stderr b/src/tools/miri/tests/genmc/pass/litmus/detour.no_join.stderr
new file mode 100644
index 00000000000..5006f11fefb
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/detour.no_join.stderr
@@ -0,0 +1,2 @@
+Running GenMC Verification...
+Verification complete with 9 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/litmus/detour.rs b/src/tools/miri/tests/genmc/pass/litmus/detour.rs
new file mode 100644
index 00000000000..7136c029bbb
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/detour.rs
@@ -0,0 +1,68 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+//@revisions: join no_join
+
+// Translated from GenMC's "litmus/detour" test.
+
+// This test has two revisitions to test whether we get the same result
+// independent of whether we join the spawned threads or not.
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+
+use std::sync::atomic::AtomicI64;
+use std::sync::atomic::Ordering::*;
+
+use crate::genmc::*;
+
+static X: AtomicI64 = AtomicI64::new(0);
+static Y: AtomicI64 = AtomicI64::new(0);
+static Z: AtomicI64 = AtomicI64::new(0);
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    // FIXME(genmc,HACK): remove these initializing writes once Miri-GenMC supports mixed atomic-non-atomic accesses.
+    X.store(0, Relaxed);
+    Y.store(0, Relaxed);
+    Z.store(0, Relaxed);
+
+    unsafe {
+        // Make these static so we can exit the main thread while the other threads still run.
+        // If these are `let mut` like the other tests, this will cause a use-after-free bug.
+        static mut A: i64 = 1234;
+        static mut B: i64 = 1234;
+        static mut C: i64 = 1234;
+        let ids = [
+            spawn_pthread_closure(|| {
+                X.store(1, Relaxed);
+            }),
+            spawn_pthread_closure(|| {
+                A = Z.load(Relaxed);
+                X.store(A.wrapping_sub(1), Relaxed);
+                B = X.load(Relaxed);
+                Y.store(B, Relaxed);
+            }),
+            spawn_pthread_closure(|| {
+                C = Y.load(Relaxed);
+                Z.store(C, Relaxed);
+            }),
+        ];
+
+        // The `no_join` revision doesn't join any of the running threads to test that
+        // we still explore the same number of executions in that case.
+        if cfg!(no_join) {
+            return 0;
+        }
+
+        // Join so we can read the final values.
+        join_pthreads(ids);
+
+        // Check that we don't get any unexpected values:
+        if !matches!((A, B, C), (0, 1, 0) | (0, -1, 0) | (0, 1, 1) | (0, -1, -1)) {
+            std::process::abort();
+        }
+
+        0
+    }
+}
diff --git a/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.rs b/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.rs
new file mode 100644
index 00000000000..796ffbf97f9
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.rs
@@ -0,0 +1,53 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+
+// Translated from GenMC's "fr+w+w+w+reads" test.
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+
+use std::sync::atomic::AtomicU64;
+use std::sync::atomic::Ordering::*;
+
+use crate::genmc::*;
+
+static X: AtomicU64 = AtomicU64::new(0);
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses.
+    X.store(0, Relaxed);
+
+    unsafe {
+        let mut result = [1234; 4];
+        let ids = [
+            spawn_pthread_closure(|| {
+                X.store(1, Relaxed);
+            }),
+            spawn_pthread_closure(|| {
+                X.store(2, Relaxed);
+            }),
+            spawn_pthread_closure(|| {
+                X.store(3, Relaxed);
+            }),
+            spawn_pthread_closure(|| {
+                result[0] = X.load(Relaxed);
+                result[1] = X.load(Relaxed);
+                result[2] = X.load(Relaxed);
+                result[3] = X.load(Relaxed);
+            }),
+        ];
+        // Join so we can read the final values.
+        join_pthreads(ids);
+
+        // Check that we don't get any unexpected values:
+        for val in result {
+            if !matches!(val, 0..=3) {
+                std::process::abort();
+            }
+        }
+
+        0
+    }
+}
diff --git a/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.stderr b/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.stderr
new file mode 100644
index 00000000000..c09b50e282f
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/fr_w_w_w_reads.stderr
@@ -0,0 +1,2 @@
+Running GenMC Verification...
+Verification complete with 210 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/litmus/inc2w.rs b/src/tools/miri/tests/genmc/pass/litmus/inc2w.rs
new file mode 100644
index 00000000000..fd8574c4f7c
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/inc2w.rs
@@ -0,0 +1,45 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+
+// Translated from GenMC's test "litmus/inc2w".
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+
+use std::sync::atomic::AtomicU64;
+use std::sync::atomic::Ordering::*;
+
+use crate::genmc::*;
+
+static X: AtomicU64 = AtomicU64::new(0);
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    // FIXME(genmc,HACK): remove this initializing write once Miri-GenMC supports mixed atomic-non-atomic accesses.
+    X.store(0, Relaxed);
+
+    unsafe {
+        let ids = [
+            spawn_pthread_closure(|| {
+                X.fetch_add(1, Relaxed);
+            }),
+            spawn_pthread_closure(|| {
+                X.store(4, Release);
+            }),
+            spawn_pthread_closure(|| {
+                X.fetch_add(2, Relaxed);
+            }),
+        ];
+        // Join so we can read the final values.
+        join_pthreads(ids);
+
+        // Check that we don't get any unexpected values:
+        let x = X.load(Relaxed);
+        if !matches!(x, 4..=7) {
+            std::process::abort();
+        }
+
+        0
+    }
+}
diff --git a/src/tools/miri/tests/genmc/pass/litmus/inc2w.stderr b/src/tools/miri/tests/genmc/pass/litmus/inc2w.stderr
new file mode 100644
index 00000000000..b9bdf2245ae
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/inc2w.stderr
@@ -0,0 +1,2 @@
+Running GenMC Verification...
+Verification complete with 6 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/litmus/inc_inc_RR_W_RR.rs b/src/tools/miri/tests/genmc/pass/litmus/inc_inc_RR_W_RR.rs
new file mode 100644
index 00000000000..40ca4863185
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/inc_inc_RR_W_RR.rs
@@ -0,0 +1,63 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+
+use std::ffi::c_void;
+use std::sync::atomic::AtomicU64;
+use std::sync::atomic::Ordering::*;
+
+use crate::genmc::*;
+
+static X: AtomicU64 = AtomicU64::new(0);
+
+static mut A: u64 = 0;
+static mut B: u64 = 0;
+static mut C: u64 = 0;
+static mut D: u64 = 0;
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    let thread_order = [thread_1, thread_2, thread_3, thread_4, thread_5];
+    let ids = unsafe { spawn_pthreads_no_params(thread_order) };
+    unsafe { join_pthreads(ids) };
+
+    if unsafe { A == 42 && B == 2 && C == 1 && D == 42 } {
+        std::process::abort();
+    }
+
+    0
+}
+
+pub extern "C" fn thread_1(_value: *mut c_void) -> *mut c_void {
+    X.fetch_add(1, Relaxed);
+    std::ptr::null_mut()
+}
+
+pub extern "C" fn thread_2(_value: *mut c_void) -> *mut c_void {
+    X.fetch_add(1, Relaxed);
+    std::ptr::null_mut()
+}
+
+pub extern "C" fn thread_3(_value: *mut c_void) -> *mut c_void {
+    unsafe {
+        A = X.load(Relaxed);
+        B = X.load(Relaxed);
+    }
+    std::ptr::null_mut()
+}
+
+pub extern "C" fn thread_4(_value: *mut c_void) -> *mut c_void {
+    X.store(42, Relaxed);
+    std::ptr::null_mut()
+}
+
+pub extern "C" fn thread_5(_value: *mut c_void) -> *mut c_void {
+    unsafe {
+        C = X.load(Relaxed);
+        D = X.load(Relaxed);
+    }
+    std::ptr::null_mut()
+}
diff --git a/src/tools/miri/tests/genmc/pass/litmus/inc_inc_RR_W_RR.stderr b/src/tools/miri/tests/genmc/pass/litmus/inc_inc_RR_W_RR.stderr
new file mode 100644
index 00000000000..770fb7ef880
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/inc_inc_RR_W_RR.stderr
@@ -0,0 +1,2 @@
+Running GenMC Verification...
+Verification complete with 600 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/litmus/riwi.rs b/src/tools/miri/tests/genmc/pass/litmus/riwi.rs
new file mode 100644
index 00000000000..49564c8e4fe
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/riwi.rs
@@ -0,0 +1,31 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows
+
+// Translated from GenMC's "litmus/riwi" test.
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+
+use std::sync::atomic::AtomicU64;
+use std::sync::atomic::Ordering::*;
+
+use crate::genmc::*;
+
+static X: AtomicU64 = AtomicU64::new(0);
+static Y: AtomicU64 = AtomicU64::new(0);
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    unsafe {
+        spawn_pthread_closure(|| {
+            Y.load(Relaxed);
+            X.fetch_add(1, Relaxed);
+        });
+        spawn_pthread_closure(|| {
+            X.fetch_add(1, Relaxed);
+            Y.store(1, Release);
+        });
+        0
+    }
+}
diff --git a/src/tools/miri/tests/genmc/pass/litmus/riwi.stderr b/src/tools/miri/tests/genmc/pass/litmus/riwi.stderr
new file mode 100644
index 00000000000..485142e945a
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/riwi.stderr
@@ -0,0 +1,2 @@
+Running GenMC Verification...
+Verification complete with 3 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/litmus/viktor-relseq.rs b/src/tools/miri/tests/genmc/pass/litmus/viktor-relseq.rs
new file mode 100644
index 00000000000..3256c9f4211
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/viktor-relseq.rs
@@ -0,0 +1,38 @@
+//@compile-flags: -Zmiri-genmc -Zmiri-disable-stacked-borrows -Zmiri-genmc-estimate
+
+// Translated from GenMC's "litmus/viktor-relseq" test.
+//
+// This test also checks that we can run the GenMC estimation mode.
+
+#![no_main]
+
+#[path = "../../../utils/genmc.rs"]
+mod genmc;
+
+use std::sync::atomic::AtomicU64;
+use std::sync::atomic::Ordering::*;
+
+use crate::genmc::*;
+
+static LOCK: AtomicU64 = AtomicU64::new(0);
+
+#[unsafe(no_mangle)]
+fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
+    unsafe {
+        spawn_pthread_closure(|| {
+            LOCK.fetch_add(1, Acquire);
+            LOCK.fetch_add(1, Relaxed);
+        });
+        spawn_pthread_closure(|| {
+            LOCK.fetch_add(1, Relaxed);
+            LOCK.fetch_add(1, Relaxed);
+        });
+        spawn_pthread_closure(|| {
+            LOCK.fetch_add(1, Release);
+        });
+        spawn_pthread_closure(|| {
+            LOCK.fetch_add(1, Relaxed);
+        });
+        0
+    }
+}
diff --git a/src/tools/miri/tests/genmc/pass/litmus/viktor-relseq.stderr b/src/tools/miri/tests/genmc/pass/litmus/viktor-relseq.stderr
new file mode 100644
index 00000000000..c53d5c569b9
--- /dev/null
+++ b/src/tools/miri/tests/genmc/pass/litmus/viktor-relseq.stderr
@@ -0,0 +1,4 @@
+Estimating GenMC verification time...
+Expected verification time: [MEAN] ± [SD]
+Running GenMC Verification...
+Verification complete with 180 executions. No errors found.
diff --git a/src/tools/miri/tests/genmc/pass/test_cxx_build.rs b/src/tools/miri/tests/genmc/pass/test_cxx_build.rs
deleted file mode 100644
index f621bd9114f..00000000000
--- a/src/tools/miri/tests/genmc/pass/test_cxx_build.rs
+++ /dev/null
@@ -1,8 +0,0 @@
-//@compile-flags: -Zmiri-genmc
-
-#![no_main]
-
-#[unsafe(no_mangle)]
-fn miri_start(_argc: isize, _argv: *const *const u8) -> isize {
-    0
-}
diff --git a/src/tools/miri/tests/genmc/pass/test_cxx_build.stderr b/src/tools/miri/tests/genmc/pass/test_cxx_build.stderr
deleted file mode 100644
index 4b7aa824bd1..00000000000
--- a/src/tools/miri/tests/genmc/pass/test_cxx_build.stderr
+++ /dev/null
@@ -1,5 +0,0 @@
-warning: borrow tracking has been disabled, it is not (yet) supported in GenMC mode.
-C++: GenMC handle created!
-Miri: GenMC handle creation successful!
-C++: GenMC handle destroyed!
-Miri: Dropping GenMC handle successful!
diff --git a/src/tools/miri/tests/native-lib/aggregate_arguments.c b/src/tools/miri/tests/native-lib/aggregate_arguments.c
new file mode 100644
index 00000000000..8ad687f2aec
--- /dev/null
+++ b/src/tools/miri/tests/native-lib/aggregate_arguments.c
@@ -0,0 +1,52 @@
+#include <stdint.h>
+
+// See comments in build_native_lib()
+#define EXPORT __attribute__((visibility("default")))
+
+/* Test: fail/pass_struct_expose_only_range */
+
+typedef struct HasPointer {
+  uint8_t *ptr;
+} HasPointer;
+
+EXPORT uint8_t access_struct_ptr(const HasPointer s) {
+  return *s.ptr;
+}
+
+/* Test: test_pass_struct */
+
+typedef struct PassMe {
+    int32_t value;
+    int64_t other_value;
+} PassMe;
+
+EXPORT int64_t pass_struct(const PassMe pass_me) {
+  return pass_me.value + pass_me.other_value;
+}
+
+/* Test: test_pass_struct_complex */
+
+typedef struct Part1 {
+    uint16_t high;
+    uint16_t low;
+} Part1;
+
+typedef struct Part2 {
+    uint32_t bits;
+} Part2;
+
+typedef struct ComplexStruct {
+    Part1 part_1;
+    Part2 part_2;
+    uint32_t part_3;
+} ComplexStruct;
+
+EXPORT int32_t pass_struct_complex(const ComplexStruct complex, uint16_t high, uint16_t low, uint32_t bits) {
+  if (complex.part_1.high == high && complex.part_1.low == low
+      && complex.part_2.bits == bits
+      && complex.part_3 == bits)
+    return 0;
+  else {
+    return 1;
+  }
+}
diff --git a/src/tools/miri/tests/native-lib/fail/pass_struct_expose_only_range.rs b/src/tools/miri/tests/native-lib/fail/pass_struct_expose_only_range.rs
new file mode 100644
index 00000000000..a2b43031a29
--- /dev/null
+++ b/src/tools/miri/tests/native-lib/fail/pass_struct_expose_only_range.rs
@@ -0,0 +1,23 @@
+//@compile-flags: -Zmiri-permissive-provenance
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+struct HasPointer {
+    ptr: *const u8,
+}
+
+extern "C" {
+    fn access_struct_ptr(s: HasPointer) -> u8;
+}
+
+fn main() {
+    let structs = vec![HasPointer { ptr: &0 }, HasPointer { ptr: &1 }];
+    unsafe {
+        let r = access_struct_ptr(structs[1]);
+        assert_eq!(r, 1);
+        // There are two pointers stored in the allocation backing `structs`; ensure
+        // we only exposed the one that was actually passed to C.
+        let _val = *std::ptr::with_exposed_provenance::<u8>(structs[1].ptr.addr()); // fine, ptr got sent to C
+        let _val = *std::ptr::with_exposed_provenance::<u8>(structs[0].ptr.addr()); //~ ERROR: memory access failed
+    };
+}
diff --git a/src/tools/miri/tests/native-lib/fail/pass_struct_expose_only_range.stderr b/src/tools/miri/tests/native-lib/fail/pass_struct_expose_only_range.stderr
new file mode 100644
index 00000000000..a8f85001c23
--- /dev/null
+++ b/src/tools/miri/tests/native-lib/fail/pass_struct_expose_only_range.stderr
@@ -0,0 +1,28 @@
+warning: sharing memory with a native function called via FFI
+  --> tests/native-lib/fail/pass_struct_expose_only_range.rs:LL:CC
+   |
+LL |         let r = access_struct_ptr(structs[1]);
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ sharing memory with a native function
+   |
+   = help: when memory is shared with a native function call, Miri stops tracking initialization and provenance for that memory
+   = help: in particular, Miri assumes that the native call initializes all memory it has access to
+   = help: Miri also assumes that any part of this memory may be a pointer that is permitted to point to arbitrary exposed memory
+   = help: what this means is that Miri will easily miss Undefined Behavior related to incorrect usage of this shared memory, so you should not take a clean Miri run as a signal that your FFI code is UB-free
+   = note: BACKTRACE:
+   = note: inside `main` at tests/native-lib/fail/pass_struct_expose_only_range.rs:LL:CC
+
+error: Undefined Behavior: memory access failed: attempting to access 1 byte, but got $HEX[noalloc] which is a dangling pointer (it has no provenance)
+  --> tests/native-lib/fail/pass_struct_expose_only_range.rs:LL:CC
+   |
+LL |         let _val = *std::ptr::with_exposed_provenance::<u8>(structs[0].ptr.addr());
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
+   |
+   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
+   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+   = note: BACKTRACE:
+   = note: inside `main` at tests/native-lib/fail/pass_struct_expose_only_range.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; 1 warning emitted
+
diff --git a/src/tools/miri/tests/native-lib/fail/struct_not_extern_c.rs b/src/tools/miri/tests/native-lib/fail/struct_not_extern_c.rs
new file mode 100644
index 00000000000..cf8315e0fd9
--- /dev/null
+++ b/src/tools/miri/tests/native-lib/fail/struct_not_extern_c.rs
@@ -0,0 +1,19 @@
+// Only works on Unix targets
+//@ignore-target: windows wasm
+//@only-on-host
+
+#![allow(improper_ctypes)]
+
+pub struct PassMe {
+    pub value: i32,
+    pub other_value: i64,
+}
+
+extern "C" {
+    fn pass_struct(s: PassMe) -> i64;
+}
+
+fn main() {
+    let pass_me = PassMe { value: 42, other_value: 1337 };
+    unsafe { pass_struct(pass_me) }; //~ ERROR: unsupported operation: passing a non-#[repr(C)] struct over FFI
+}
diff --git a/src/tools/miri/tests/native-lib/fail/struct_not_extern_c.stderr b/src/tools/miri/tests/native-lib/fail/struct_not_extern_c.stderr
new file mode 100644
index 00000000000..90e59a31da4
--- /dev/null
+++ b/src/tools/miri/tests/native-lib/fail/struct_not_extern_c.stderr
@@ -0,0 +1,14 @@
+error: unsupported operation: passing a non-#[repr(C)] struct over FFI: PassMe
+  --> tests/native-lib/fail/struct_not_extern_c.rs:LL:CC
+   |
+LL |     unsafe { pass_struct(pass_me) };
+   |              ^^^^^^^^^^^^^^^^^^^^ unsupported operation occurred here
+   |
+   = 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/native-lib/fail/struct_not_extern_c.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/native-lib/fail/tracing/partial_init.stderr b/src/tools/miri/tests/native-lib/fail/tracing/partial_init.stderr
index 74a599ede5c..1c0ad2f0dc9 100644
--- a/src/tools/miri/tests/native-lib/fail/tracing/partial_init.stderr
+++ b/src/tools/miri/tests/native-lib/fail/tracing/partial_init.stderr
@@ -35,7 +35,7 @@ LL |     partial_init();
 
 Uninitialized memory occurred at ALLOC[0x2..0x3], in this allocation:
 ALLOC (stack variable, size: 3, align: 1) {
-    ╾00[wildcard] (1 ptr byte)╼ ╾00[wildcard] (1 ptr byte)╼ __                                        │ ━━░
+    ╾00[wildcard] (ptr fragment 0)╼ ╾00[wildcard] (ptr fragment 0)╼ __                                        │ ━━░
 }
 
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
diff --git a/src/tools/miri/tests/native-lib/fail/uninit_struct.rs b/src/tools/miri/tests/native-lib/fail/uninit_struct.rs
new file mode 100644
index 00000000000..cf61c7f3915
--- /dev/null
+++ b/src/tools/miri/tests/native-lib/fail/uninit_struct.rs
@@ -0,0 +1,27 @@
+#[repr(C)]
+#[derive(Copy, Clone)]
+struct ComplexStruct {
+    part_1: Part1,
+    part_2: Part2,
+    part_3: u32,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+struct Part1 {
+    high: u16,
+    low: u16,
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+struct Part2 {
+    bits: u32,
+}
+
+extern "C" {
+    fn pass_struct_complex(s: ComplexStruct, high: u16, low: u16, bits: u32) -> i32;
+}
+
+fn main() {
+    let arg = std::mem::MaybeUninit::<ComplexStruct>::uninit();
+    unsafe { pass_struct_complex(*arg.as_ptr(), 0, 0, 0) }; //~ ERROR: Undefined Behavior: constructing invalid value
+}
diff --git a/src/tools/miri/tests/native-lib/fail/uninit_struct.stderr b/src/tools/miri/tests/native-lib/fail/uninit_struct.stderr
new file mode 100644
index 00000000000..0fe6ad9c77b
--- /dev/null
+++ b/src/tools/miri/tests/native-lib/fail/uninit_struct.stderr
@@ -0,0 +1,15 @@
+error: Undefined Behavior: constructing invalid value at .part_1.high: encountered uninitialized memory, but expected an integer
+  --> tests/native-lib/fail/uninit_struct.rs:LL:CC
+   |
+LL |     unsafe { pass_struct_complex(*arg.as_ptr(), 0, 0, 0) };
+   |                                  ^^^^^^^^^^^^^ Undefined Behavior occurred here
+   |
+   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
+   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+   = note: BACKTRACE:
+   = note: inside `main` at tests/native-lib/fail/uninit_struct.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/native-lib/pass/aggregate_arguments.rs b/src/tools/miri/tests/native-lib/pass/aggregate_arguments.rs
new file mode 100644
index 00000000000..55acb240612
--- /dev/null
+++ b/src/tools/miri/tests/native-lib/pass/aggregate_arguments.rs
@@ -0,0 +1,52 @@
+fn main() {
+    test_pass_struct();
+    test_pass_struct_complex();
+}
+
+/// Test passing a basic struct as an argument.
+fn test_pass_struct() {
+    // Exactly two fields, so that we hit the ScalarPair case.
+    #[repr(C)]
+    struct PassMe {
+        value: i32,
+        other_value: i64,
+    }
+
+    extern "C" {
+        fn pass_struct(s: PassMe) -> i64;
+    }
+
+    let pass_me = PassMe { value: 42, other_value: 1337 };
+    assert_eq!(unsafe { pass_struct(pass_me) }, 42 + 1337);
+}
+
+/// Test passing a more complex struct as an argument.
+fn test_pass_struct_complex() {
+    #[repr(C)]
+    struct ComplexStruct {
+        part_1: Part1,
+        part_2: Part2,
+        part_3: u32,
+    }
+    #[repr(C)]
+    struct Part1 {
+        high: u16,
+        low: u16,
+    }
+    #[repr(C)]
+    struct Part2 {
+        bits: u32,
+    }
+
+    extern "C" {
+        fn pass_struct_complex(s: ComplexStruct, high: u16, low: u16, bits: u32) -> i32;
+    }
+
+    let high = 0xabcd;
+    let low = 0xef01;
+    let bits = 0xabcdef01;
+
+    let complex =
+        ComplexStruct { part_1: Part1 { high, low }, part_2: Part2 { bits }, part_3: bits };
+    assert_eq!(unsafe { pass_struct_complex(complex, high, low, bits) }, 0);
+}
diff --git a/src/tools/miri/tests/native-lib/pass/ptr_read_access.rs b/src/tools/miri/tests/native-lib/pass/ptr_read_access.rs
index 4f3c37f00c1..bab73f7cf17 100644
--- a/src/tools/miri/tests/native-lib/pass/ptr_read_access.rs
+++ b/src/tools/miri/tests/native-lib/pass/ptr_read_access.rs
@@ -1,6 +1,7 @@
 //@revisions: trace notrace
 //@[trace] only-target: x86_64-unknown-linux-gnu i686-unknown-linux-gnu
 //@[trace] compile-flags: -Zmiri-native-lib-enable-tracing
+//@compile-flags: -Zmiri-permissive-provenance
 
 fn main() {
     test_access_pointer();
diff --git a/src/tools/miri/tests/panic/oob_subslice.stderr b/src/tools/miri/tests/panic/oob_subslice.stderr
index f8270f4ad4d..e1e5bd33d31 100644
--- a/src/tools/miri/tests/panic/oob_subslice.stderr
+++ b/src/tools/miri/tests/panic/oob_subslice.stderr
@@ -1,5 +1,5 @@
 
 thread 'main' ($TID) panicked at tests/panic/oob_subslice.rs:LL:CC:
-range end index 5 out of range for slice of length 4
+range end index 4 out of range for slice of length 4
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
 note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
diff --git a/src/tools/miri/tests/panic/transmute_fat2.rs b/src/tools/miri/tests/panic/transmute_fat2.rs
index e695ff2d57b..7441f25d03e 100644
--- a/src/tools/miri/tests/panic/transmute_fat2.rs
+++ b/src/tools/miri/tests/panic/transmute_fat2.rs
@@ -1,3 +1,5 @@
+#![allow(integer_to_ptr_transmutes)]
+
 fn main() {
     #[cfg(all(target_endian = "little", target_pointer_width = "64"))]
     let bad = unsafe { std::mem::transmute::<u128, &[u8]>(42) };
diff --git a/src/tools/miri/tests/pass-dep/shims/windows-fs.rs b/src/tools/miri/tests/pass-dep/shims/windows-fs.rs
index 4ca19046b67..7b756603d92 100644
--- a/src/tools/miri/tests/pass-dep/shims/windows-fs.rs
+++ b/src/tools/miri/tests/pass-dep/shims/windows-fs.rs
@@ -2,20 +2,20 @@
 //@compile-flags: -Zmiri-disable-isolation
 #![allow(nonstandard_style)]
 
-use std::io::{ErrorKind, Read, Write};
+use std::io::{ErrorKind, Read, Seek, SeekFrom, Write};
 use std::os::windows::ffi::OsStrExt;
-use std::os::windows::io::AsRawHandle;
+use std::os::windows::io::{AsRawHandle, FromRawHandle};
 use std::path::Path;
-use std::{fs, ptr};
+use std::{fs, mem, ptr};
 
 #[path = "../../utils/mod.rs"]
 mod utils;
 
 use windows_sys::Wdk::Storage::FileSystem::{NtReadFile, NtWriteFile};
 use windows_sys::Win32::Foundation::{
-    CloseHandle, ERROR_ACCESS_DENIED, ERROR_ALREADY_EXISTS, ERROR_IO_DEVICE, GENERIC_READ,
-    GENERIC_WRITE, GetLastError, RtlNtStatusToDosError, STATUS_ACCESS_DENIED,
-    STATUS_IO_DEVICE_ERROR, STATUS_SUCCESS, SetLastError,
+    CloseHandle, DUPLICATE_SAME_ACCESS, DuplicateHandle, ERROR_ACCESS_DENIED, ERROR_ALREADY_EXISTS,
+    ERROR_IO_DEVICE, FALSE, GENERIC_READ, GENERIC_WRITE, GetLastError, RtlNtStatusToDosError,
+    STATUS_ACCESS_DENIED, STATUS_IO_DEVICE_ERROR, STATUS_SUCCESS, SetLastError,
 };
 use windows_sys::Win32::Storage::FileSystem::{
     BY_HANDLE_FILE_INFORMATION, CREATE_ALWAYS, CREATE_NEW, CreateFileW, DeleteFileW,
@@ -24,6 +24,7 @@ use windows_sys::Win32::Storage::FileSystem::{
     FILE_SHARE_WRITE, GetFileInformationByHandle, OPEN_ALWAYS, OPEN_EXISTING, SetFilePointerEx,
 };
 use windows_sys::Win32::System::IO::IO_STATUS_BLOCK;
+use windows_sys::Win32::System::Threading::GetCurrentProcess;
 
 fn main() {
     unsafe {
@@ -36,6 +37,7 @@ fn main() {
         test_ntstatus_to_dos();
         test_file_read_write();
         test_file_seek();
+        test_dup_handle();
     }
 }
 
@@ -273,6 +275,39 @@ unsafe fn test_file_read_write() {
     assert_eq!(GetLastError(), 1234);
 }
 
+unsafe fn test_dup_handle() {
+    let temp = utils::tmp().join("test_dup.txt");
+
+    let mut file1 = fs::File::options().read(true).write(true).create(true).open(&temp).unwrap();
+
+    file1.write_all(b"Hello, World!\n").unwrap();
+    file1.seek(SeekFrom::Start(0)).unwrap();
+
+    let first_handle = file1.as_raw_handle();
+
+    let cur_proc = GetCurrentProcess();
+    let mut second_handle = mem::zeroed();
+    let res = DuplicateHandle(
+        cur_proc,
+        first_handle,
+        cur_proc,
+        &mut second_handle,
+        0,
+        FALSE,
+        DUPLICATE_SAME_ACCESS,
+    );
+    assert!(res != 0);
+
+    let mut buf1 = [0; 5];
+    file1.read(&mut buf1).unwrap();
+    assert_eq!(&buf1, b"Hello");
+
+    let mut file2 = fs::File::from_raw_handle(second_handle);
+    let mut buf2 = [0; 5];
+    file2.read(&mut buf2).unwrap();
+    assert_eq!(&buf2, b", Wor");
+}
+
 unsafe fn test_file_seek() {
     let temp = utils::tmp().join("test_file_seek.txt");
     let mut file = fs::File::options().create(true).write(true).read(true).open(&temp).unwrap();
diff --git a/src/tools/miri/tests/pass/0weak_memory_consistency.rs b/src/tools/miri/tests/pass/0weak_memory/consistency.rs
index e4ed9675de8..16a38ebd9d4 100644
--- a/src/tools/miri/tests/pass/0weak_memory_consistency.rs
+++ b/src/tools/miri/tests/pass/0weak_memory/consistency.rs
@@ -1,6 +1,7 @@
-//@compile-flags: -Zmiri-ignore-leaks -Zmiri-disable-stacked-borrows -Zmiri-disable-validation -Zmiri-provenance-gc=10000
+//@compile-flags: -Zmiri-ignore-leaks -Zmiri-disable-stacked-borrows -Zmiri-disable-validation
 // This test's runtime explodes if the GC interval is set to 1 (which we do in CI), so we
 // override it internally back to the default frequency.
+//@compile-flags: -Zmiri-provenance-gc=10000
 
 // The following tests check whether our weak memory emulation produces
 // any inconsistent execution outcomes
@@ -14,13 +15,6 @@
 // To mitigate this, each test is ran enough times such that the chance
 // of spurious success is very low. These tests never spuriously fail.
 
-// Test cases and their consistent outcomes are from
-// http://svr-pes20-cppmem.cl.cam.ac.uk/cppmem/
-// Based on
-// M. Batty, S. Owens, S. Sarkar, P. Sewell and T. Weber,
-// "Mathematizing C++ concurrency", ACM SIGPLAN Notices, vol. 46, no. 1, pp. 55-66, 2011.
-// Available: https://ss265.host.cs.st-andrews.ac.uk/papers/n3132.pdf.
-
 use std::sync::atomic::Ordering::*;
 use std::sync::atomic::{AtomicBool, AtomicI32, Ordering, fence};
 use std::thread::spawn;
@@ -56,6 +50,8 @@ fn spin_until_bool(loc: &AtomicBool, ord: Ordering, val: bool) -> bool {
     val
 }
 
+/// Test matching https://www.doc.ic.ac.uk/~afd/homepages/papers/pdfs/2017/POPL.pdf, Figure 7.
+/// (The Figure 8 test is in `weak.rs`.)
 fn test_corr() {
     let x = static_atomic(0);
     let y = static_atomic(0);
@@ -75,19 +71,25 @@ fn test_corr() {
     let j3 = spawn(move || { //                      |                    |
         spin_until_i32(&y, Acquire, 1); // <---------+                    |
         x.load(Relaxed) // <----------------------------------------------+
-        // The two reads on x are ordered by hb, so they cannot observe values
-        // differently from the modification order. If the first read observed
-        // 2, then the second read must observe 2 as well.
     });
 
     j1.join().unwrap();
     let r2 = j2.join().unwrap();
     let r3 = j3.join().unwrap();
+    // The two reads on x are ordered by hb, so they cannot observe values
+    // differently from the modification order. If the first read observed
+    // 2, then the second read must observe 2 as well.
     if r2 == 2 {
         assert_eq!(r3, 2);
     }
 }
 
+/// This test case is from:
+/// http://svr-pes20-cppmem.cl.cam.ac.uk/cppmem/, "WRC"
+/// Based on
+/// M. Batty, S. Owens, S. Sarkar, P. Sewell and T. Weber,
+/// "Mathematizing C++ concurrency", ACM SIGPLAN Notices, vol. 46, no. 1, pp. 55-66, 2011.
+/// Available: https://www.cl.cam.ac.uk/~pes20/cpp/popl085ap-sewell.pdf.
 fn test_wrc() {
     let x = static_atomic(0);
     let y = static_atomic(0);
@@ -114,6 +116,8 @@ fn test_wrc() {
     assert_eq!(r3, 1);
 }
 
+/// Another test from http://svr-pes20-cppmem.cl.cam.ac.uk/cppmem/:
+/// MP (na_rel+acq_na)
 fn test_message_passing() {
     let mut var = 0u32;
     let ptr = &mut var as *mut u32;
@@ -139,7 +143,8 @@ fn test_message_passing() {
     assert_eq!(r2, 1);
 }
 
-// LB+acq_rel+acq_rel
+/// Another test from http://svr-pes20-cppmem.cl.cam.ac.uk/cppmem/:
+/// LB+acq_rel+acq_rel
 fn test_load_buffering_acq_rel() {
     let x = static_atomic(0);
     let y = static_atomic(0);
diff --git a/src/tools/miri/tests/pass/0weak_memory_consistency_sc.rs b/src/tools/miri/tests/pass/0weak_memory/consistency_sc.rs
index 937c2a8cf28..cb8535b8ad7 100644
--- a/src/tools/miri/tests/pass/0weak_memory_consistency_sc.rs
+++ b/src/tools/miri/tests/pass/0weak_memory/consistency_sc.rs
@@ -1,6 +1,7 @@
-//@compile-flags: -Zmiri-ignore-leaks -Zmiri-disable-stacked-borrows -Zmiri-disable-validation -Zmiri-provenance-gc=10000
+//@compile-flags: -Zmiri-ignore-leaks -Zmiri-disable-stacked-borrows -Zmiri-disable-validation
 // This test's runtime explodes if the GC interval is set to 1 (which we do in CI), so we
 // override it internally back to the default frequency.
+//@compile-flags: -Zmiri-provenance-gc=10000
 
 // The following tests check whether our weak memory emulation produces
 // any inconsistent execution outcomes
@@ -348,7 +349,7 @@ fn test_sc_relaxed() {
 }
 
 pub fn main() {
-    for _ in 0..50 {
+    for _ in 0..32 {
         test_sc_store_buffering();
         test_iriw_sc_rlx();
         test_cpp20_sc_fence_fix();
diff --git a/src/tools/miri/tests/pass/weak_memory/extra_cpp.rs b/src/tools/miri/tests/pass/0weak_memory/extra_cpp.rs
index 94df7308080..94df7308080 100644
--- a/src/tools/miri/tests/pass/weak_memory/extra_cpp.rs
+++ b/src/tools/miri/tests/pass/0weak_memory/extra_cpp.rs
diff --git a/src/tools/miri/tests/pass/0weak_memory/weak.rs b/src/tools/miri/tests/pass/0weak_memory/weak.rs
new file mode 100644
index 00000000000..611733d0dac
--- /dev/null
+++ b/src/tools/miri/tests/pass/0weak_memory/weak.rs
@@ -0,0 +1,238 @@
+//@compile-flags: -Zmiri-ignore-leaks -Zmiri-fixed-schedule
+// This test's runtime explodes if the GC interval is set to 1 (which we do in CI), so we
+// override it internally back to the default frequency.
+//@compile-flags: -Zmiri-provenance-gc=10000
+
+// Tests showing weak memory behaviours are exhibited, even with a fixed scheule.
+// We run all tests a number of times and then check that we see the desired list of outcomes.
+
+// Spurious failure is possible, if you are really unlucky with
+// the RNG and always read the latest value from the store buffer.
+
+use std::sync::atomic::Ordering::*;
+use std::sync::atomic::{AtomicUsize, fence};
+use std::thread::spawn;
+
+#[path = "../../utils/mod.rs"]
+mod utils;
+use utils::check_all_outcomes;
+
+#[allow(dead_code)]
+#[derive(Copy, Clone)]
+struct EvilSend<T>(pub T);
+
+unsafe impl<T> Send for EvilSend<T> {}
+unsafe impl<T> Sync for EvilSend<T> {}
+
+// We can't create static items because we need to run each test multiple times.
+fn static_atomic(val: usize) -> &'static AtomicUsize {
+    Box::leak(Box::new(AtomicUsize::new(val)))
+}
+
+// Spins until it reads the given value
+fn spin_until(loc: &AtomicUsize, val: usize) -> usize {
+    while loc.load(Relaxed) != val {
+        std::hint::spin_loop();
+    }
+    val
+}
+
+fn relaxed() {
+    check_all_outcomes([0, 1, 2], || {
+        let x = static_atomic(0);
+        let j1 = spawn(move || {
+            x.store(1, Relaxed);
+            // Preemption is disabled, so the store above will never be the
+            // latest store visible to another thread.
+            x.store(2, Relaxed);
+        });
+
+        let j2 = spawn(move || x.load(Relaxed));
+
+        j1.join().unwrap();
+        let r2 = j2.join().unwrap();
+
+        // There are three possible values here: 0 (from the initial read), 1 (from the first relaxed
+        // read), and 2 (the last read).
+        r2
+    });
+}
+
+// https://www.doc.ic.ac.uk/~afd/homepages/papers/pdfs/2017/POPL.pdf Figure 8
+fn seq_cst() {
+    check_all_outcomes([1, 3], || {
+        let x = static_atomic(0);
+
+        let j1 = spawn(move || {
+            x.store(1, Relaxed);
+        });
+
+        let j2 = spawn(move || {
+            x.store(2, SeqCst);
+            x.store(3, SeqCst);
+        });
+
+        let j3 = spawn(move || x.load(SeqCst));
+
+        j1.join().unwrap();
+        j2.join().unwrap();
+        let r3 = j3.join().unwrap();
+
+        // Even though we force t3 to run last, it can still see the value 1.
+        // And it can *never* see the value 2!
+        r3
+    });
+}
+
+fn initialization_write(add_fence: bool) {
+    check_all_outcomes([11, 22], || {
+        let x = static_atomic(11);
+
+        let wait = static_atomic(0);
+
+        let j1 = spawn(move || {
+            x.store(22, Relaxed);
+            // Relaxed is intentional. We want to test if the thread 2 reads the initialisation write
+            // after a relaxed write
+            wait.store(1, Relaxed);
+        });
+
+        let j2 = spawn(move || {
+            spin_until(wait, 1);
+            if add_fence {
+                fence(AcqRel);
+            }
+            x.load(Relaxed)
+        });
+
+        j1.join().unwrap();
+        let r2 = j2.join().unwrap();
+
+        r2
+    });
+}
+
+fn faa_replaced_by_load() {
+    check_all_outcomes([true, false], || {
+        // Example from https://github.com/llvm/llvm-project/issues/56450#issuecomment-1183695905
+        pub fn rdmw(storing: &AtomicUsize, sync: &AtomicUsize, loading: &AtomicUsize) -> usize {
+            storing.store(1, Relaxed);
+            fence(Release);
+            // sync.fetch_add(0, Relaxed);
+            sync.load(Relaxed);
+            fence(Acquire);
+            loading.load(Relaxed)
+        }
+
+        let x = static_atomic(0);
+        let y = static_atomic(0);
+        let z = static_atomic(0);
+
+        let t1 = spawn(move || rdmw(y, x, z));
+
+        let t2 = spawn(move || rdmw(z, x, y));
+
+        let a = t1.join().unwrap();
+        let b = t2.join().unwrap();
+        (a, b) == (0, 0)
+    });
+}
+
+/// Checking that the weaker release sequence example from
+/// <https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0982r0.html> can actually produce the
+/// new behavior (`Some(0)` in our version).
+fn weaker_release_sequences() {
+    check_all_outcomes([None, Some(0), Some(1)], || {
+        let x = static_atomic(0);
+        let y = static_atomic(0);
+
+        let t1 = spawn(move || {
+            x.store(2, Relaxed);
+        });
+        let t2 = spawn(move || {
+            y.store(1, Relaxed);
+            x.store(1, Release);
+            x.store(3, Relaxed);
+        });
+        let t3 = spawn(move || {
+            if x.load(Acquire) == 3 {
+                // In C++11, if we read the 3 here, and if the store of 1 was just before the store
+                // of 3 in mo order (which it is because we fix the schedule), this forms a release
+                // sequence, meaning we acquire the release store of 1, and we can thus never see
+                // the value 0.
+                // In C++20, this is no longer a release sequence, so 0 can now be observed.
+                Some(y.load(Relaxed))
+            } else {
+                None
+            }
+        });
+
+        t1.join().unwrap();
+        t2.join().unwrap();
+        t3.join().unwrap()
+    });
+}
+
+/// Ensuring normal release sequences (with RMWs) still work correctly.
+fn release_sequence() {
+    check_all_outcomes([None, Some(1)], || {
+        let x = static_atomic(0);
+        let y = static_atomic(0);
+
+        let t1 = spawn(move || {
+            y.store(1, Relaxed);
+            x.store(1, Release);
+            x.swap(3, Relaxed);
+        });
+        let t2 = spawn(move || {
+            if x.load(Acquire) == 3 {
+                // If we read 3 here, we are seeing the result of the `x.swap` above, which was
+                // relaxed but forms a release sequence with the `x.store`. This means there is a
+                // release sequence, so we acquire the `y.store` and cannot see the original value
+                // `0` any more.
+                Some(y.load(Relaxed))
+            } else {
+                None
+            }
+        });
+
+        t1.join().unwrap();
+        t2.join().unwrap()
+    });
+}
+
+/// Ensure that when we read from an outdated release store, we acquire its clock.
+fn old_release_store() {
+    check_all_outcomes([None, Some(1)], || {
+        let x = static_atomic(0);
+        let y = static_atomic(0);
+
+        let t1 = spawn(move || {
+            y.store(1, Relaxed);
+            x.store(1, Release); // this is what we want to read from
+            x.store(3, Relaxed);
+        });
+        let t2 = spawn(move || {
+            if x.load(Acquire) == 1 {
+                // We must have acquired the `y.store` so we cannot see the initial value any more.
+                Some(y.load(Relaxed))
+            } else {
+                None
+            }
+        });
+
+        t1.join().unwrap();
+        t2.join().unwrap()
+    });
+}
+
+pub fn main() {
+    relaxed();
+    seq_cst();
+    initialization_write(false);
+    initialization_write(true);
+    faa_replaced_by_load();
+    release_sequence();
+    weaker_release_sequences();
+    old_release_store();
+}
diff --git a/src/tools/miri/tests/pass/atomic.rs b/src/tools/miri/tests/pass/atomic.rs
index 3de34e570c7..d8ac5114f27 100644
--- a/src/tools/miri/tests/pass/atomic.rs
+++ b/src/tools/miri/tests/pass/atomic.rs
@@ -2,7 +2,6 @@
 //@[tree]compile-flags: -Zmiri-tree-borrows
 //@compile-flags: -Zmiri-strict-provenance
 
-#![feature(strict_provenance_atomic_ptr)]
 // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint
 #![allow(static_mut_refs)]
 
diff --git a/src/tools/miri/tests/pass/binops.rs b/src/tools/miri/tests/pass/binops.rs
index 0aff7acb29d..fcbe6c85b7b 100644
--- a/src/tools/miri/tests/pass/binops.rs
+++ b/src/tools/miri/tests/pass/binops.rs
@@ -32,6 +32,7 @@ fn test_bool() {
     assert_eq!(true ^ true, false);
 }
 
+#[allow(integer_to_ptr_transmutes)]
 fn test_ptr() {
     unsafe {
         let p1: *const u8 = ::std::mem::transmute(0_usize);
diff --git a/src/tools/miri/tests/pass/both_borrows/basic_aliasing_model.rs b/src/tools/miri/tests/pass/both_borrows/basic_aliasing_model.rs
index 6a625e597df..115e232dde4 100644
--- a/src/tools/miri/tests/pass/both_borrows/basic_aliasing_model.rs
+++ b/src/tools/miri/tests/pass/both_borrows/basic_aliasing_model.rs
@@ -1,6 +1,7 @@
 //@revisions: stack tree
 //@[tree]compile-flags: -Zmiri-tree-borrows
 #![feature(allocator_api)]
+use std::alloc::{Layout, alloc, dealloc};
 use std::cell::Cell;
 use std::ptr;
 
@@ -23,7 +24,8 @@ fn main() {
     not_unpin_not_protected();
     write_does_not_invalidate_all_aliases();
     box_into_raw_allows_interior_mutable_alias();
-    cell_inside_struct()
+    cell_inside_struct();
+    zst();
 }
 
 // Make sure that reading from an `&mut` does, like reborrowing to `&`,
@@ -287,3 +289,31 @@ fn cell_inside_struct() {
     // Writing to `field1`, which is reserved, should also be allowed.
     (*a).field1 = 88;
 }
+
+/// ZST reborrows on various kinds of dangling pointers are valid.
+fn zst() {
+    unsafe {
+        // Integer pointer.
+        let ptr = ptr::without_provenance_mut::<()>(15);
+        let _ref = &mut *ptr;
+
+        // Out-of-bounds pointer.
+        let mut b = Box::new(0u8);
+        let ptr = (&raw mut *b).wrapping_add(15) as *mut ();
+        let _ref = &mut *ptr;
+
+        // Deallocated pointer.
+        let ptr = &raw mut *b as *mut ();
+        drop(b);
+        let _ref = &mut *ptr;
+
+        // zero-sized protectors do not affect deallocation
+        fn with_protector(_x: &mut (), ptr: *mut u8, l: Layout) {
+            // `_x` here is strongly protected but covers zero bytes.
+            unsafe { dealloc(ptr, l) };
+        }
+        let l = Layout::from_size_align(1, 1).unwrap();
+        let ptr = alloc(l);
+        with_protector(&mut *ptr.cast::<()>(), ptr, l);
+    }
+}
diff --git a/src/tools/miri/tests/pass/btreemap.rs b/src/tools/miri/tests/pass/btreemap.rs
index 1d65e69bf72..7af6d7b5551 100644
--- a/src/tools/miri/tests/pass/btreemap.rs
+++ b/src/tools/miri/tests/pass/btreemap.rs
@@ -1,7 +1,6 @@
 //@revisions: stack tree
 //@[tree]compile-flags: -Zmiri-tree-borrows
 //@compile-flags: -Zmiri-strict-provenance
-#![feature(btree_extract_if)]
 use std::collections::{BTreeMap, BTreeSet};
 use std::mem;
 
diff --git a/src/tools/miri/tests/pass/concurrency/sync.rs b/src/tools/miri/tests/pass/concurrency/sync.rs
index a92359758da..142ac9cc8ca 100644
--- a/src/tools/miri/tests/pass/concurrency/sync.rs
+++ b/src/tools/miri/tests/pass/concurrency/sync.rs
@@ -9,7 +9,8 @@ use std::time::{Duration, Instant};
 
 // We are expecting to sleep for 10ms. How long of a sleep we are accepting?
 // Even with 1000ms we still see this test fail on macOS runners.
-const MAX_SLEEP_TIME_MS: u64 = 2000;
+// On a aarch64-pc-windows-msvc runner, we saw 2.7s!
+const MAX_SLEEP_TIME_MS: u64 = 4000;
 
 // Check if Rust barriers are working.
 
diff --git a/src/tools/miri/tests/pass/float.rs b/src/tools/miri/tests/pass/float.rs
index fe7316c6680..67a14c2b389 100644
--- a/src/tools/miri/tests/pass/float.rs
+++ b/src/tools/miri/tests/pass/float.rs
@@ -8,12 +8,16 @@
 #![allow(internal_features)]
 #![allow(unnecessary_transmutes)]
 
+#[path = "../utils/mod.rs"]
+mod utils;
 use std::any::type_name;
 use std::cmp::min;
 use std::fmt::{Debug, Display, LowerHex};
 use std::hint::black_box;
 use std::{f32, f64};
 
+use utils::check_nondet;
+
 /// Compare the two floats, allowing for $ulp many ULPs of error.
 ///
 /// ULP means "Units in the Last Place" or "Units of Least Precision".
@@ -39,9 +43,8 @@ macro_rules! assert_approx_eq {
     }};
 
     ($a:expr, $b: expr) => {
-        // accept up to 12ULP (4ULP for host floats and 4ULP for miri artificial error and 4 for any additional effects
-        // due to having multiple error sources.
-        assert_approx_eq!($a, $b, 12);
+        // accept up to 8ULP (4ULP for host floats and 4ULP for miri artificial error).
+        assert_approx_eq!($a, $b, 8);
     };
 }
 
@@ -176,6 +179,7 @@ fn assert_eq_msg<T: PartialEq + Debug>(x: T, y: T, msg: impl Display) {
 }
 
 /// Check that floats have bitwise equality
+#[track_caller]
 fn assert_biteq<F: Float>(a: F, b: F, msg: impl Display) {
     let ab = a.to_bits();
     let bb = b.to_bits();
@@ -189,6 +193,7 @@ fn assert_biteq<F: Float>(a: F, b: F, msg: impl Display) {
 }
 
 /// Check that two floats have equality
+#[track_caller]
 fn assert_feq<F: Float>(a: F, b: F, msg: impl Display) {
     let ab = a.to_bits();
     let bb = b.to_bits();
@@ -280,6 +285,35 @@ fn basic() {
     assert_eq!(34.2f64.abs(), 34.2f64);
     assert_eq!((-1.0f128).abs(), 1.0f128);
     assert_eq!(34.2f128.abs(), 34.2f128);
+
+    assert_eq!(64_f16.sqrt(), 8_f16);
+    assert_eq!(64_f32.sqrt(), 8_f32);
+    assert_eq!(64_f64.sqrt(), 8_f64);
+    assert_eq!(64_f128.sqrt(), 8_f128);
+    assert_eq!(f16::INFINITY.sqrt(), f16::INFINITY);
+    assert_eq!(f32::INFINITY.sqrt(), f32::INFINITY);
+    assert_eq!(f64::INFINITY.sqrt(), f64::INFINITY);
+    assert_eq!(f128::INFINITY.sqrt(), f128::INFINITY);
+    assert_eq!(0.0_f16.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_eq!(0.0_f128.sqrt().total_cmp(&0.0), std::cmp::Ordering::Equal);
+    assert_eq!((-0.0_f16).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_eq!((-0.0_f128).sqrt().total_cmp(&-0.0), std::cmp::Ordering::Equal);
+    assert!((-5.0_f16).sqrt().is_nan());
+    assert!((-5.0_f32).sqrt().is_nan());
+    assert!((-5.0_f64).sqrt().is_nan());
+    assert!((-5.0_f128).sqrt().is_nan());
+    assert!(f16::NEG_INFINITY.sqrt().is_nan());
+    assert!(f32::NEG_INFINITY.sqrt().is_nan());
+    assert!(f64::NEG_INFINITY.sqrt().is_nan());
+    assert!(f128::NEG_INFINITY.sqrt().is_nan());
+    assert!(f16::NAN.sqrt().is_nan());
+    assert!(f32::NAN.sqrt().is_nan());
+    assert!(f64::NAN.sqrt().is_nan());
+    assert!(f128::NAN.sqrt().is_nan());
 }
 
 /// Test casts from floats to ints and back
@@ -1011,21 +1045,6 @@ pub fn libm() {
         unsafe { ldexp(a, b) }
     }
 
-    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);
 
@@ -1051,6 +1070,10 @@ pub fn libm() {
     assert_eq!(42f64.powf(0.0), 1.0);
     assert_eq!(f32::INFINITY.powf(0.0), 1.0);
     assert_eq!(f64::INFINITY.powf(0.0), 1.0);
+    assert_eq!(f32::NEG_INFINITY.powi(3), f32::NEG_INFINITY);
+    assert_eq!(f32::NEG_INFINITY.powi(2), f32::INFINITY);
+    assert_eq!(f64::INFINITY.powi(3), f64::INFINITY);
+    assert_eq!(f64::INFINITY.powi(2), f64::INFINITY);
 
     // f*::NAN is a quiet NAN and should return 1 as well.
     assert_eq!(f32::NAN.powi(0), 1.0);
@@ -1088,6 +1111,8 @@ pub fn libm() {
 
     assert_approx_eq!(1f32.exp_m1(), f32::consts::E - 1.0);
     assert_approx_eq!(1f64.exp_m1(), f64::consts::E - 1.0);
+    assert_approx_eq!(f32::NEG_INFINITY.exp_m1(), -1.0);
+    assert_approx_eq!(f64::NEG_INFINITY.exp_m1(), -1.0);
 
     assert_approx_eq!(10f32.exp2(), 1024f32);
     assert_approx_eq!(50f64.exp2(), 1125899906842624f64);
@@ -1123,6 +1148,7 @@ pub fn libm() {
     assert_eq!(ldexp(0.65f64, 3i32), 5.2f64);
     assert_eq!(ldexp(1.42, 0xFFFF), f64::INFINITY);
     assert_eq!(ldexp(1.42, -0xFFFF), 0f64);
+    assert_eq!(ldexp(42.0, 0), 42.0);
 
     // Trigonometric functions.
 
@@ -1131,8 +1157,13 @@ pub fn libm() {
     assert_approx_eq!((f64::consts::PI / 2f64).sin(), 1f64);
     assert_approx_eq!(f32::consts::FRAC_PI_6.sin(), 0.5);
     assert_approx_eq!(f64::consts::FRAC_PI_6.sin(), 0.5);
-    assert_approx_eq!(f32::consts::FRAC_PI_4.sin().asin(), f32::consts::FRAC_PI_4);
-    assert_approx_eq!(f64::consts::FRAC_PI_4.sin().asin(), f64::consts::FRAC_PI_4);
+    // Increase error tolerance to 16ULP because of the extra operation.
+    assert_approx_eq!(f32::consts::FRAC_PI_4.sin().asin(), f32::consts::FRAC_PI_4, 16);
+    assert_approx_eq!(f64::consts::FRAC_PI_4.sin().asin(), f64::consts::FRAC_PI_4, 16);
+    assert_biteq(0.0f32.asin(), 0.0f32, "asin(+0) = +0");
+    assert_biteq((-0.0f32).asin(), -0.0, "asin(-0) = -0");
+    assert_biteq(0.0f64.asin(), 0.0, "asin(+0) = +0");
+    assert_biteq((-0.0f64).asin(), -0.0, "asin(-0) = -0");
 
     assert_approx_eq!(1.0f32.sinh(), 1.1752012f32);
     assert_approx_eq!(1.0f64.sinh(), 1.1752011936438014f64);
@@ -1159,11 +1190,18 @@ pub fn libm() {
     assert_approx_eq!((f64::consts::PI * 2f64).cos(), 1f64);
     assert_approx_eq!(f32::consts::FRAC_PI_3.cos(), 0.5);
     assert_approx_eq!(f64::consts::FRAC_PI_3.cos(), 0.5);
-    assert_approx_eq!(f32::consts::FRAC_PI_4.cos().acos(), f32::consts::FRAC_PI_4);
-    assert_approx_eq!(f64::consts::FRAC_PI_4.cos().acos(), f64::consts::FRAC_PI_4);
+    // Increase error tolerance to 16ULP because of the extra operation.
+    assert_approx_eq!(f32::consts::FRAC_PI_4.cos().acos(), f32::consts::FRAC_PI_4, 16);
+    assert_approx_eq!(f64::consts::FRAC_PI_4.cos().acos(), f64::consts::FRAC_PI_4, 16);
+    assert_biteq(1.0f32.acos(), 0.0, "acos(1) = 0");
+    assert_biteq(1.0f64.acos(), 0.0, "acos(1) = 0");
 
-    assert_approx_eq!(1.0f32.cosh(), 1.54308f32);
+    assert_approx_eq!(1.0f32.cosh(), 1.5430806f32);
     assert_approx_eq!(1.0f64.cosh(), 1.5430806348152437f64);
+    assert_eq!(0.0f32.cosh(), 1.0);
+    assert_eq!(0.0f64.cosh(), 1.0);
+    assert_eq!((-0.0f32).cosh(), 1.0);
+    assert_eq!((-0.0f64).cosh(), 1.0);
     assert_approx_eq!(2.0f32.acosh(), 1.31695789692481670862504634730796844f32);
     assert_approx_eq!(3.0f64.acosh(), 1.76274717403908605046521864995958461f64);
 
@@ -1173,6 +1211,47 @@ pub fn libm() {
     assert_approx_eq!(1.0_f64, 1.0_f64.tan().atan());
     assert_approx_eq!(1.0f32.atan2(2.0f32), 0.46364761f32);
     assert_approx_eq!(1.0f32.atan2(2.0f32), 0.46364761f32);
+    // C standard defines a bunch of fixed outputs for atan2
+    macro_rules! fixed_atan2_cases{
+        ($float_type:ident) => {{
+            use std::$float_type::consts::{PI, FRAC_PI_2, FRAC_PI_4};
+            use $float_type::{INFINITY, NEG_INFINITY};
+
+            // atan2(±0,−0) = ±π.
+            assert_eq!($float_type::atan2(0.0, -0.0), PI, "atan2(0,−0) = π");
+            assert_eq!($float_type::atan2(-0.0, -0.0), -PI, "atan2(-0,−0) = -π");
+
+            // atan2(±0, y) = ±π for y < 0.
+            assert_eq!($float_type::atan2(0.0, -1.0), PI, "atan2(0, y) = π for y < 0.");
+            assert_eq!($float_type::atan2(-0.0, -1.0), -PI, "atan2(-0, y) = -π for y < 0.");
+
+            // atan2(x, ±0) = −π/2 for x < 0.
+            assert_eq!($float_type::atan2(-1.0, 0.0), -FRAC_PI_2, "atan2(x, 0) = −π/2 for x < 0");
+            assert_eq!($float_type::atan2(-1.0, -0.0), -FRAC_PI_2, "atan2(x, -0) = −π/2 for x < 0");
+
+            // atan2(x, ±0) =  π/2 for x > 0.
+            assert_eq!($float_type::atan2(1.0, 0.0), FRAC_PI_2, "atan2(x, 0) =  π/2 for x > 0.");
+            assert_eq!($float_type::atan2(1.0, -0.0), FRAC_PI_2, "atan2(x, -0) =  π/2 for x > 0.");
+
+            // atan2(±x,−∞) = ±π for finite x > 0.
+            assert_eq!($float_type::atan2(1.0, NEG_INFINITY), PI, "atan2(x, −∞) = π for finite x > 0");
+            assert_eq!($float_type::atan2(-1.0, NEG_INFINITY), -PI, "atan2(-x, −∞) = -π for finite x > 0");
+
+            // atan2(±∞, y) returns ±π/2 for finite y.
+            assert_eq!($float_type::atan2(INFINITY, 1.0), FRAC_PI_2, "atan2(+∞, y) returns π/2 for finite y");
+            assert_eq!($float_type::atan2(NEG_INFINITY, 1.0), -FRAC_PI_2, "atan2(-∞, y) returns -π/2 for finite y");
+
+            // atan2(±∞, −∞) = ±3π/4
+            assert_eq!($float_type::atan2(INFINITY, NEG_INFINITY), 3.0 * FRAC_PI_4, "atan2(+∞, −∞) = 3π/4");
+            assert_eq!($float_type::atan2(NEG_INFINITY, NEG_INFINITY), -3.0 * FRAC_PI_4, "atan2(-∞, −∞) = -3π/4");
+
+            // atan2(±∞, +∞) = ±π/4
+            assert_eq!($float_type::atan2(INFINITY, INFINITY), FRAC_PI_4, "atan2(+∞, +∞) = π/4");
+            assert_eq!($float_type::atan2(NEG_INFINITY, INFINITY), -FRAC_PI_4, "atan2(-∞, +∞) = -π/4");
+        }}
+    }
+    fixed_atan2_cases!(f32);
+    fixed_atan2_cases!(f64);
 
     assert_approx_eq!(
         1.0f32.tanh(),
@@ -1182,6 +1261,11 @@ pub fn libm() {
         1.0f64.tanh(),
         (1.0 - f64::consts::E.powi(-2)) / (1.0 + f64::consts::E.powi(-2))
     );
+    assert_eq!(f32::INFINITY.tanh(), 1.0);
+    assert_eq!(f32::NEG_INFINITY.tanh(), -1.0);
+    assert_eq!(f64::INFINITY.tanh(), 1.0);
+    assert_eq!(f64::NEG_INFINITY.tanh(), -1.0);
+
     assert_approx_eq!(0.5f32.atanh(), 0.54930614433405484569762261846126285f32);
     assert_approx_eq!(0.5f64.atanh(), 0.54930614433405484569762261846126285f64);
 
@@ -1202,8 +1286,14 @@ pub fn libm() {
 
     assert_approx_eq!(1.0f32.erf(), 0.84270079294971486934122063508260926f32);
     assert_approx_eq!(1.0f64.erf(), 0.84270079294971486934122063508260926f64);
+    assert_eq!(f32::INFINITY.erf(), 1.0);
+    assert_eq!(f64::INFINITY.erf(), 1.0);
     assert_approx_eq!(1.0f32.erfc(), 0.15729920705028513065877936491739074f32);
     assert_approx_eq!(1.0f64.erfc(), 0.15729920705028513065877936491739074f64);
+    assert_eq!(f32::NEG_INFINITY.erfc(), 2.0);
+    assert_eq!(f64::NEG_INFINITY.erfc(), 2.0);
+    assert_eq!(f32::INFINITY.erfc(), 0.0);
+    assert_eq!(f64::INFINITY.erfc(), 0.0);
 }
 
 fn test_fast() {
@@ -1329,12 +1419,12 @@ fn test_fmuladd() {
 
     #[inline(never)]
     pub fn test_operations_f32(a: f32, b: f32, c: f32) {
-        assert_approx_eq!(unsafe { fmuladdf32(a, b, c) }, a * b + c);
+        assert_approx_eq!(fmuladdf32(a, b, c), a * b + c);
     }
 
     #[inline(never)]
     pub fn test_operations_f64(a: f64, b: f64, c: f64) {
-        assert_approx_eq!(unsafe { fmuladdf64(a, b, c) }, a * b + c);
+        assert_approx_eq!(fmuladdf64(a, b, c), a * b + c);
     }
 
     test_operations_f32(0.1, 0.2, 0.3);
@@ -1343,29 +1433,14 @@ fn test_fmuladd() {
 
 /// `min` and `max` on equal arguments are non-deterministic.
 fn test_min_max_nondet() {
-    /// Ensure that if we call the closure often enough, we see both `true` and `false.`
-    #[track_caller]
-    fn ensure_both(f: impl Fn() -> bool) {
-        let rounds = 32;
-        let first = f();
-        for _ in 1..rounds {
-            if f() != first {
-                // We saw two different values!
-                return;
-            }
-        }
-        // We saw the same thing N times.
-        panic!("expected non-determinism, got {rounds} times the same result: {first:?}");
-    }
-
-    ensure_both(|| f16::min(0.0, -0.0).is_sign_positive());
-    ensure_both(|| f16::max(0.0, -0.0).is_sign_positive());
-    ensure_both(|| f32::min(0.0, -0.0).is_sign_positive());
-    ensure_both(|| f32::max(0.0, -0.0).is_sign_positive());
-    ensure_both(|| f64::min(0.0, -0.0).is_sign_positive());
-    ensure_both(|| f64::max(0.0, -0.0).is_sign_positive());
-    ensure_both(|| f128::min(0.0, -0.0).is_sign_positive());
-    ensure_both(|| f128::max(0.0, -0.0).is_sign_positive());
+    check_nondet(|| f16::min(0.0, -0.0).is_sign_positive());
+    check_nondet(|| f16::max(0.0, -0.0).is_sign_positive());
+    check_nondet(|| f32::min(0.0, -0.0).is_sign_positive());
+    check_nondet(|| f32::max(0.0, -0.0).is_sign_positive());
+    check_nondet(|| f64::min(0.0, -0.0).is_sign_positive());
+    check_nondet(|| f64::max(0.0, -0.0).is_sign_positive());
+    check_nondet(|| f128::min(0.0, -0.0).is_sign_positive());
+    check_nondet(|| f128::max(0.0, -0.0).is_sign_positive());
 }
 
 fn test_non_determinism() {
@@ -1375,35 +1450,20 @@ fn test_non_determinism() {
     };
     use std::{f32, f64};
 
-    /// Ensure that the operation is non-deterministic
-    #[track_caller]
-    fn ensure_nondet<T: PartialEq + std::fmt::Debug>(f: impl Fn() -> T) {
-        let rounds = 16;
-        let first = f();
-        for _ in 1..rounds {
-            if f() != first {
-                // We saw two different values!
-                return;
-            }
-        }
-        // We saw the same thing N times.
-        panic!("expected non-determinism, got {rounds} times the same result: {first:?}");
-    }
-
     macro_rules! test_operations_f {
         ($a:expr, $b:expr) => {
-            ensure_nondet(|| fadd_algebraic($a, $b));
-            ensure_nondet(|| fsub_algebraic($a, $b));
-            ensure_nondet(|| fmul_algebraic($a, $b));
-            ensure_nondet(|| fdiv_algebraic($a, $b));
-            ensure_nondet(|| frem_algebraic($a, $b));
+            check_nondet(|| fadd_algebraic($a, $b));
+            check_nondet(|| fsub_algebraic($a, $b));
+            check_nondet(|| fmul_algebraic($a, $b));
+            check_nondet(|| fdiv_algebraic($a, $b));
+            check_nondet(|| frem_algebraic($a, $b));
 
             unsafe {
-                ensure_nondet(|| fadd_fast($a, $b));
-                ensure_nondet(|| fsub_fast($a, $b));
-                ensure_nondet(|| fmul_fast($a, $b));
-                ensure_nondet(|| fdiv_fast($a, $b));
-                ensure_nondet(|| frem_fast($a, $b));
+                check_nondet(|| fadd_fast($a, $b));
+                check_nondet(|| fsub_fast($a, $b));
+                check_nondet(|| fmul_fast($a, $b));
+                check_nondet(|| fdiv_fast($a, $b));
+                check_nondet(|| frem_fast($a, $b));
             }
         };
     }
@@ -1413,72 +1473,70 @@ fn test_non_determinism() {
     }
     pub fn test_operations_f32(a: f32, b: f32) {
         test_operations_f!(a, b);
-        // FIXME: some are temporarily disabled as it breaks std tests.
-        ensure_nondet(|| a.powf(b));
-        ensure_nondet(|| a.powi(2));
-        ensure_nondet(|| a.log(b));
-        ensure_nondet(|| a.exp());
-        ensure_nondet(|| 10f32.exp2());
-        ensure_nondet(|| f32::consts::E.ln());
-        ensure_nondet(|| 10f32.log10());
-        ensure_nondet(|| 8f32.log2());
-        // ensure_nondet(|| 1f32.ln_1p());
-        // ensure_nondet(|| 27.0f32.cbrt());
-        // ensure_nondet(|| 3.0f32.hypot(4.0f32));
-        ensure_nondet(|| 1f32.sin());
-        ensure_nondet(|| 1f32.cos());
+        check_nondet(|| a.powf(b));
+        check_nondet(|| a.powi(2));
+        check_nondet(|| a.log(b));
+        check_nondet(|| a.exp());
+        check_nondet(|| 10f32.exp2());
+        check_nondet(|| f32::consts::E.ln());
+        check_nondet(|| 10f32.log10());
+        check_nondet(|| 8f32.log2());
+        check_nondet(|| 1f32.ln_1p());
+        check_nondet(|| 27.0f32.cbrt());
+        check_nondet(|| 3.0f32.hypot(4.0f32));
+        check_nondet(|| 1f32.sin());
+        check_nondet(|| 1f32.cos());
         // On i686-pc-windows-msvc , these functions are implemented by calling the `f64` version,
         // which means the little rounding errors Miri introduces are discarded by the cast down to
         // `f32`. Just skip the test for them.
-        // if !cfg!(all(target_os = "windows", target_env = "msvc", target_arch = "x86")) {
-        //     ensure_nondet(|| 1.0f32.tan());
-        //     ensure_nondet(|| 1.0f32.asin());
-        //     ensure_nondet(|| 5.0f32.acos());
-        //     ensure_nondet(|| 1.0f32.atan());
-        //     ensure_nondet(|| 1.0f32.atan2(2.0f32));
-        //     ensure_nondet(|| 1.0f32.sinh());
-        //     ensure_nondet(|| 1.0f32.cosh());
-        //     ensure_nondet(|| 1.0f32.tanh());
-        // }
-        // ensure_nondet(|| 1.0f32.asinh());
-        // ensure_nondet(|| 2.0f32.acosh());
-        // ensure_nondet(|| 0.5f32.atanh());
-        // ensure_nondet(|| 5.0f32.gamma());
-        // ensure_nondet(|| 5.0f32.ln_gamma());
-        // ensure_nondet(|| 5.0f32.erf());
-        // ensure_nondet(|| 5.0f32.erfc());
+        if !cfg!(all(target_os = "windows", target_env = "msvc", target_arch = "x86")) {
+            check_nondet(|| 1.0f32.tan());
+            check_nondet(|| 1.0f32.asin());
+            check_nondet(|| 5.0f32.acos());
+            check_nondet(|| 1.0f32.atan());
+            check_nondet(|| 1.0f32.atan2(2.0f32));
+            check_nondet(|| 1.0f32.sinh());
+            check_nondet(|| 1.0f32.cosh());
+            check_nondet(|| 1.0f32.tanh());
+        }
+        check_nondet(|| 1.0f32.asinh());
+        check_nondet(|| 2.0f32.acosh());
+        check_nondet(|| 0.5f32.atanh());
+        check_nondet(|| 5.0f32.gamma());
+        check_nondet(|| 5.0f32.ln_gamma());
+        check_nondet(|| 5.0f32.erf());
+        check_nondet(|| 5.0f32.erfc());
     }
     pub fn test_operations_f64(a: f64, b: f64) {
         test_operations_f!(a, b);
-        // FIXME: some are temporarily disabled as it breaks std tests.
-        ensure_nondet(|| a.powf(b));
-        ensure_nondet(|| a.powi(2));
-        ensure_nondet(|| a.log(b));
-        ensure_nondet(|| a.exp());
-        ensure_nondet(|| 50f64.exp2());
-        ensure_nondet(|| 3f64.ln());
-        ensure_nondet(|| f64::consts::E.log10());
-        ensure_nondet(|| f64::consts::E.log2());
-        // ensure_nondet(|| 1f64.ln_1p());
-        // ensure_nondet(|| 27.0f64.cbrt());
-        // ensure_nondet(|| 3.0f64.hypot(4.0f64));
-        ensure_nondet(|| 1f64.sin());
-        ensure_nondet(|| 1f64.cos());
-        // ensure_nondet(|| 1.0f64.tan());
-        // ensure_nondet(|| 1.0f64.asin());
-        // ensure_nondet(|| 5.0f64.acos());
-        // ensure_nondet(|| 1.0f64.atan());
-        // ensure_nondet(|| 1.0f64.atan2(2.0f64));
-        // ensure_nondet(|| 1.0f64.sinh());
-        // ensure_nondet(|| 1.0f64.cosh());
-        // ensure_nondet(|| 1.0f64.tanh());
-        // ensure_nondet(|| 1.0f64.asinh());
-        // ensure_nondet(|| 3.0f64.acosh());
-        // ensure_nondet(|| 0.5f64.atanh());
-        // ensure_nondet(|| 5.0f64.gamma());
-        // ensure_nondet(|| 5.0f64.ln_gamma());
-        // ensure_nondet(|| 5.0f64.erf());
-        // ensure_nondet(|| 5.0f64.erfc());
+        check_nondet(|| a.powf(b));
+        check_nondet(|| a.powi(2));
+        check_nondet(|| a.log(b));
+        check_nondet(|| a.exp());
+        check_nondet(|| 50f64.exp2());
+        check_nondet(|| 3f64.ln());
+        check_nondet(|| f64::consts::E.log10());
+        check_nondet(|| f64::consts::E.log2());
+        check_nondet(|| 1f64.ln_1p());
+        check_nondet(|| 27.0f64.cbrt());
+        check_nondet(|| 3.0f64.hypot(4.0f64));
+        check_nondet(|| 1f64.sin());
+        check_nondet(|| 1f64.cos());
+        check_nondet(|| 1.0f64.tan());
+        check_nondet(|| 1.0f64.asin());
+        check_nondet(|| 5.0f64.acos());
+        check_nondet(|| 1.0f64.atan());
+        check_nondet(|| 1.0f64.atan2(2.0f64));
+        check_nondet(|| 1.0f64.sinh());
+        check_nondet(|| 1.0f64.cosh());
+        check_nondet(|| 1.0f64.tanh());
+        check_nondet(|| 1.0f64.asinh());
+        check_nondet(|| 3.0f64.acosh());
+        check_nondet(|| 0.5f64.atanh());
+        check_nondet(|| 5.0f64.gamma());
+        check_nondet(|| 5.0f64.ln_gamma());
+        check_nondet(|| 5.0f64.erf());
+        check_nondet(|| 5.0f64.erfc());
     }
     pub fn test_operations_f128(a: f128, b: f128) {
         test_operations_f!(a, b);
@@ -1490,15 +1548,15 @@ fn test_non_determinism() {
     test_operations_f128(25., 18.);
 
     // SNaN^0 = (1 | NaN)
-    ensure_nondet(|| f32::powf(SNAN_F32, 0.0).is_nan());
-    ensure_nondet(|| f64::powf(SNAN_F64, 0.0).is_nan());
+    check_nondet(|| f32::powf(SNAN_F32, 0.0).is_nan());
+    check_nondet(|| f64::powf(SNAN_F64, 0.0).is_nan());
 
     // 1^SNaN = (1 | NaN)
-    ensure_nondet(|| f32::powf(1.0, SNAN_F32).is_nan());
-    ensure_nondet(|| f64::powf(1.0, SNAN_F64).is_nan());
+    check_nondet(|| f32::powf(1.0, SNAN_F32).is_nan());
+    check_nondet(|| f64::powf(1.0, SNAN_F64).is_nan());
 
     // same as powf (keep it consistent):
     // x^SNaN = (1 | NaN)
-    ensure_nondet(|| f32::powi(SNAN_F32, 0).is_nan());
-    ensure_nondet(|| f64::powi(SNAN_F64, 0).is_nan());
+    check_nondet(|| f32::powi(SNAN_F32, 0).is_nan());
+    check_nondet(|| f64::powi(SNAN_F64, 0).is_nan());
 }
diff --git a/src/tools/miri/tests/pass/float_extra_rounding_error.rs b/src/tools/miri/tests/pass/float_extra_rounding_error.rs
new file mode 100644
index 00000000000..24d7cf2ccee
--- /dev/null
+++ b/src/tools/miri/tests/pass/float_extra_rounding_error.rs
@@ -0,0 +1,31 @@
+//! Check that the flags to control the extra rounding error work.
+//@revisions: random max none
+//@[max]compile-flags: -Zmiri-max-extra-rounding-error
+//@[none]compile-flags: -Zmiri-no-extra-rounding-error
+#![feature(cfg_select)]
+
+use std::collections::HashSet;
+use std::hint::black_box;
+
+fn main() {
+    let expected = cfg_select! {
+        random => 9, // -4 ..= +4 ULP error
+        max => 2,
+        none => 1,
+    };
+    // Call `sin(0.5)` a bunch of times and see how many different values we get.
+    let mut values = HashSet::new();
+    for _ in 0..(expected * 16) {
+        let val = black_box(0.5f64).sin();
+        values.insert(val.to_bits());
+    }
+    assert_eq!(values.len(), expected);
+
+    if !cfg!(none) {
+        // Ensure the smallest and biggest value are 8 ULP apart.
+        // We can just subtract the raw bit representations for this.
+        let min = *values.iter().min().unwrap();
+        let max = *values.iter().max().unwrap();
+        assert_eq!(min.abs_diff(max), 8);
+    }
+}
diff --git a/src/tools/miri/tests/pass/float_nan.rs b/src/tools/miri/tests/pass/float_nan.rs
index 3ffdb6868ac..c07ffdf9740 100644
--- a/src/tools/miri/tests/pass/float_nan.rs
+++ b/src/tools/miri/tests/pass/float_nan.rs
@@ -1,9 +1,14 @@
+// This test's runtime explodes if the GC interval is set to 1 (which we do in CI), so we
+// override it internally back to the default frequency.
+//@compile-flags: -Zmiri-provenance-gc=10000
 #![feature(float_gamma, portable_simd, core_intrinsics)]
-use std::collections::HashSet;
 use std::fmt;
-use std::hash::Hash;
 use std::hint::black_box;
 
+#[path = "../utils/mod.rs"]
+mod utils;
+use utils::check_all_outcomes;
+
 fn ldexp(a: f64, b: i32) -> f64 {
     extern "C" {
         fn ldexp(x: f64, n: i32) -> f64;
@@ -25,24 +30,6 @@ enum NaNKind {
 }
 use NaNKind::*;
 
-#[track_caller]
-fn check_all_outcomes<T: Eq + Hash + fmt::Display>(expected: HashSet<T>, generate: impl Fn() -> T) {
-    let mut seen = HashSet::new();
-    // Let's give it sixteen times as many tries as we are expecting values.
-    let tries = expected.len() * 16;
-    for _ in 0..tries {
-        let val = generate();
-        assert!(expected.contains(&val), "got an unexpected value: {val}");
-        seen.insert(val);
-    }
-    // Let's see if we saw them all.
-    for val in expected {
-        if !seen.contains(&val) {
-            panic!("did not get value that should be possible: {val}");
-        }
-    }
-}
-
 // -- f32 support
 #[repr(C)]
 #[derive(Copy, Clone, Eq, PartialEq, Hash)]
@@ -69,7 +56,7 @@ const F32_EXP: u32 = 8; // 8 bits of exponent
 const F32_MANTISSA: u32 = F32_SIGN_BIT - F32_EXP;
 const F32_NAN_PAYLOAD: u32 = F32_MANTISSA - 1;
 
-impl fmt::Display for F32 {
+impl fmt::Debug for F32 {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         // Alaways show raw bits.
         write!(f, "0x{:08x} ", self.0)?;
@@ -142,7 +129,7 @@ const F64_EXP: u32 = 11; // 11 bits of exponent
 const F64_MANTISSA: u32 = F64_SIGN_BIT - F64_EXP;
 const F64_NAN_PAYLOAD: u32 = F64_MANTISSA - 1;
 
-impl fmt::Display for F64 {
+impl fmt::Debug for F64 {
     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
         // Alaways show raw bits.
         write!(f, "0x{:08x} ", self.0)?;
@@ -193,51 +180,50 @@ impl F64 {
 
 fn test_f32() {
     // Freshly generated NaNs can have either sign.
-    check_all_outcomes(
-        HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]),
-        || F32::from(0.0 / black_box(0.0)),
-    );
+    check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || {
+        F32::from(0.0 / black_box(0.0))
+    });
     // When there are NaN inputs, their payload can be propagated, with any sign.
     let all1_payload = u32_ones(22);
     let all1 = F32::nan(Pos, Quiet, all1_payload).as_f32();
     check_all_outcomes(
-        HashSet::from_iter([
+        [
             F32::nan(Pos, Quiet, 0),
             F32::nan(Neg, Quiet, 0),
             F32::nan(Pos, Quiet, all1_payload),
             F32::nan(Neg, Quiet, all1_payload),
-        ]),
+        ],
         || F32::from(0.0 + all1),
     );
     // When there are two NaN inputs, the output can be either one, or the preferred NaN.
     let just1 = F32::nan(Neg, Quiet, 1).as_f32();
     check_all_outcomes(
-        HashSet::from_iter([
+        [
             F32::nan(Pos, Quiet, 0),
             F32::nan(Neg, Quiet, 0),
             F32::nan(Pos, Quiet, 1),
             F32::nan(Neg, Quiet, 1),
             F32::nan(Pos, Quiet, all1_payload),
             F32::nan(Neg, Quiet, all1_payload),
-        ]),
+        ],
         || F32::from(just1 - all1),
     );
     // When there are *signaling* NaN inputs, they might be quieted or not.
     let all1_snan = F32::nan(Pos, Signaling, all1_payload).as_f32();
     check_all_outcomes(
-        HashSet::from_iter([
+        [
             F32::nan(Pos, Quiet, 0),
             F32::nan(Neg, Quiet, 0),
             F32::nan(Pos, Quiet, all1_payload),
             F32::nan(Neg, Quiet, all1_payload),
             F32::nan(Pos, Signaling, all1_payload),
             F32::nan(Neg, Signaling, all1_payload),
-        ]),
+        ],
         || F32::from(0.0 * all1_snan),
     );
     // Mix signaling and non-signaling NaN.
     check_all_outcomes(
-        HashSet::from_iter([
+        [
             F32::nan(Pos, Quiet, 0),
             F32::nan(Neg, Quiet, 0),
             F32::nan(Pos, Quiet, 1),
@@ -246,35 +232,26 @@ fn test_f32() {
             F32::nan(Neg, Quiet, all1_payload),
             F32::nan(Pos, Signaling, all1_payload),
             F32::nan(Neg, Signaling, all1_payload),
-        ]),
+        ],
         || F32::from(just1 % all1_snan),
     );
 
     // Unary `-` must preserve payloads exactly.
-    check_all_outcomes(HashSet::from_iter([F32::nan(Neg, Quiet, all1_payload)]), || {
-        F32::from(-all1)
-    });
-    check_all_outcomes(HashSet::from_iter([F32::nan(Neg, Signaling, all1_payload)]), || {
-        F32::from(-all1_snan)
-    });
+    check_all_outcomes([F32::nan(Neg, Quiet, all1_payload)], || F32::from(-all1));
+    check_all_outcomes([F32::nan(Neg, Signaling, all1_payload)], || F32::from(-all1_snan));
 
     // Intrinsics
     let nan = F32::nan(Neg, Quiet, 0).as_f32();
     let snan = F32::nan(Neg, Signaling, 1).as_f32();
+    check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || {
+        F32::from(f32::min(nan, nan))
+    });
+    check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || {
+        F32::from(nan.floor())
+    });
+    check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || F32::from(nan.sin()));
     check_all_outcomes(
-        HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]),
-        || F32::from(f32::min(nan, nan)),
-    );
-    check_all_outcomes(
-        HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]),
-        || F32::from(nan.floor()),
-    );
-    check_all_outcomes(
-        HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]),
-        || F32::from(nan.sin()),
-    );
-    check_all_outcomes(
-        HashSet::from_iter([
+        [
             F32::nan(Pos, Quiet, 0),
             F32::nan(Neg, Quiet, 0),
             F32::nan(Pos, Quiet, 1),
@@ -285,37 +262,32 @@ fn test_f32() {
             F32::nan(Neg, Quiet, all1_payload),
             F32::nan(Pos, Signaling, all1_payload),
             F32::nan(Neg, Signaling, all1_payload),
-        ]),
+        ],
         || F32::from(just1.mul_add(F32::nan(Neg, Quiet, 2).as_f32(), all1_snan)),
     );
+    check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || {
+        F32::from(nan.powf(nan))
+    });
     check_all_outcomes(
-        HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]),
-        || F32::from(nan.powf(nan)),
-    );
-    check_all_outcomes(
-        HashSet::from_iter([1.0f32.into()]),
+        [1.0f32.into()],
         || F32::from(1.0f32.powf(nan)), // special `pow` rule
     );
-    check_all_outcomes(
-        HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]),
-        || F32::from(nan.powi(1)),
-    );
+    check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || {
+        F32::from(nan.powi(1))
+    });
 
     // libm functions
+    check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || {
+        F32::from(nan.sinh())
+    });
+    check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || {
+        F32::from(nan.atan2(nan))
+    });
+    check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || {
+        F32::from(nan.ln_gamma().0)
+    });
     check_all_outcomes(
-        HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]),
-        || F32::from(nan.sinh()),
-    );
-    check_all_outcomes(
-        HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]),
-        || F32::from(nan.atan2(nan)),
-    );
-    check_all_outcomes(
-        HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]),
-        || F32::from(nan.ln_gamma().0),
-    );
-    check_all_outcomes(
-        HashSet::from_iter([
+        [
             F32::from(1.0),
             F32::nan(Pos, Quiet, 0),
             F32::nan(Neg, Quiet, 0),
@@ -323,58 +295,57 @@ fn test_f32() {
             F32::nan(Neg, Quiet, 1),
             F32::nan(Pos, Signaling, 1),
             F32::nan(Neg, Signaling, 1),
-        ]),
+        ],
         || F32::from(snan.powf(0.0)),
     );
 }
 
 fn test_f64() {
     // Freshly generated NaNs can have either sign.
-    check_all_outcomes(
-        HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]),
-        || F64::from(0.0 / black_box(0.0)),
-    );
+    check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || {
+        F64::from(0.0 / black_box(0.0))
+    });
     // When there are NaN inputs, their payload can be propagated, with any sign.
     let all1_payload = u64_ones(51);
     let all1 = F64::nan(Pos, Quiet, all1_payload).as_f64();
     check_all_outcomes(
-        HashSet::from_iter([
+        [
             F64::nan(Pos, Quiet, 0),
             F64::nan(Neg, Quiet, 0),
             F64::nan(Pos, Quiet, all1_payload),
             F64::nan(Neg, Quiet, all1_payload),
-        ]),
+        ],
         || F64::from(0.0 + all1),
     );
     // When there are two NaN inputs, the output can be either one, or the preferred NaN.
     let just1 = F64::nan(Neg, Quiet, 1).as_f64();
     check_all_outcomes(
-        HashSet::from_iter([
+        [
             F64::nan(Pos, Quiet, 0),
             F64::nan(Neg, Quiet, 0),
             F64::nan(Pos, Quiet, 1),
             F64::nan(Neg, Quiet, 1),
             F64::nan(Pos, Quiet, all1_payload),
             F64::nan(Neg, Quiet, all1_payload),
-        ]),
+        ],
         || F64::from(just1 - all1),
     );
     // When there are *signaling* NaN inputs, they might be quieted or not.
     let all1_snan = F64::nan(Pos, Signaling, all1_payload).as_f64();
     check_all_outcomes(
-        HashSet::from_iter([
+        [
             F64::nan(Pos, Quiet, 0),
             F64::nan(Neg, Quiet, 0),
             F64::nan(Pos, Quiet, all1_payload),
             F64::nan(Neg, Quiet, all1_payload),
             F64::nan(Pos, Signaling, all1_payload),
             F64::nan(Neg, Signaling, all1_payload),
-        ]),
+        ],
         || F64::from(0.0 * all1_snan),
     );
     // Mix signaling and non-signaling NaN.
     check_all_outcomes(
-        HashSet::from_iter([
+        [
             F64::nan(Pos, Quiet, 0),
             F64::nan(Neg, Quiet, 0),
             F64::nan(Pos, Quiet, 1),
@@ -383,27 +354,22 @@ fn test_f64() {
             F64::nan(Neg, Quiet, all1_payload),
             F64::nan(Pos, Signaling, all1_payload),
             F64::nan(Neg, Signaling, all1_payload),
-        ]),
+        ],
         || F64::from(just1 % all1_snan),
     );
 
     // Intrinsics
     let nan = F64::nan(Neg, Quiet, 0).as_f64();
     let snan = F64::nan(Neg, Signaling, 1).as_f64();
+    check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || {
+        F64::from(f64::min(nan, nan))
+    });
+    check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || {
+        F64::from(nan.floor())
+    });
+    check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || F64::from(nan.sin()));
     check_all_outcomes(
-        HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]),
-        || F64::from(f64::min(nan, nan)),
-    );
-    check_all_outcomes(
-        HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]),
-        || F64::from(nan.floor()),
-    );
-    check_all_outcomes(
-        HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]),
-        || F64::from(nan.sin()),
-    );
-    check_all_outcomes(
-        HashSet::from_iter([
+        [
             F64::nan(Pos, Quiet, 0),
             F64::nan(Neg, Quiet, 0),
             F64::nan(Pos, Quiet, 1),
@@ -414,41 +380,35 @@ fn test_f64() {
             F64::nan(Neg, Quiet, all1_payload),
             F64::nan(Pos, Signaling, all1_payload),
             F64::nan(Neg, Signaling, all1_payload),
-        ]),
+        ],
         || F64::from(just1.mul_add(F64::nan(Neg, Quiet, 2).as_f64(), all1_snan)),
     );
+    check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || {
+        F64::from(nan.powf(nan))
+    });
     check_all_outcomes(
-        HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]),
-        || F64::from(nan.powf(nan)),
-    );
-    check_all_outcomes(
-        HashSet::from_iter([1.0f64.into()]),
+        [1.0f64.into()],
         || F64::from(1.0f64.powf(nan)), // special `pow` rule
     );
-    check_all_outcomes(
-        HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]),
-        || F64::from(nan.powi(1)),
-    );
+    check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || {
+        F64::from(nan.powi(1))
+    });
 
     // libm functions
+    check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || {
+        F64::from(nan.sinh())
+    });
+    check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || {
+        F64::from(nan.atan2(nan))
+    });
+    check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || {
+        F64::from(ldexp(nan, 1))
+    });
+    check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || {
+        F64::from(nan.ln_gamma().0)
+    });
     check_all_outcomes(
-        HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]),
-        || F64::from(nan.sinh()),
-    );
-    check_all_outcomes(
-        HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]),
-        || F64::from(nan.atan2(nan)),
-    );
-    check_all_outcomes(
-        HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]),
-        || F64::from(ldexp(nan, 1)),
-    );
-    check_all_outcomes(
-        HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]),
-        || F64::from(nan.ln_gamma().0),
-    );
-    check_all_outcomes(
-        HashSet::from_iter([
+        [
             F64::from(1.0),
             F64::nan(Pos, Quiet, 0),
             F64::nan(Neg, Quiet, 0),
@@ -456,7 +416,7 @@ fn test_f64() {
             F64::nan(Neg, Quiet, 1),
             F64::nan(Pos, Signaling, 1),
             F64::nan(Neg, Signaling, 1),
-        ]),
+        ],
         || F64::from(snan.powf(0.0)),
     );
 }
@@ -467,82 +427,79 @@ fn test_casts() {
     let left1_payload_64 = (all1_payload_32 as u64) << (51 - 22);
 
     // 64-to-32
-    check_all_outcomes(
-        HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]),
-        || F32::from(F64::nan(Pos, Quiet, 0).as_f64() as f32),
-    );
+    check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || {
+        F32::from(F64::nan(Pos, Quiet, 0).as_f64() as f32)
+    });
     // The preferred payload is always a possibility.
     check_all_outcomes(
-        HashSet::from_iter([
+        [
             F32::nan(Pos, Quiet, 0),
             F32::nan(Neg, Quiet, 0),
             F32::nan(Pos, Quiet, all1_payload_32),
             F32::nan(Neg, Quiet, all1_payload_32),
-        ]),
+        ],
         || F32::from(F64::nan(Pos, Quiet, all1_payload_64).as_f64() as f32),
     );
     // If the input is signaling, then the output *may* also be signaling.
     check_all_outcomes(
-        HashSet::from_iter([
+        [
             F32::nan(Pos, Quiet, 0),
             F32::nan(Neg, Quiet, 0),
             F32::nan(Pos, Quiet, all1_payload_32),
             F32::nan(Neg, Quiet, all1_payload_32),
             F32::nan(Pos, Signaling, all1_payload_32),
             F32::nan(Neg, Signaling, all1_payload_32),
-        ]),
+        ],
         || F32::from(F64::nan(Pos, Signaling, all1_payload_64).as_f64() as f32),
     );
     // Check that the low bits are gone (not the high bits).
+    check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || {
+        F32::from(F64::nan(Pos, Quiet, 1).as_f64() as f32)
+    });
     check_all_outcomes(
-        HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]),
-        || F32::from(F64::nan(Pos, Quiet, 1).as_f64() as f32),
-    );
-    check_all_outcomes(
-        HashSet::from_iter([
+        [
             F32::nan(Pos, Quiet, 0),
             F32::nan(Neg, Quiet, 0),
             F32::nan(Pos, Quiet, 1),
             F32::nan(Neg, Quiet, 1),
-        ]),
+        ],
         || F32::from(F64::nan(Pos, Quiet, 1 << (51 - 22)).as_f64() as f32),
     );
     check_all_outcomes(
-        HashSet::from_iter([
+        [
             F32::nan(Pos, Quiet, 0),
             F32::nan(Neg, Quiet, 0),
             // The `1` payload becomes `0`, and the `0` payload cannot be signaling,
             // so these are the only options.
-        ]),
+        ],
         || F32::from(F64::nan(Pos, Signaling, 1).as_f64() as f32),
     );
 
     // 32-to-64
-    check_all_outcomes(
-        HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]),
-        || F64::from(F32::nan(Pos, Quiet, 0).as_f32() as f64),
-    );
+    check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || {
+        F64::from(F32::nan(Pos, Quiet, 0).as_f32() as f64)
+    });
     // The preferred payload is always a possibility.
     // Also checks that 0s are added on the right.
     check_all_outcomes(
-        HashSet::from_iter([
+        [
             F64::nan(Pos, Quiet, 0),
             F64::nan(Neg, Quiet, 0),
             F64::nan(Pos, Quiet, left1_payload_64),
             F64::nan(Neg, Quiet, left1_payload_64),
-        ]),
+        ],
         || F64::from(F32::nan(Pos, Quiet, all1_payload_32).as_f32() as f64),
     );
     // If the input is signaling, then the output *may* also be signaling.
     check_all_outcomes(
-        HashSet::from_iter([
+        [
             F64::nan(Pos, Quiet, 0),
             F64::nan(Neg, Quiet, 0),
             F64::nan(Pos, Quiet, left1_payload_64),
             F64::nan(Neg, Quiet, left1_payload_64),
             F64::nan(Pos, Signaling, left1_payload_64),
             F64::nan(Neg, Signaling, left1_payload_64),
-        ]),
+        ],
         || F64::from(F32::nan(Pos, Signaling, all1_payload_32).as_f32() as f64),
     );
 }
@@ -552,48 +509,35 @@ fn test_simd() {
     use std::simd::*;
 
     let nan = F32::nan(Neg, Quiet, 0).as_f32();
-    check_all_outcomes(
-        HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]),
-        || F32::from(unsafe { simd_div(f32x4::splat(0.0), f32x4::splat(0.0)) }[0]),
-    );
-    check_all_outcomes(
-        HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]),
-        || F32::from(unsafe { simd_fmin(f32x4::splat(nan), f32x4::splat(nan)) }[0]),
-    );
-    check_all_outcomes(
-        HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]),
-        || F32::from(unsafe { simd_fmax(f32x4::splat(nan), f32x4::splat(nan)) }[0]),
-    );
-    check_all_outcomes(
-        HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]),
-        || {
-            F32::from(
-                unsafe { simd_fma(f32x4::splat(nan), f32x4::splat(nan), f32x4::splat(nan)) }[0],
-            )
-        },
-    );
-    check_all_outcomes(
-        HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]),
-        || F32::from(unsafe { simd_reduce_add_ordered::<_, f32>(f32x4::splat(nan), nan) }),
-    );
-    check_all_outcomes(
-        HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]),
-        || F32::from(unsafe { simd_reduce_max::<_, f32>(f32x4::splat(nan)) }),
-    );
-    check_all_outcomes(
-        HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]),
-        || F32::from(unsafe { simd_fsqrt(f32x4::splat(nan)) }[0]),
-    );
-    check_all_outcomes(
-        HashSet::from_iter([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)]),
-        || F32::from(unsafe { simd_ceil(f32x4::splat(nan)) }[0]),
-    );
+    check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || {
+        F32::from(unsafe { simd_div(f32x4::splat(0.0), f32x4::splat(0.0)) }[0])
+    });
+    check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || {
+        F32::from(unsafe { simd_fmin(f32x4::splat(nan), f32x4::splat(nan)) }[0])
+    });
+    check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || {
+        F32::from(unsafe { simd_fmax(f32x4::splat(nan), f32x4::splat(nan)) }[0])
+    });
+    check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || {
+        F32::from(unsafe { simd_fma(f32x4::splat(nan), f32x4::splat(nan), f32x4::splat(nan)) }[0])
+    });
+    check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || {
+        F32::from(unsafe { simd_reduce_add_ordered::<_, f32>(f32x4::splat(nan), nan) })
+    });
+    check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || {
+        F32::from(unsafe { simd_reduce_max::<_, f32>(f32x4::splat(nan)) })
+    });
+    check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || {
+        F32::from(unsafe { simd_fsqrt(f32x4::splat(nan)) }[0])
+    });
+    check_all_outcomes([F32::nan(Pos, Quiet, 0), F32::nan(Neg, Quiet, 0)], || {
+        F32::from(unsafe { simd_ceil(f32x4::splat(nan)) }[0])
+    });
 
     // Casts
-    check_all_outcomes(
-        HashSet::from_iter([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)]),
-        || F64::from(unsafe { simd_cast::<f32x4, f64x4>(f32x4::splat(nan)) }[0]),
-    );
+    check_all_outcomes([F64::nan(Pos, Quiet, 0), F64::nan(Neg, Quiet, 0)], || {
+        F64::from(unsafe { simd_cast::<f32x4, f64x4>(f32x4::splat(nan)) }[0])
+    });
 }
 
 fn main() {
diff --git a/src/tools/miri/tests/pass/function_calls/exported_symbol_weak.rs b/src/tools/miri/tests/pass/function_calls/exported_symbol_weak.rs
new file mode 100644
index 00000000000..abf4b6718ab
--- /dev/null
+++ b/src/tools/miri/tests/pass/function_calls/exported_symbol_weak.rs
@@ -0,0 +1,39 @@
+#![feature(linkage)]
+
+// FIXME move this module to a separate crate once aux-build is allowed
+// This currently depends on the fact that miri skips the codegen check
+// that denies multiple symbols with the same name.
+mod first {
+    #[no_mangle]
+    #[linkage = "weak"]
+    extern "C" fn foo() -> i32 {
+        1
+    }
+
+    #[no_mangle]
+    #[linkage = "weak"]
+    extern "C" fn bar() -> i32 {
+        2
+    }
+}
+
+mod second {
+    #[no_mangle]
+    extern "C" fn bar() -> i32 {
+        3
+    }
+}
+
+extern "C" {
+    fn foo() -> i32;
+    fn bar() -> i32;
+}
+
+fn main() {
+    unsafe {
+        // If there is no non-weak definition, the weak definition will be used.
+        assert_eq!(foo(), 1);
+        // Non-weak definition takes presedence.
+        assert_eq!(bar(), 3);
+    }
+}
diff --git a/src/tools/miri/tests/pass/intrinsics/fmuladd_nondeterministic.rs b/src/tools/miri/tests/pass/intrinsics/fmuladd_nondeterministic.rs
index b688405c4b1..abc156d49cb 100644
--- a/src/tools/miri/tests/pass/intrinsics/fmuladd_nondeterministic.rs
+++ b/src/tools/miri/tests/pass/intrinsics/fmuladd_nondeterministic.rs
@@ -3,73 +3,48 @@ use std::intrinsics::simd::simd_relaxed_fma;
 use std::intrinsics::{fmuladdf32, fmuladdf64};
 use std::simd::prelude::*;
 
-fn ensure_both_happen(f: impl Fn() -> bool) -> bool {
-    let mut saw_true = false;
-    let mut saw_false = false;
-    for _ in 0..50 {
-        let b = f();
-        if b {
-            saw_true = true;
-        } else {
-            saw_false = true;
-        }
-        if saw_true && saw_false {
-            return true;
-        }
-    }
-    false
-}
+#[path = "../../utils/mod.rs"]
+mod utils;
+use utils::check_nondet;
 
 fn main() {
-    assert!(
-        ensure_both_happen(|| {
-            let a = std::hint::black_box(0.1_f64);
-            let b = std::hint::black_box(0.2);
-            let c = std::hint::black_box(-a * b);
-            // It is unspecified whether the following operation is fused or not. The
-            // following evaluates to 0.0 if unfused, and nonzero (-1.66e-18) if fused.
-            let x = unsafe { fmuladdf64(a, b, c) };
-            x == 0.0
-        }),
-        "`fmuladdf64` failed to be evaluated as both fused and unfused"
-    );
+    check_nondet(|| {
+        let a = std::hint::black_box(0.1_f64);
+        let b = std::hint::black_box(0.2);
+        let c = std::hint::black_box(-a * b);
+        // It is unspecified whether the following operation is fused or not. The
+        // following evaluates to 0.0 if unfused, and nonzero (-1.66e-18) if fused.
+        let x = fmuladdf64(a, b, c);
+        x == 0.0
+    });
 
-    assert!(
-        ensure_both_happen(|| {
-            let a = std::hint::black_box(0.1_f32);
-            let b = std::hint::black_box(0.2);
-            let c = std::hint::black_box(-a * b);
-            // It is unspecified whether the following operation is fused or not. The
-            // following evaluates to 0.0 if unfused, and nonzero (-8.1956386e-10) if fused.
-            let x = unsafe { fmuladdf32(a, b, c) };
-            x == 0.0
-        }),
-        "`fmuladdf32` failed to be evaluated as both fused and unfused"
-    );
+    check_nondet(|| {
+        let a = std::hint::black_box(0.1_f32);
+        let b = std::hint::black_box(0.2);
+        let c = std::hint::black_box(-a * b);
+        // It is unspecified whether the following operation is fused or not. The
+        // following evaluates to 0.0 if unfused, and nonzero (-8.1956386e-10) if fused.
+        let x = fmuladdf32(a, b, c);
+        x == 0.0
+    });
 
-    assert!(
-        ensure_both_happen(|| {
-            let a = f32x4::splat(std::hint::black_box(0.1));
-            let b = f32x4::splat(std::hint::black_box(0.2));
-            let c = std::hint::black_box(-a * b);
-            let x = unsafe { simd_relaxed_fma(a, b, c) };
-            // Whether we fuse or not is a per-element decision, so sometimes these should be
-            // the same and sometimes not.
-            x[0] == x[1]
-        }),
-        "`simd_relaxed_fma` failed to be evaluated as both fused and unfused"
-    );
+    check_nondet(|| {
+        let a = f32x4::splat(std::hint::black_box(0.1));
+        let b = f32x4::splat(std::hint::black_box(0.2));
+        let c = std::hint::black_box(-a * b);
+        let x = unsafe { simd_relaxed_fma(a, b, c) };
+        // Whether we fuse or not is a per-element decision, so sometimes these should be
+        // the same and sometimes not.
+        x[0] == x[1]
+    });
 
-    assert!(
-        ensure_both_happen(|| {
-            let a = f64x4::splat(std::hint::black_box(0.1));
-            let b = f64x4::splat(std::hint::black_box(0.2));
-            let c = std::hint::black_box(-a * b);
-            let x = unsafe { simd_relaxed_fma(a, b, c) };
-            // Whether we fuse or not is a per-element decision, so sometimes these should be
-            // the same and sometimes not.
-            x[0] == x[1]
-        }),
-        "`simd_relaxed_fma` failed to be evaluated as both fused and unfused"
-    );
+    check_nondet(|| {
+        let a = f64x4::splat(std::hint::black_box(0.1));
+        let b = f64x4::splat(std::hint::black_box(0.2));
+        let c = std::hint::black_box(-a * b);
+        let x = unsafe { simd_relaxed_fma(a, b, c) };
+        // Whether we fuse or not is a per-element decision, so sometimes these should be
+        // the same and sometimes not.
+        x[0] == x[1]
+    });
 }
diff --git a/src/tools/miri/tests/pass/intrinsics/integer.rs b/src/tools/miri/tests/pass/intrinsics/integer.rs
index 13e7bd8e1b9..8727b6d3c87 100644
--- a/src/tools/miri/tests/pass/intrinsics/integer.rs
+++ b/src/tools/miri/tests/pass/intrinsics/integer.rs
@@ -1,7 +1,7 @@
 // SPDX-License-Identifier: MIT OR Apache-2.0
 // SPDX-FileCopyrightText: The Rust Project Developers (see https://thanks.rust-lang.org)
 
-#![feature(core_intrinsics)]
+#![feature(core_intrinsics, funnel_shifts)]
 use std::intrinsics::*;
 
 pub fn main() {
@@ -143,5 +143,11 @@ pub fn main() {
 
         assert_eq!(unchecked_mul(6u8, 7), 42);
         assert_eq!(unchecked_mul(13, -5), -65);
+
+        assert_eq!(unchecked_funnel_shl(1_u32, 2, 5), 32);
+        assert_eq!(unchecked_funnel_shl(1_u32, 2, 31), 0x80000001);
+
+        assert_eq!(unchecked_funnel_shr(1_u32, 2, 5), 0x08000000);
+        assert_eq!(unchecked_funnel_shr(1_u32, 2, 31), 2);
     }
 }
diff --git a/src/tools/miri/tests/pass/overlapping_assignment_aggregate_scalar.rs b/src/tools/miri/tests/pass/overlapping_assignment_aggregate_scalar.rs
new file mode 100644
index 00000000000..0cb2f764242
--- /dev/null
+++ b/src/tools/miri/tests/pass/overlapping_assignment_aggregate_scalar.rs
@@ -0,0 +1,19 @@
+#![feature(custom_mir, core_intrinsics)]
+#![allow(internal_features)]
+
+use std::intrinsics::mir::*;
+
+#[custom_mir(dialect = "runtime")]
+fn main() {
+    mir! {
+        let _1: (u8,);
+        {
+            _1.0 = 0_u8;
+            // This is a scalar type, so overlap is (for now) not UB.
+            // However, we used to treat such overlapping assignments incorrectly
+            // (see <https://github.com/rust-lang/rust/issues/146383#issuecomment-3273224645>).
+            _1 = (_1.0, );
+            Return()
+        }
+    }
+}
diff --git a/src/tools/miri/tests/pass/prefetch.rs b/src/tools/miri/tests/pass/prefetch.rs
new file mode 100644
index 00000000000..99c75c38bde
--- /dev/null
+++ b/src/tools/miri/tests/pass/prefetch.rs
@@ -0,0 +1,23 @@
+#![feature(core_intrinsics)]
+
+// Test that these intrinsics work. Their behavior should be a no-op.
+
+fn main() {
+    static X: [u8; 8] = [0; 8];
+
+    ::std::intrinsics::prefetch_read_data::<_, 1>(::std::ptr::null::<u8>());
+    ::std::intrinsics::prefetch_read_data::<_, 2>(::std::ptr::dangling::<u8>());
+    ::std::intrinsics::prefetch_read_data::<_, 3>(X.as_ptr());
+
+    ::std::intrinsics::prefetch_write_data::<_, 1>(::std::ptr::null::<u8>());
+    ::std::intrinsics::prefetch_write_data::<_, 2>(::std::ptr::dangling::<u8>());
+    ::std::intrinsics::prefetch_write_data::<_, 3>(X.as_ptr());
+
+    ::std::intrinsics::prefetch_read_instruction::<_, 1>(::std::ptr::null::<u8>());
+    ::std::intrinsics::prefetch_read_instruction::<_, 2>(::std::ptr::dangling::<u8>());
+    ::std::intrinsics::prefetch_read_instruction::<_, 3>(X.as_ptr());
+
+    ::std::intrinsics::prefetch_write_instruction::<_, 1>(::std::ptr::null::<u8>());
+    ::std::intrinsics::prefetch_write_instruction::<_, 2>(::std::ptr::dangling::<u8>());
+    ::std::intrinsics::prefetch_write_instruction::<_, 3>(X.as_ptr());
+}
diff --git a/src/tools/miri/tests/pass/provenance.rs b/src/tools/miri/tests/pass/provenance.rs
index 8c52b703190..46f5bd73d55 100644
--- a/src/tools/miri/tests/pass/provenance.rs
+++ b/src/tools/miri/tests/pass/provenance.rs
@@ -6,7 +6,6 @@ const PTR_SIZE: usize = mem::size_of::<&i32>();
 
 fn main() {
     basic();
-    partial_overwrite_then_restore();
     bytewise_ptr_methods();
     bytewise_custom_memcpy();
     bytewise_custom_memcpy_chunked();
@@ -29,40 +28,6 @@ fn basic() {
     assert_eq!(unsafe { *ptr_back }, 42);
 }
 
-/// Overwrite one byte of a pointer, then restore it.
-fn partial_overwrite_then_restore() {
-    unsafe fn ptr_bytes<'x>(ptr: &'x mut *const i32) -> &'x mut [mem::MaybeUninit<u8>; PTR_SIZE] {
-        mem::transmute(ptr)
-    }
-
-    // Returns a value with the same provenance as `x` but 0 for the integer value.
-    // `x` must be initialized.
-    unsafe fn zero_with_provenance(x: mem::MaybeUninit<u8>) -> mem::MaybeUninit<u8> {
-        let ptr = [x; PTR_SIZE];
-        let ptr: *const i32 = mem::transmute(ptr);
-        let mut ptr = ptr.with_addr(0);
-        ptr_bytes(&mut ptr)[0]
-    }
-
-    unsafe {
-        let ptr = &42;
-        let mut ptr = ptr as *const i32;
-        // Get a bytewise view of the pointer.
-        let ptr_bytes = ptr_bytes(&mut ptr);
-
-        // The highest bytes must be 0 for this to work.
-        let hi = if cfg!(target_endian = "little") { ptr_bytes.len() - 1 } else { 0 };
-        assert_eq!(*ptr_bytes[hi].as_ptr().cast::<u8>(), 0);
-        // Overwrite provenance on the last byte.
-        ptr_bytes[hi] = mem::MaybeUninit::new(0);
-        // Restore it from the another byte.
-        ptr_bytes[hi] = zero_with_provenance(ptr_bytes[1]);
-
-        // Now ptr should be good again.
-        assert_eq!(*ptr, 42);
-    }
-}
-
 fn bytewise_ptr_methods() {
     let mut ptr1 = &1;
     let mut ptr2 = &2;
diff --git a/src/tools/miri/tests/pass/shims/fs.rs b/src/tools/miri/tests/pass/shims/fs.rs
index e7f11c54704..022dcc5dcba 100644
--- a/src/tools/miri/tests/pass/shims/fs.rs
+++ b/src/tools/miri/tests/pass/shims/fs.rs
@@ -72,7 +72,9 @@ fn test_file() {
 
     // Writing to a file opened for reading should error (and not stop interpretation). std does not
     // categorize the error so we don't check for details.
-    file.write(&[]).unwrap_err();
+    file.write(&[0]).unwrap_err();
+    // However, writing 0 bytes can succeed or fail.
+    let _ignore = file.write(&[]);
 
     // Removing file should succeed.
     remove_file(&path).unwrap();
diff --git a/src/tools/miri/tests/pass/shims/x86/rounding-error.rs b/src/tools/miri/tests/pass/shims/x86/rounding-error.rs
new file mode 100644
index 00000000000..bf56111b2e4
--- /dev/null
+++ b/src/tools/miri/tests/pass/shims/x86/rounding-error.rs
@@ -0,0 +1,37 @@
+// We're testing x86 target specific features
+//@only-target: x86_64 i686
+
+//! rsqrt and rcp SSE/AVX operations are approximate. We use that as license to treat them as
+//! non-deterministic. Ensure that we do indeed see random results within the expected error bounds.
+
+#[cfg(target_arch = "x86")]
+use std::arch::x86::*;
+#[cfg(target_arch = "x86_64")]
+use std::arch::x86_64::*;
+use std::collections::HashSet;
+
+fn main() {
+    let mut vals = HashSet::new();
+    for _ in 0..50 {
+        unsafe {
+            // Compute the inverse square root of 4.0, four times.
+            let a = _mm_setr_ps(4.0, 4.0, 4.0, 4.0);
+            let exact = 0.5;
+            let r = _mm_rsqrt_ps(a);
+            let r: [f32; 4] = std::mem::transmute(r);
+            // Check the results.
+            for r in r {
+                vals.insert(r.to_bits());
+                // Ensure the relative error is less than 2^-12.
+                let rel_error = (r - exact) / exact;
+                let log_error = rel_error.abs().log2();
+                assert!(
+                    rel_error == 0.0 || log_error < -12.0,
+                    "got an error of {rel_error} = 2^{log_error}"
+                );
+            }
+        }
+    }
+    // Ensure we saw a bunch of different results.
+    assert!(vals.len() >= 50);
+}
diff --git a/src/tools/miri/tests/pass/static_align.rs b/src/tools/miri/tests/pass/static_align.rs
new file mode 100644
index 00000000000..f292f028568
--- /dev/null
+++ b/src/tools/miri/tests/pass/static_align.rs
@@ -0,0 +1,14 @@
+#![feature(static_align)]
+
+// When a static uses `align(N)`, its address should be a multiple of `N`.
+
+#[rustc_align_static(256)]
+static FOO: u64 = 0;
+
+#[rustc_align_static(512)]
+static BAR: u64 = 0;
+
+fn main() {
+    assert!(core::ptr::from_ref(&FOO).addr().is_multiple_of(256));
+    assert!(core::ptr::from_ref(&BAR).addr().is_multiple_of(512));
+}
diff --git a/src/tools/miri/tests/pass/too-large-primval-write-problem.rs b/src/tools/miri/tests/pass/too-large-primval-write-problem.rs
index f4c418bd78a..00882b7ecca 100644
--- a/src/tools/miri/tests/pass/too-large-primval-write-problem.rs
+++ b/src/tools/miri/tests/pass/too-large-primval-write-problem.rs
@@ -7,6 +7,8 @@
 //
 // This is just intended as a regression test to make sure we don't reintroduce this problem.
 
+#![allow(integer_to_ptr_transmutes)]
+
 #[cfg(target_pointer_width = "32")]
 fn main() {
     use std::mem::transmute;
diff --git a/src/tools/miri/tests/pass/transmute_ptr.rs b/src/tools/miri/tests/pass/transmute_ptr.rs
index 0944781c6de..0a53b778294 100644
--- a/src/tools/miri/tests/pass/transmute_ptr.rs
+++ b/src/tools/miri/tests/pass/transmute_ptr.rs
@@ -1,6 +1,6 @@
 //@revisions: stack tree
 //@[tree]compile-flags: -Zmiri-tree-borrows
-use std::{mem, ptr};
+use std::mem;
 
 fn t1() {
     // If we are careful, we can exploit data layout...
@@ -27,27 +27,8 @@ fn ptr_integer_array() {
     let _x: [u8; PTR_SIZE] = unsafe { mem::transmute(&0) };
 }
 
-fn ptr_in_two_halves() {
-    unsafe {
-        let ptr = &0 as *const i32;
-        let arr = [ptr; 2];
-        // We want to do a scalar read of a pointer at offset PTR_SIZE/2 into this array. But we
-        // cannot use a packed struct or `read_unaligned`, as those use the memcpy code path in
-        // Miri. So instead we shift the entire array by a bit and then the actual read we want to
-        // do is perfectly aligned.
-        let mut target_arr = [ptr::null::<i32>(); 3];
-        let target = target_arr.as_mut_ptr().cast::<u8>();
-        target.add(PTR_SIZE / 2).cast::<[*const i32; 2]>().write_unaligned(arr);
-        // Now target_arr[1] is a mix of the two `ptr` we had stored in `arr`.
-        let strange_ptr = target_arr[1];
-        // Check that the provenance works out.
-        assert_eq!(*strange_ptr.with_addr(ptr.addr()), 0);
-    }
-}
-
 fn main() {
     t1();
     t2();
     ptr_integer_array();
-    ptr_in_two_halves();
 }
diff --git a/src/tools/miri/tests/pass/tree_borrows/cell-inside-box.rs b/src/tools/miri/tests/pass/tree_borrows/cell-inside-box.rs
index adf2f4e845b..4a868455c84 100644
--- a/src/tools/miri/tests/pass/tree_borrows/cell-inside-box.rs
+++ b/src/tools/miri/tests/pass/tree_borrows/cell-inside-box.rs
@@ -20,7 +20,7 @@ pub fn main() {
         name!(ptr2);
 
         // We perform a write through `x`.
-        // Because `ptr1` is ReservedIM, a child write will make it transition to Active.
+        // Because `ptr1` is ReservedIM, a child write will make it transition to Unique.
         // Because `ptr2` is ReservedIM, a foreign write doesn't have any effect on it.
         let x = (*ptr1).get();
         *x = 1;
diff --git a/src/tools/miri/tests/pass/tree_borrows/cell-lazy-write-to-surrounding.rs b/src/tools/miri/tests/pass/tree_borrows/cell-lazy-write-to-surrounding.rs
index abe08f2cd22..7352784ac7a 100644
--- a/src/tools/miri/tests/pass/tree_borrows/cell-lazy-write-to-surrounding.rs
+++ b/src/tools/miri/tests/pass/tree_borrows/cell-lazy-write-to-surrounding.rs
@@ -14,9 +14,11 @@ fn main() {
     foo(&arr[0]);
 
     let pair = (Cell::new(1), 1);
-    // TODO: Ideally, this would result in UB since the second element
-    // in `pair` is Frozen.  We would need some way to express a
-    // "shared reference with permission to access surrounding
-    // interior mutable data".
     foo(&pair.0);
+
+    // As long as the "inside" part is `!Freeze`, the permission to mutate the "outside" is preserved.
+    let pair = (Cell::new(()), 1);
+    let x = &pair.0;
+    let ptr = (&raw const *x).cast::<i32>().cast_mut();
+    unsafe { ptr.write(0) };
 }
diff --git a/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.rs b/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.rs
index 4fbccef2367..edd649b91ed 100644
--- a/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.rs
+++ b/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.rs
@@ -6,7 +6,7 @@
 mod utils;
 
 // To check that a reborrow is counted as a Read access, we use a reborrow
-// with no additional Read to Freeze an Active pointer.
+// with no additional Read to Freeze an Unique pointer.
 
 fn main() {
     unsafe {
@@ -15,7 +15,7 @@ fn main() {
         let alloc_id = alloc_id!(parent);
         let x = &mut *parent;
         name!(x);
-        *x = 0; // x is now Active
+        *x = 0; // x is now Unique
         print_state!(alloc_id);
         let y = &mut *parent;
         name!(y);
diff --git a/src/tools/miri/tests/pass/tree_borrows/reserved.rs b/src/tools/miri/tests/pass/tree_borrows/reserved.rs
index c57cd7fcf0a..83e1d9c70ed 100644
--- a/src/tools/miri/tests/pass/tree_borrows/reserved.rs
+++ b/src/tools/miri/tests/pass/tree_borrows/reserved.rs
@@ -68,7 +68,7 @@ unsafe fn cell_unprotected_read() {
 }
 
 // Foreign Write on an interior mutable pointer is a noop.
-// Also y must become Active.
+// Also y must become Unique.
 unsafe fn cell_unprotected_write() {
     print("[interior mut] Foreign Write: Re* -> Re*");
     let base = &mut UnsafeCell::new(0u64);
@@ -97,7 +97,7 @@ unsafe fn int_protected_read() {
 }
 
 // Foreign Read on a Reserved is a noop.
-// Also y must become Active.
+// Also y must become Unique.
 unsafe fn int_unprotected_read() {
     print("[] Foreign Read: Res -> Res");
     let base = &mut 0u8;
diff --git a/src/tools/miri/tests/pass/weak_memory/weak.rs b/src/tools/miri/tests/pass/weak_memory/weak.rs
deleted file mode 100644
index 199f83f0528..00000000000
--- a/src/tools/miri/tests/pass/weak_memory/weak.rs
+++ /dev/null
@@ -1,151 +0,0 @@
-//@compile-flags: -Zmiri-ignore-leaks -Zmiri-fixed-schedule
-
-// Tests showing weak memory behaviours are exhibited. All tests
-// return true when the desired behaviour is seen.
-// This is scheduler and pseudo-RNG dependent, so each test is
-// run multiple times until one try returns true.
-// Spurious failure is possible, if you are really unlucky with
-// the RNG and always read the latest value from the store buffer.
-
-use std::sync::atomic::Ordering::*;
-use std::sync::atomic::{AtomicUsize, fence};
-use std::thread::spawn;
-
-#[allow(dead_code)]
-#[derive(Copy, Clone)]
-struct EvilSend<T>(pub T);
-
-unsafe impl<T> Send for EvilSend<T> {}
-unsafe impl<T> Sync for EvilSend<T> {}
-
-// We can't create static items because we need to run each test multiple times.
-fn static_atomic(val: usize) -> &'static AtomicUsize {
-    Box::leak(Box::new(AtomicUsize::new(val)))
-}
-
-// Spins until it reads the given value
-fn spin_until(loc: &AtomicUsize, val: usize) -> usize {
-    while loc.load(Relaxed) != val {
-        std::hint::spin_loop();
-    }
-    val
-}
-
-fn relaxed(initial_read: bool) -> bool {
-    let x = static_atomic(0);
-    let j1 = spawn(move || {
-        x.store(1, Relaxed);
-        // Preemption is disabled, so the store above will never be the
-        // latest store visible to another thread.
-        x.store(2, Relaxed);
-    });
-
-    let j2 = spawn(move || x.load(Relaxed));
-
-    j1.join().unwrap();
-    let r2 = j2.join().unwrap();
-
-    // There are three possible values here: 0 (from the initial read), 1 (from the first relaxed
-    // read), and 2 (the last read). The last case is boring and we cover the other two.
-    r2 == if initial_read { 0 } else { 1 }
-}
-
-// https://www.doc.ic.ac.uk/~afd/homepages/papers/pdfs/2017/POPL.pdf Figure 8
-fn seq_cst() -> bool {
-    let x = static_atomic(0);
-
-    let j1 = spawn(move || {
-        x.store(1, Relaxed);
-    });
-
-    let j2 = spawn(move || {
-        x.store(2, SeqCst);
-        x.store(3, SeqCst);
-    });
-
-    let j3 = spawn(move || x.load(SeqCst));
-
-    j1.join().unwrap();
-    j2.join().unwrap();
-    let r3 = j3.join().unwrap();
-
-    r3 == 1
-}
-
-fn initialization_write(add_fence: bool) -> bool {
-    let x = static_atomic(11);
-
-    let wait = static_atomic(0);
-
-    let j1 = spawn(move || {
-        x.store(22, Relaxed);
-        // Relaxed is intentional. We want to test if the thread 2 reads the initialisation write
-        // after a relaxed write
-        wait.store(1, Relaxed);
-    });
-
-    let j2 = spawn(move || {
-        spin_until(wait, 1);
-        if add_fence {
-            fence(AcqRel);
-        }
-        x.load(Relaxed)
-    });
-
-    j1.join().unwrap();
-    let r2 = j2.join().unwrap();
-
-    r2 == 11
-}
-
-fn faa_replaced_by_load() -> bool {
-    // Example from https://github.com/llvm/llvm-project/issues/56450#issuecomment-1183695905
-    #[no_mangle]
-    pub fn rdmw(storing: &AtomicUsize, sync: &AtomicUsize, loading: &AtomicUsize) -> usize {
-        storing.store(1, Relaxed);
-        fence(Release);
-        // sync.fetch_add(0, Relaxed);
-        sync.load(Relaxed);
-        fence(Acquire);
-        loading.load(Relaxed)
-    }
-
-    let x = static_atomic(0);
-    let y = static_atomic(0);
-    let z = static_atomic(0);
-
-    // Since each thread is so short, we need to make sure that they truely run at the same time
-    // Otherwise t1 will finish before t2 even starts
-    let go = static_atomic(0);
-
-    let t1 = spawn(move || {
-        spin_until(go, 1);
-        rdmw(y, x, z)
-    });
-
-    let t2 = spawn(move || {
-        spin_until(go, 1);
-        rdmw(z, x, y)
-    });
-
-    go.store(1, Relaxed);
-
-    let a = t1.join().unwrap();
-    let b = t2.join().unwrap();
-    (a, b) == (0, 0)
-}
-
-/// Asserts that the function returns true at least once in 100 runs
-#[track_caller]
-fn assert_once(f: fn() -> bool) {
-    assert!(std::iter::repeat_with(|| f()).take(100).any(|x| x));
-}
-
-pub fn main() {
-    assert_once(|| relaxed(false));
-    assert_once(|| relaxed(true));
-    assert_once(seq_cst);
-    assert_once(|| initialization_write(false));
-    assert_once(|| initialization_write(true));
-    assert_once(faa_replaced_by_load);
-}
diff --git a/src/tools/miri/tests/ui.rs b/src/tools/miri/tests/ui.rs
index f021d5194cd..efaaf9fc841 100644
--- a/src/tools/miri/tests/ui.rs
+++ b/src/tools/miri/tests/ui.rs
@@ -60,6 +60,7 @@ fn build_native_lib(target: &str) -> PathBuf {
             native_lib_path.to_str().unwrap(),
             // FIXME: Automate gathering of all relevant C source files in the directory.
             "tests/native-lib/scalar_arguments.c",
+            "tests/native-lib/aggregate_arguments.c",
             "tests/native-lib/ptr_read_access.c",
             "tests/native-lib/ptr_write_access.c",
             // Ensure we notice serious problems in the C code.
@@ -276,6 +277,8 @@ regexes! {
     r"\bsys/([a-z_]+)/[a-z]+\b"     => "sys/$1/PLATFORM",
     // erase paths into the crate registry
     r"[^ ]*/\.?cargo/registry/.*/(.*\.rs)"  => "CARGO_REGISTRY/.../$1",
+    // remove time print from GenMC estimation mode output.
+    "\nExpected verification time: .* ± .*" => "\nExpected verification time: [MEAN] ± [SD]",
 }
 
 enum Dependencies {
diff --git a/src/tools/miri/tests/utils/genmc.rs b/src/tools/miri/tests/utils/genmc.rs
new file mode 100644
index 00000000000..29b3181992b
--- /dev/null
+++ b/src/tools/miri/tests/utils/genmc.rs
@@ -0,0 +1,64 @@
+#![allow(unused)]
+
+use std::ffi::c_void;
+
+use libc::{self, pthread_attr_t, pthread_t};
+
+/// Spawn a thread using `pthread_create`, abort the process on any errors.
+pub unsafe fn spawn_pthread(
+    f: extern "C" fn(*mut c_void) -> *mut c_void,
+    value: *mut c_void,
+) -> pthread_t {
+    let mut thread_id: pthread_t = 0;
+
+    let attr: *const pthread_attr_t = std::ptr::null();
+
+    if unsafe { libc::pthread_create(&raw mut thread_id, attr, f, value) } != 0 {
+        std::process::abort();
+    }
+    thread_id
+}
+
+/// Unsafe because we do *not* check that `F` is `Send + 'static`.
+/// That makes it much easier to write tests...
+pub unsafe fn spawn_pthread_closure<F: FnOnce()>(f: F) -> pthread_t {
+    let mut thread_id: pthread_t = 0;
+    let attr: *const pthread_attr_t = std::ptr::null();
+    let f = Box::new(f);
+    extern "C" fn thread_func<F: FnOnce()>(f: *mut c_void) -> *mut c_void {
+        let f = unsafe { Box::from_raw(f as *mut F) };
+        f();
+        std::ptr::null_mut()
+    }
+    if unsafe {
+        libc::pthread_create(
+            &raw mut thread_id,
+            attr,
+            thread_func::<F>,
+            Box::into_raw(f) as *mut c_void,
+        )
+    } != 0
+    {
+        std::process::abort();
+    }
+    thread_id
+}
+
+// Join the given pthread, abort the process on any errors.
+pub unsafe fn join_pthread(thread_id: pthread_t) {
+    if unsafe { libc::pthread_join(thread_id, std::ptr::null_mut()) } != 0 {
+        std::process::abort();
+    }
+}
+
+/// Spawn `N` threads using `pthread_create` without any arguments, abort the process on any errors.
+pub unsafe fn spawn_pthreads_no_params<const N: usize>(
+    functions: [extern "C" fn(*mut c_void) -> *mut c_void; N],
+) -> [pthread_t; N] {
+    functions.map(|func| spawn_pthread(func, std::ptr::null_mut()))
+}
+
+// Join the `N` given pthreads, abort the process on any errors.
+pub unsafe fn join_pthreads<const N: usize>(thread_ids: [pthread_t; N]) {
+    let _ = thread_ids.map(|id| join_pthread(id));
+}
diff --git a/src/tools/miri/tests/utils/libc.rs b/src/tools/miri/tests/utils/libc.rs
index 1a3cd067c04..4757a5a268c 100644
--- a/src/tools/miri/tests/utils/libc.rs
+++ b/src/tools/miri/tests/utils/libc.rs
@@ -34,10 +34,7 @@ pub unsafe fn write_all(
         if res < 0 {
             return res;
         }
-        if res == 0 {
-            // EOF?
-            break;
-        }
+        // Apparently a return value of 0 is just a short write, nothing special (unlike reads).
         written_so_far += res as libc::size_t;
     }
     return written_so_far as libc::ssize_t;
diff --git a/src/tools/miri/tests/utils/mod.rs b/src/tools/miri/tests/utils/mod.rs
index 138ada4e20d..37f99962163 100644
--- a/src/tools/miri/tests/utils/mod.rs
+++ b/src/tools/miri/tests/utils/mod.rs
@@ -16,3 +16,53 @@ pub fn run_provenance_gc() {
     // SAFETY: No preconditions. The GC is fine to run at any time.
     unsafe { miri_run_provenance_gc() }
 }
+
+/// Check that the function produces the intended set of outcomes.
+#[track_caller]
+pub fn check_all_outcomes<T: Eq + std::hash::Hash + std::fmt::Debug>(
+    expected: impl IntoIterator<Item = T>,
+    generate: impl Fn() -> T,
+) {
+    use std::collections::HashSet;
+
+    let expected: HashSet<T> = HashSet::from_iter(expected);
+    let mut seen = HashSet::new();
+    // Let's give it N times as many tries as we are expecting values.
+    let min_tries = std::cmp::max(20, expected.len() * 4);
+    let max_tries = expected.len() * 50;
+    for i in 0..max_tries {
+        let val = generate();
+        assert!(expected.contains(&val), "got an unexpected value: {val:?}");
+        seen.insert(val);
+        if i >= min_tries && expected.len() == seen.len() {
+            // We saw everything and we did enough tries, let's avoid wasting time.
+            return;
+        }
+    }
+    // Let's see if we saw them all.
+    if expected.len() == seen.len() {
+        return;
+    }
+    // Find the missing one.
+    for val in expected {
+        if !seen.contains(&val) {
+            panic!("did not get value that should be possible: {val:?}");
+        }
+    }
+    unreachable!()
+}
+
+/// Check that the operation is non-deterministic
+#[track_caller]
+pub fn check_nondet<T: PartialEq + std::fmt::Debug>(f: impl Fn() -> T) {
+    let rounds = 50;
+    let first = f();
+    for _ in 1..rounds {
+        if f() != first {
+            // We saw two different values!
+            return;
+        }
+    }
+    // We saw the same thing N times.
+    panic!("expected non-determinism, got {rounds} times the same result: {first:?}");
+}
diff --git a/src/tools/miri/tests/x86_64-unknown-kernel.json b/src/tools/miri/tests/x86_64-unknown-kernel.json
index a5eaceb4f68..0f8032c39d5 100644
--- a/src/tools/miri/tests/x86_64-unknown-kernel.json
+++ b/src/tools/miri/tests/x86_64-unknown-kernel.json
@@ -1,7 +1,7 @@
 {
   "llvm-target": "x86_64-unknown-none",
   "target-endian": "little",
-  "target-pointer-width": "64",
+  "target-pointer-width": 64,
   "target-c-int-width": 32,
   "data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128",
   "arch": "x86_64",
diff --git a/src/tools/miri/triagebot.toml b/src/tools/miri/triagebot.toml
index a0ce9f80024..c747cbb0a52 100644
--- a/src/tools/miri/triagebot.toml
+++ b/src/tools/miri/triagebot.toml
@@ -50,11 +50,14 @@ new_pr = true
 [autolabel."S-waiting-on-author"]
 new_draft = true
 
-# Automatically close and reopen PRs made by bots to run CI on them
-[bot-pull-requests]
-
-# Canonicalize issue numbers to avoid closing the wrong issue when upstreaming this subtree
+# Canonicalize issue numbers to avoid closing the wrong issue when upstreaming this subtree.
 [canonicalize-issue-links]
 
-# Prevents mentions in commits to avoid users being spammed
+# Prevents mentions in commits to avoid users being spammed.
 [no-mentions]
+
+# Show range-diff links on force pushes.
+[range-diff]
+
+# Add link to the review body to review changes since posting it.
+[review-changes-since]
diff --git a/src/tools/nix-dev-shell/shell.nix b/src/tools/nix-dev-shell/shell.nix
index 0adbacf7e8d..6ca8a7c4652 100644
--- a/src/tools/nix-dev-shell/shell.nix
+++ b/src/tools/nix-dev-shell/shell.nix
@@ -14,6 +14,8 @@ pkgs.mkShell {
   packages = [
     pkgs.git
     pkgs.nix
+    pkgs.glibc.out
+    pkgs.glibc.static
     x
     # Get the runtime deps of the x wrapper
   ] ++ lists.flatten (attrsets.attrValues env);
@@ -22,5 +24,7 @@ pkgs.mkShell {
     # Avoid creating text files for ICEs.
     RUSTC_ICE = 0;
     SSL_CERT_FILE = cacert;
+    # cargo seems to dlopen libcurl, so we need it in the ld library path
+    LD_LIBRARY_PATH = "${pkgs.lib.makeLibraryPath [pkgs.stdenv.cc.cc.lib pkgs.curl]}";
   };
 }
diff --git a/src/tools/opt-dist/Cargo.toml b/src/tools/opt-dist/Cargo.toml
index 2ed3fbac709..f4051ae67d7 100644
--- a/src/tools/opt-dist/Cargo.toml
+++ b/src/tools/opt-dist/Cargo.toml
@@ -10,7 +10,7 @@ log = "0.4"
 anyhow = "1"
 humantime = "2"
 humansize = "2"
-sysinfo = { version = "0.36.0", default-features = false, features = ["disk"] }
+sysinfo = { version = "0.37.0", default-features = false, features = ["disk"] }
 fs_extra = "1"
 camino = "1"
 tar = "0.4"
diff --git a/src/tools/opt-dist/src/bolt.rs b/src/tools/opt-dist/src/bolt.rs
index a06e59fcc41..3ee9912b8c2 100644
--- a/src/tools/opt-dist/src/bolt.rs
+++ b/src/tools/opt-dist/src/bolt.rs
@@ -9,6 +9,7 @@ use crate::utils::io::copy_file;
 /// Instruments an artifact at the given `path` (in-place) with BOLT and then calls `func`.
 /// After this function finishes, the original file will be restored.
 pub fn with_bolt_instrumented<F: FnOnce(&Utf8Path) -> anyhow::Result<R>, R>(
+    env: &Environment,
     path: &Utf8Path,
     func: F,
 ) -> anyhow::Result<R> {
@@ -26,7 +27,7 @@ pub fn with_bolt_instrumented<F: FnOnce(&Utf8Path) -> anyhow::Result<R>, R>(
     let profile_prefix = Utf8Path::from_path(&profile_prefix).unwrap();
 
     // Instrument the original file with BOLT, saving the result into `instrumented_path`
-    cmd(&["llvm-bolt"])
+    cmd(&[env.llvm_bolt().as_str()])
         .arg("-instrument")
         .arg(path)
         .arg(&format!("--instrumentation-file={profile_prefix}"))
@@ -61,7 +62,7 @@ pub fn bolt_optimize(
     let split_strategy =
         if env.host_tuple().starts_with("aarch64") { "profile2" } else { "cdsplit" };
 
-    cmd(&["llvm-bolt"])
+    cmd(&[env.llvm_bolt().as_str()])
         .arg(temp_path.display())
         .arg("-data")
         .arg(&profile.0)
diff --git a/src/tools/opt-dist/src/environment.rs b/src/tools/opt-dist/src/environment.rs
index 2cae0785f33..7cc51901a83 100644
--- a/src/tools/opt-dist/src/environment.rs
+++ b/src/tools/opt-dist/src/environment.rs
@@ -116,6 +116,14 @@ impl Environment {
     pub fn stage0(&self) -> Utf8PathBuf {
         self.stage0_root.clone().unwrap_or_else(|| self.build_artifacts().join("stage0"))
     }
+
+    pub fn llvm_bolt(&self) -> Utf8PathBuf {
+        self.host_llvm_dir().join(format!("bin/llvm-bolt{}", executable_extension()))
+    }
+
+    pub fn merge_fdata(&self) -> Utf8PathBuf {
+        self.host_llvm_dir().join(format!("bin/merge-fdata{}", executable_extension()))
+    }
 }
 
 /// What is the extension of binary executables on this platform?
diff --git a/src/tools/opt-dist/src/exec.rs b/src/tools/opt-dist/src/exec.rs
index a8d4c93d160..a3935f98359 100644
--- a/src/tools/opt-dist/src/exec.rs
+++ b/src/tools/opt-dist/src/exec.rs
@@ -189,6 +189,12 @@ impl Bootstrap {
         self
     }
 
+    /// Rebuild rustc in case of statically linked LLVM
+    pub fn rustc_rebuild(mut self) -> Self {
+        self.cmd = self.cmd.arg("--keep-stage").arg("0");
+        self
+    }
+
     pub fn run(self, timer: &mut TimerSection) -> anyhow::Result<()> {
         self.cmd.run()?;
         let metrics = load_metrics(&self.metrics_path)?;
diff --git a/src/tools/opt-dist/src/main.rs b/src/tools/opt-dist/src/main.rs
index 19706b4a4f0..48b25f235dd 100644
--- a/src/tools/opt-dist/src/main.rs
+++ b/src/tools/opt-dist/src/main.rs
@@ -329,7 +329,7 @@ fn execute_pipeline(
 
                 // FIXME(kobzol): try gather profiles together, at once for LLVM and rustc
                 // Instrument the libraries and gather profiles
-                let llvm_profile = with_bolt_instrumented(&llvm_lib, |llvm_profile_dir| {
+                let llvm_profile = with_bolt_instrumented(env, &llvm_lib, |llvm_profile_dir| {
                     stage.section("Gather profiles", |_| {
                         gather_bolt_profiles(env, "LLVM", llvm_benchmarks(env), llvm_profile_dir)
                     })
@@ -354,7 +354,7 @@ fn execute_pipeline(
             log::info!("Optimizing {rustc_lib} with BOLT");
 
             // Instrument it and gather profiles
-            let rustc_profile = with_bolt_instrumented(&rustc_lib, |rustc_profile_dir| {
+            let rustc_profile = with_bolt_instrumented(env, &rustc_lib, |rustc_profile_dir| {
                 stage.section("Gather profiles", |_| {
                     gather_bolt_profiles(env, "rustc", rustc_benchmarks(env), rustc_profile_dir)
                 })
@@ -375,8 +375,14 @@ fn execute_pipeline(
 
     let mut dist = Bootstrap::dist(env, &dist_args)
         .llvm_pgo_optimize(llvm_pgo_profile.as_ref())
-        .rustc_pgo_optimize(&rustc_pgo_profile)
-        .avoid_rustc_rebuild();
+        .rustc_pgo_optimize(&rustc_pgo_profile);
+
+    // if LLVM is not built we'll have PGO optimized rustc
+    dist = if env.supports_shared_llvm() || !env.build_llvm() {
+        dist.avoid_rustc_rebuild()
+    } else {
+        dist.rustc_rebuild()
+    };
 
     for bolt_profile in bolt_profiles {
         dist = dist.with_bolt_profile(bolt_profile);
diff --git a/src/tools/opt-dist/src/tests.rs b/src/tools/opt-dist/src/tests.rs
index d5121b8c786..fa55805bbf2 100644
--- a/src/tools/opt-dist/src/tests.rs
+++ b/src/tools/opt-dist/src/tests.rs
@@ -36,6 +36,16 @@ pub fn run_tests(env: &Environment) -> anyhow::Result<()> {
     let cargo_dir = extract_dist_dir(&format!("cargo-{version}-{host_triple}"))?.join("cargo");
     let extracted_src_dir = extract_dist_dir(&format!("rust-src-{version}"))?.join("rust-src");
 
+    // If we have a Cranelift archive, copy it to the rustc sysroot
+    if let Ok(_) = find_file_in_dir(&dist_dir, "rustc-codegen-cranelift-", ".tar.xz") {
+        let extracted_codegen_dir =
+            extract_dist_dir(&format!("rustc-codegen-cranelift-{version}-{host_triple}"))?
+                .join("rustc-codegen-cranelift-preview");
+        let rel_path =
+            Utf8Path::new("lib").join("rustlib").join(host_triple).join("codegen-backends");
+        copy_directory(&extracted_codegen_dir.join(&rel_path), &rustc_dir.join(&rel_path))?;
+    }
+
     // We need to manually copy libstd to the extracted rustc sysroot
     copy_directory(
         &libstd_dir.join("lib").join("rustlib").join(host_triple).join("lib"),
diff --git a/src/tools/opt-dist/src/training.rs b/src/tools/opt-dist/src/training.rs
index ae062d5c60c..b3e95e087e3 100644
--- a/src/tools/opt-dist/src/training.rs
+++ b/src/tools/opt-dist/src/training.rs
@@ -39,7 +39,6 @@ fn init_compiler_benchmarks(
         "--exact-match",
         crates.join(",").as_str(),
     ])
-    .env("RUST_LOG", "collector=debug")
     .env("RUSTC", env.rustc_stage_0().as_str())
     .env("RUSTC_BOOTSTRAP", "1")
     .workdir(&env.rustc_perf_dir());
@@ -195,7 +194,8 @@ pub fn gather_bolt_profiles(
     let profiles: Vec<_> =
         glob::glob(&format!("{profile_prefix}*"))?.collect::<Result<Vec<_>, _>>()?;
 
-    let mut merge_args = vec!["merge-fdata"];
+    let fdata = env.merge_fdata();
+    let mut merge_args = vec![fdata.as_str()];
     merge_args.extend(profiles.iter().map(|p| p.to_str().unwrap()));
 
     with_log_group("Merging BOLT profiles", || {
diff --git a/src/tools/remote-test-server/src/main.rs b/src/tools/remote-test-server/src/main.rs
index 67a7ad6f3b4..5ec5e6e2898 100644
--- a/src/tools/remote-test-server/src/main.rs
+++ b/src/tools/remote-test-server/src/main.rs
@@ -53,6 +53,10 @@ impl Config {
             batch: false,
             bind: if cfg!(target_os = "android") || cfg!(windows) {
                 ([0, 0, 0, 0], 12345).into()
+            } else if cfg!(target_env = "sim") {
+                // iOS/tvOS/watchOS/visionOS simulators share network device
+                // with the host machine.
+                ([127, 0, 0, 1], 12345).into()
             } else {
                 ([10, 0, 2, 15], 12345).into()
             },
@@ -262,10 +266,17 @@ fn handle_run(socket: TcpStream, work: &Path, tmp: &Path, lock: &Mutex<()>, conf
     cmd.args(args);
     cmd.envs(env);
 
-    // On windows, libraries are just searched in the executable directory,
-    // system directories, PWD, and PATH, in that order. PATH is the only one
-    // we can change for this.
-    let library_path = if cfg!(windows) { "PATH" } else { "LD_LIBRARY_PATH" };
+    let library_path = if cfg!(windows) {
+        // On windows, libraries are just searched in the executable directory,
+        // system directories, PWD, and PATH, in that order. PATH is the only
+        // one we can change for this.
+        "PATH"
+    } else if cfg!(target_vendor = "apple") {
+        // On Apple platforms, the environment variable is named differently.
+        "DYLD_LIBRARY_PATH"
+    } else {
+        "LD_LIBRARY_PATH"
+    };
 
     // Support libraries were uploaded to `work` earlier, so make sure that's
     // in `LD_LIBRARY_PATH`. Also include our own current dir which may have
diff --git a/src/tools/run-make-support/Cargo.toml b/src/tools/run-make-support/Cargo.toml
index a4e7534137d..250e0f65a9f 100644
--- a/src/tools/run-make-support/Cargo.toml
+++ b/src/tools/run-make-support/Cargo.toml
@@ -17,7 +17,7 @@ object = "0.37"
 regex = "1.11"
 serde_json = "1.0"
 similar = "2.7"
-wasmparser = { version = "0.219", default-features = false, features = ["std"] }
+wasmparser = { version = "0.236", default-features = false, features = ["std", "features", "validate"] }
 # tidy-alphabetical-end
 
 # Shared with bootstrap and compiletest
diff --git a/src/tools/run-make-support/src/external_deps/cargo.rs b/src/tools/run-make-support/src/external_deps/cargo.rs
index 8da9f002c41..3f2d0ce14e3 100644
--- a/src/tools/run-make-support/src/external_deps/cargo.rs
+++ b/src/tools/run-make-support/src/external_deps/cargo.rs
@@ -1,11 +1,17 @@
 use crate::command::Command;
-use crate::env_var;
 use crate::util::set_host_compiler_dylib_path;
 
-/// Returns a command that can be used to invoke cargo. The cargo is provided by compiletest
-/// through the `CARGO` env var.
+/// Returns a command that can be used to invoke in-tree cargo. The cargo is provided by compiletest
+/// through the `CARGO` env var, and is **only** available for the `run-make-cargo` test suite.
 pub fn cargo() -> Command {
-    let mut cmd = Command::new(env_var("CARGO"));
+    let cargo_path = std::env::var("CARGO").unwrap_or_else(|e| {
+        panic!(
+            "in-tree `cargo` should be available for `run-make-cargo` test suite, but not \
+            `run-make` test suite: {e}"
+        )
+    });
+
+    let mut cmd = Command::new(cargo_path);
     set_host_compiler_dylib_path(&mut cmd);
     cmd
 }
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 60d3366ee98..b74b1d5e166 100644
--- a/src/tools/run-make-support/src/external_deps/rustc.rs
+++ b/src/tools/run-make-support/src/external_deps/rustc.rs
@@ -405,6 +405,12 @@ impl Rustc {
         };
         self
     }
+
+    /// Make that the generated LLVM IR is in source order.
+    pub fn codegen_source_order(&mut self) -> &mut Self {
+        self.cmd.arg("-Zcodegen-source-order");
+        self
+    }
 }
 
 /// Query the sysroot path corresponding `rustc --print=sysroot`.
diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs
index 191e205f257..fef75401d94 100644
--- a/src/tools/run-make-support/src/lib.rs
+++ b/src/tools/run-make-support/src/lib.rs
@@ -1,7 +1,8 @@
 //! `run-make-support` is a support library for run-make tests. It provides command wrappers and
 //! convenience utility functions to help test writers reduce duplication. The support library
-//! notably is built via cargo: this means that if your test wants some non-trivial utility, such
-//! as `object` or `wasmparser`, they can be re-exported and be made available through this library.
+//! notably is built via bootstrap cargo: this means that if your test wants some non-trivial
+//! utility, such as `object` or `wasmparser`, they can be re-exported and be made available through
+//! this library.
 
 #![warn(unreachable_pub)]
 
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
index 301d4cca066..e5c213ca937 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lib.rs
@@ -15,10 +15,6 @@ extern crate rustc_parse_format;
 #[cfg(not(feature = "in-rust-tree"))]
 extern crate ra_ap_rustc_parse_format as rustc_parse_format;
 
-#[cfg(feature = "in-rust-tree")]
-extern crate rustc_abi;
-
-#[cfg(not(feature = "in-rust-tree"))]
 extern crate ra_ap_rustc_abi as rustc_abi;
 
 pub mod db;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
index c2acfc4142e..281cf6b2d4b 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
@@ -3,45 +3,24 @@
 
 #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))]
 
-#[cfg(feature = "in-rust-tree")]
-extern crate rustc_index;
+// FIXME: We used to import `rustc_*` deps from `rustc_private` with `feature = "in-rust-tree" but
+// temporarily switched to crates.io versions due to hardships that working on them from rustc
+// demands corresponding changes on rust-analyzer at the same time.
+// For details, see the zulip discussion below:
+// https://rust-lang.zulipchat.com/#narrow/channel/185405-t-compiler.2Frust-analyzer/topic/relying.20on.20in-tree.20.60rustc_type_ir.60.2F.60rustc_next_trait_solver.60/with/541055689
 
-#[cfg(not(feature = "in-rust-tree"))]
 extern crate ra_ap_rustc_index as rustc_index;
 
-#[cfg(feature = "in-rust-tree")]
-extern crate rustc_abi;
-
-#[cfg(not(feature = "in-rust-tree"))]
 extern crate ra_ap_rustc_abi as rustc_abi;
 
-#[cfg(feature = "in-rust-tree")]
-extern crate rustc_pattern_analysis;
-
-#[cfg(not(feature = "in-rust-tree"))]
 extern crate ra_ap_rustc_pattern_analysis as rustc_pattern_analysis;
 
-#[cfg(feature = "in-rust-tree")]
-extern crate rustc_ast_ir;
-
-#[cfg(not(feature = "in-rust-tree"))]
 extern crate ra_ap_rustc_ast_ir as rustc_ast_ir;
 
-#[cfg(feature = "in-rust-tree")]
-extern crate rustc_type_ir;
-
-#[cfg(not(feature = "in-rust-tree"))]
 extern crate ra_ap_rustc_type_ir as rustc_type_ir;
 
-#[cfg(feature = "in-rust-tree")]
-extern crate rustc_next_trait_solver;
-
-#[cfg(not(feature = "in-rust-tree"))]
 extern crate ra_ap_rustc_next_trait_solver as rustc_next_trait_solver;
 
-#[cfg(feature = "in-rust-tree")]
-extern crate rustc_data_structures as ena;
-
 mod builder;
 mod chalk_db;
 mod chalk_ext;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs
index 7ebefa76ed0..0b3582051bc 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/consts.rs
@@ -36,8 +36,6 @@ impl<'db> Const<'db> {
             internee: kind,
             flags: flags.flags,
             outer_exclusive_binder: flags.outer_exclusive_binder,
-            #[cfg(feature = "in-rust-tree")]
-            stable_hash: ena::fingerprint::Fingerprint::ZERO,
         };
         Const::new_(interner.db(), InternedWrapperNoDebug(cached))
     }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs
index 86545415009..99b1354b633 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs
@@ -227,8 +227,6 @@ impl<'db> Predicate<'db> {
             internee: kind,
             flags: flags.flags,
             outer_exclusive_binder: flags.outer_exclusive_binder,
-            #[cfg(feature = "in-rust-tree")]
-            stable_hash: ena::fingerprint::Fingerprint::ZERO,
         };
         Predicate::new_(interner.db(), InternedWrapperNoDebug(cached))
     }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs
index 16bf082b01a..a25996ab485 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs
@@ -61,8 +61,6 @@ impl<'db> Ty<'db> {
             internee: kind,
             flags: flags.flags,
             outer_exclusive_binder: flags.outer_exclusive_binder,
-            #[cfg(feature = "in-rust-tree")]
-            stable_hash: ena::fingerprint::Fingerprint::ZERO,
         };
         Ty::new_(interner.db(), InternedWrapperNoDebug(cached))
     }
diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index 5592ca285fb..93f6f6329e1 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -20,10 +20,6 @@
 #![cfg_attr(feature = "in-rust-tree", feature(rustc_private))]
 #![recursion_limit = "512"]
 
-#[cfg(feature = "in-rust-tree")]
-extern crate rustc_type_ir;
-
-#[cfg(not(feature = "in-rust-tree"))]
 extern crate ra_ap_rustc_type_ir as rustc_type_ir;
 
 mod attrs;
diff --git a/src/tools/rustbook/Cargo.lock b/src/tools/rustbook/Cargo.lock
index d0eeab22062..cd7ee6fb4fe 100644
--- a/src/tools/rustbook/Cargo.lock
+++ b/src/tools/rustbook/Cargo.lock
@@ -47,9 +47,9 @@ dependencies = [
 
 [[package]]
 name = "anstream"
-version = "0.6.19"
+version = "0.6.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "301af1932e46185686725e0fad2f8f2aa7da69dd70bf6ecc44d6b703844a3933"
+checksum = "3ae563653d1938f79b1ab1b5e668c87c76a9930414574a6583a7b7e11a8e6192"
 dependencies = [
  "anstyle",
  "anstyle-parse",
@@ -77,29 +77,29 @@ dependencies = [
 
 [[package]]
 name = "anstyle-query"
-version = "1.1.3"
+version = "1.1.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c8bdeb6047d8983be085bab0ba1472e6dc604e7041dbf6fcd5e71523014fae9"
+checksum = "9e231f6134f61b71076a3eab506c379d4f36122f2af15a9ff04415ea4c3339e2"
 dependencies = [
- "windows-sys 0.59.0",
+ "windows-sys 0.60.2",
 ]
 
 [[package]]
 name = "anstyle-wincon"
-version = "3.0.9"
+version = "3.0.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "403f75924867bb1033c59fbf0797484329750cfbe3c4325cd33127941fabc882"
+checksum = "3e0633414522a32ffaac8ac6cc8f748e090c5717661fddeea04219e2344f5f2a"
 dependencies = [
  "anstyle",
  "once_cell_polyfill",
- "windows-sys 0.59.0",
+ "windows-sys 0.60.2",
 ]
 
 [[package]]
 name = "anyhow"
-version = "1.0.98"
+version = "1.0.99"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
+checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100"
 
 [[package]]
 name = "autocfg"
@@ -124,9 +124,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
 
 [[package]]
 name = "bitflags"
-version = "2.9.1"
+version = "2.9.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
+checksum = "6a65b545ab31d687cff52899d4890855fec459eb6afe0da6417b8a18da87aa29"
 
 [[package]]
 name = "block-buffer"
@@ -156,9 +156,9 @@ checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
 
 [[package]]
 name = "cc"
-version = "1.2.31"
+version = "1.2.33"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3a42d84bb6b69d3a8b3eaacf0d88f179e1929695e1ad012b6cf64d9caaa5fd2"
+checksum = "3ee0f8803222ba5a7e2777dd72ca451868909b1ac410621b676adf07280e9b5f"
 dependencies = [
  "shlex",
 ]
@@ -185,9 +185,9 @@ dependencies = [
 
 [[package]]
 name = "clap"
-version = "4.5.42"
+version = "4.5.45"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed87a9d530bb41a67537289bafcac159cb3ee28460e0a4571123d2a778a6a882"
+checksum = "1fc0e74a703892159f5ae7d3aac52c8e6c392f5ae5f359c70b5881d60aaac318"
 dependencies = [
  "clap_builder",
  "clap_derive",
@@ -195,9 +195,9 @@ dependencies = [
 
 [[package]]
 name = "clap_builder"
-version = "4.5.42"
+version = "4.5.44"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64f4f3f3c77c94aff3c7e9aac9a2ca1974a5adf392a8bb751e827d6d127ab966"
+checksum = "b3e7f4214277f3c7aa526a59dd3fbe306a370daee1f8b7b8c987069cd8e888a8"
 dependencies = [
  "anstream",
  "anstyle",
@@ -208,18 +208,18 @@ dependencies = [
 
 [[package]]
 name = "clap_complete"
-version = "4.5.55"
+version = "4.5.57"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a5abde44486daf70c5be8b8f8f1b66c49f86236edf6fa2abadb4d961c4c6229a"
+checksum = "4d9501bd3f5f09f7bbee01da9a511073ed30a80cd7a509f1214bb74eadea71ad"
 dependencies = [
  "clap",
 ]
 
 [[package]]
 name = "clap_derive"
-version = "4.5.41"
+version = "4.5.45"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491"
+checksum = "14cb31bb0a7d536caef2639baa7fad459e15c3144efefa6dbd1c84562c4739f6"
 dependencies = [
  "heck",
  "proc-macro2",
@@ -559,14 +559,14 @@ dependencies = [
  "pest_derive",
  "serde",
  "serde_json",
- "thiserror 2.0.12",
+ "thiserror 2.0.15",
 ]
 
 [[package]]
 name = "hashbrown"
-version = "0.15.4"
+version = "0.15.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5971ac85611da7067dbfcabef3c70ebb5606018acd9e2a3903a0da507521e0d5"
+checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
 
 [[package]]
 name = "heck"
@@ -1035,7 +1035,7 @@ version = "6.5.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "336b9c63443aceef14bea841b899035ae3abe89b7c486aaf4c5bd8aafedac3f0"
 dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.2",
  "libc",
  "once_cell",
  "onig_sys",
@@ -1104,7 +1104,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1db05f56d34358a8b1066f67cbb203ee3e7ed2ba674a6263a1d5ec6db2204323"
 dependencies = [
  "memchr",
- "thiserror 2.0.12",
+ "thiserror 2.0.15",
  "ucd-trie",
 ]
 
@@ -1240,9 +1240,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.95"
+version = "1.0.101"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
+checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de"
 dependencies = [
  "unicode-ident",
 ]
@@ -1253,7 +1253,7 @@ version = "0.10.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "76979bea66e7875e7509c4ec5300112b316af87fa7a252ca91c448b32dfe3993"
 dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.2",
  "memchr",
  "pulldown-cmark-escape 0.10.1",
  "unicase",
@@ -1265,7 +1265,7 @@ version = "0.12.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "f86ba2052aebccc42cbbb3ed234b8b13ce76f75c3551a303cb2bcffcff12bb14"
 dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.2",
  "getopts",
  "memchr",
  "pulldown-cmark-escape 0.11.0",
@@ -1347,7 +1347,7 @@ version = "0.5.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "5407465600fb0548f1442edf71dd20683c6ed326200ace4b1ef0763521bb3b77"
 dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.2",
 ]
 
 [[package]]
@@ -1385,6 +1385,7 @@ version = "0.1.0"
 dependencies = [
  "clap",
  "env_logger",
+ "libc",
  "mdbook",
  "mdbook-i18n-helpers",
  "mdbook-spec",
@@ -1397,7 +1398,7 @@ version = "1.0.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8"
 dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.2",
  "errno",
  "libc",
  "linux-raw-sys",
@@ -1406,9 +1407,9 @@ dependencies = [
 
 [[package]]
 name = "rustversion"
-version = "1.0.21"
+version = "1.0.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
+checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
 
 [[package]]
 name = "ryu"
@@ -1546,9 +1547,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
 
 [[package]]
 name = "syn"
-version = "2.0.104"
+version = "2.0.106"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
+checksum = "ede7c438028d4436d71104916910f5bb611972c5cfd7f89b8300a8186e6fada6"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1612,12 +1613,12 @@ dependencies = [
 
 [[package]]
 name = "terminal_size"
-version = "0.4.2"
+version = "0.4.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "45c6481c4829e4cc63825e62c49186a34538b7b2750b73b266581ffb612fb5ed"
+checksum = "60b8cb979cb11c32ce1603f8137b22262a9d131aaa5c37b5678025f22b8becd0"
 dependencies = [
  "rustix",
- "windows-sys 0.59.0",
+ "windows-sys 0.60.2",
 ]
 
 [[package]]
@@ -1637,11 +1638,11 @@ dependencies = [
 
 [[package]]
 name = "thiserror"
-version = "2.0.12"
+version = "2.0.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708"
+checksum = "80d76d3f064b981389ecb4b6b7f45a0bf9fdac1d5b9204c7bd6714fecc302850"
 dependencies = [
- "thiserror-impl 2.0.12",
+ "thiserror-impl 2.0.15",
 ]
 
 [[package]]
@@ -1657,9 +1658,9 @@ dependencies = [
 
 [[package]]
 name = "thiserror-impl"
-version = "2.0.12"
+version = "2.0.15"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d"
+checksum = "44d29feb33e986b6ea906bd9c3559a856983f92371b3eaa5e83782a351623de0"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -2116,7 +2117,7 @@ version = "0.39.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
 dependencies = [
- "bitflags 2.9.1",
+ "bitflags 2.9.2",
 ]
 
 [[package]]
@@ -2183,9 +2184,9 @@ dependencies = [
 
 [[package]]
 name = "zerovec"
-version = "0.11.2"
+version = "0.11.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428"
+checksum = "e7aa2bd55086f1ab526693ecbe444205da57e25f4489879da80635a46d90e73b"
 dependencies = [
  "yoke",
  "zerofrom",
diff --git a/src/tools/rustbook/Cargo.toml b/src/tools/rustbook/Cargo.toml
index c7c6e39f157..b34c39c225d 100644
--- a/src/tools/rustbook/Cargo.toml
+++ b/src/tools/rustbook/Cargo.toml
@@ -10,6 +10,9 @@ edition = "2021"
 [dependencies]
 clap = "4.0.32"
 env_logger = "0.11"
+# FIXME: Remove this pin once this rustix issue is resolved
+# https://github.com/bytecodealliance/rustix/issues/1496
+libc = "=0.2.174"
 mdbook-trpl = { path = "../../doc/book/packages/mdbook-trpl" }
 mdbook-i18n-helpers = "0.3.3"
 mdbook-spec = { path = "../../doc/reference/mdbook-spec" }
diff --git a/src/tools/rustdoc-gui-test/src/main.rs b/src/tools/rustdoc-gui-test/src/main.rs
index 42feae8c208..5062c2597f1 100644
--- a/src/tools/rustdoc-gui-test/src/main.rs
+++ b/src/tools/rustdoc-gui-test/src/main.rs
@@ -71,22 +71,28 @@ fn main() -> Result<(), ()> {
     let mut command = Command::new(&config.nodejs);
 
     command
-        .arg(config.rust_src.join("src/tools/rustdoc-gui/tester.js"))
+        .arg(local_node_modules.join(".bin/browser-ui-test"))
         .arg("--jobs")
         .arg(&config.jobs)
-        .arg("--doc-folder")
+        .arg("--variable")
+        .arg("DOC_PATH")
         .arg(config.out_dir.join("doc"))
-        .arg("--tests-folder")
-        .arg(config.rust_src.join("tests/rustdoc-gui"));
+        .arg("--allow-file-access-from-files")
+        .arg("--display-format")
+        .arg("compact");
 
     if local_node_modules.exists() {
-        // Link the local node_modules if exists.
+        // Link the local node_modules if it exists.
         // This is useful when we run rustdoc-gui-test from outside of the source root.
         command.env("NODE_PATH", local_node_modules);
     }
 
-    for file in &config.goml_files {
-        command.arg("--file").arg(file);
+    if config.goml_files.is_empty() {
+        command.arg("--test-folder").arg(config.rust_src.join("tests/rustdoc-gui"));
+    } else {
+        for file in &config.goml_files {
+            command.arg("--test-file").arg(config.rust_src.join("tests/rustdoc-gui").join(file));
+        }
     }
 
     command.args(&config.test_args);
diff --git a/src/tools/rustdoc-gui/.eslintrc.js b/src/tools/rustdoc-gui/.eslintrc.js
deleted file mode 100644
index 3eccbc74cb9..00000000000
--- a/src/tools/rustdoc-gui/.eslintrc.js
+++ /dev/null
@@ -1,96 +0,0 @@
-module.exports = {
-    "env": {
-        "browser": true,
-        "node": true,
-        "es6": true
-    },
-    "extends": "eslint:recommended",
-    "parserOptions": {
-        "ecmaVersion": 2019,
-        "sourceType": "module"
-    },
-    "rules": {
-        "linebreak-style": [
-            "error",
-            "unix"
-        ],
-        "semi": [
-            "error",
-            "always"
-        ],
-        "quotes": [
-            "error",
-            "double"
-        ],
-        "linebreak-style": [
-            "error",
-            "unix"
-        ],
-        "no-trailing-spaces": "error",
-        "no-var": ["error"],
-        "prefer-const": ["error"],
-        "prefer-arrow-callback": ["error"],
-        "brace-style": [
-            "error",
-            "1tbs",
-            { "allowSingleLine": false }
-        ],
-        "keyword-spacing": [
-            "error",
-            { "before": true, "after": true }
-        ],
-        "arrow-spacing": [
-            "error",
-            { "before": true, "after": true }
-        ],
-        "key-spacing": [
-            "error",
-            { "beforeColon": false, "afterColon": true, "mode": "strict" }
-        ],
-        "func-call-spacing": ["error", "never"],
-        "space-infix-ops": "error",
-        "space-before-function-paren": ["error", "never"],
-        "space-before-blocks": "error",
-        "comma-dangle": ["error", "always-multiline"],
-        "comma-style": ["error", "last"],
-        "max-len": ["error", { "code": 100, "tabWidth": 4 }],
-        "eol-last": ["error", "always"],
-        "arrow-parens": ["error", "as-needed"],
-        "no-unused-vars": [
-            "error",
-            {
-                "argsIgnorePattern": "^_",
-                "varsIgnorePattern": "^_"
-            }
-        ],
-        "eqeqeq": "error",
-        "no-const-assign": "error",
-        "no-debugger": "error",
-        "no-dupe-args": "error",
-        "no-dupe-else-if": "error",
-        "no-dupe-keys": "error",
-        "no-duplicate-case": "error",
-        "no-ex-assign": "error",
-        "no-fallthrough": "error",
-        "no-invalid-regexp": "error",
-        "no-import-assign": "error",
-        "no-self-compare": "error",
-        "no-template-curly-in-string": "error",
-        "block-scoped-var": "error",
-        "guard-for-in": "error",
-        "no-alert": "error",
-        "no-confusing-arrow": "error",
-        "no-div-regex": "error",
-        "no-floating-decimal": "error",
-        "no-implicit-globals": "error",
-        "no-implied-eval": "error",
-        "no-label-var": "error",
-        "no-lonely-if": "error",
-        "no-mixed-operators": "error",
-        "no-multi-assign": "error",
-        "no-return-assign": "error",
-        "no-script-url": "error",
-        "no-sequences": "error",
-        "no-div-regex": "error",
-    }
-};
diff --git a/src/tools/rustdoc-gui/tester.js b/src/tools/rustdoc-gui/tester.js
deleted file mode 100644
index 9dc32f335a8..00000000000
--- a/src/tools/rustdoc-gui/tester.js
+++ /dev/null
@@ -1,309 +0,0 @@
-// This package needs to be install:
-//
-// ```
-// npm install browser-ui-test
-// ```
-
-const fs = require("fs");
-const path = require("path");
-const os = require("os");
-const {Options, runTest} = require("browser-ui-test");
-
-// If a test fails or errors, we will retry it two more times in case it was a flaky failure.
-const NB_RETRY = 3;
-
-function showHelp() {
-    console.log("rustdoc-js options:");
-    console.log("  --doc-folder [PATH]        : location of the generated doc folder");
-    console.log("  --file [PATH]              : file to run (can be repeated)");
-    console.log("  --debug                    : show extra information about script run");
-    console.log("  --show-text                : render font in pages");
-    console.log("  --no-headless              : disable headless mode");
-    console.log("  --help                     : show this message then quit");
-    console.log("  --tests-folder [PATH]      : location of the .GOML tests folder");
-    console.log("  --jobs [NUMBER]            : number of threads to run tests on");
-    console.log("  --executable-path [PATH]   : path of the browser's executable to be used");
-}
-
-function isNumeric(s) {
-    return /^\d+$/.test(s);
-}
-
-function parseOptions(args) {
-    const opts = {
-        "doc_folder": "",
-        "tests_folder": "",
-        "files": [],
-        "debug": false,
-        "show_text": false,
-        "no_headless": false,
-        "jobs": -1,
-        "executable_path": null,
-    };
-    const correspondences = {
-        "--doc-folder": "doc_folder",
-        "--tests-folder": "tests_folder",
-        "--debug": "debug",
-        "--show-text": "show_text",
-        "--no-headless": "no_headless",
-        "--executable-path": "executable_path",
-    };
-
-    for (let i = 0; i < args.length; ++i) {
-        const arg = args[i];
-        if (arg === "--doc-folder"
-            || arg === "--tests-folder"
-            || arg === "--file"
-            || arg === "--jobs"
-            || arg === "--executable-path") {
-            i += 1;
-            if (i >= args.length) {
-                console.log("Missing argument after `" + arg + "` option.");
-                return null;
-            }
-            const arg_value = args[i];
-            if (arg === "--jobs") {
-                if (!isNumeric(arg_value)) {
-                    console.log(
-                        "`--jobs` option expects a positive number, found `" + arg_value + "`");
-                    return null;
-                }
-                opts["jobs"] = parseInt(arg_value);
-            } else if (arg !== "--file") {
-                opts[correspondences[arg]] = arg_value;
-            } else {
-                opts["files"].push(arg_value);
-            }
-        } else if (arg === "--help") {
-            showHelp();
-            process.exit(0);
-        } else if (correspondences[arg]) {
-            opts[correspondences[arg]] = true;
-        } else {
-            console.log("Unknown option `" + arg + "`.");
-            console.log("Use `--help` to see the list of options");
-            return null;
-        }
-    }
-    if (opts["tests_folder"].length < 1) {
-        console.log("Missing `--tests-folder` option.");
-    } else if (opts["doc_folder"].length < 1) {
-        console.log("Missing `--doc-folder` option.");
-    } else {
-        return opts;
-    }
-    return null;
-}
-
-/// Print single char status information without \n
-function char_printer(n_tests) {
-    const max_per_line = 10;
-    let current = 0;
-    return {
-        successful: function() {
-            current += 1;
-            if (current % max_per_line === 0) {
-                process.stdout.write(`. (${current}/${n_tests})${os.EOL}`);
-            } else {
-                process.stdout.write(".");
-            }
-        },
-        erroneous: function() {
-            current += 1;
-            if (current % max_per_line === 0) {
-                process.stderr.write(`F (${current}/${n_tests})${os.EOL}`);
-            } else {
-                process.stderr.write("F");
-            }
-        },
-        finish: function() {
-            if (current % max_per_line === 0) {
-                // Don't output if we are already at a matching line end
-                console.log("");
-            } else {
-                const spaces = " ".repeat(max_per_line - (current % max_per_line));
-                process.stdout.write(`${spaces} (${current}/${n_tests})${os.EOL}${os.EOL}`);
-            }
-        },
-    };
-}
-
-// Sort array by .file_name property
-function by_filename(a, b) {
-    return a.file_name - b.file_name;
-}
-
-async function runTests(opts, framework_options, files, results, status_bar, showTestFailures) {
-    const tests_queue = [];
-
-    for (const testPath of files) {
-        const callback = runTest(testPath, {"options": framework_options})
-            .then(out => {
-                const [output, nb_failures] = out;
-                results[nb_failures === 0 ? "successful" : "failed"].push({
-                    file_name: testPath,
-                    output: output,
-                });
-                if (nb_failures === 0) {
-                    status_bar.successful();
-                } else if (showTestFailures) {
-                    status_bar.erroneous();
-                }
-            })
-            .catch(err => {
-                results.errored.push({
-                    file_name: testPath,
-                    output: err,
-                });
-                if (showTestFailures) {
-                    status_bar.erroneous();
-                }
-            })
-            .finally(() => {
-                // We now remove the promise from the tests_queue.
-                tests_queue.splice(tests_queue.indexOf(callback), 1);
-            });
-        tests_queue.push(callback);
-        if (opts["jobs"] > 0 && tests_queue.length >= opts["jobs"]) {
-            await Promise.race(tests_queue);
-        }
-    }
-    if (tests_queue.length > 0) {
-        await Promise.all(tests_queue);
-    }
-}
-
-function createEmptyResults() {
-    return {
-        successful: [],
-        failed: [],
-        errored: [],
-    };
-}
-
-async function main(argv) {
-    const opts = parseOptions(argv.slice(2));
-    if (opts === null) {
-        process.exit(1);
-    }
-
-    // Print successful tests too
-    let debug = false;
-    // Run tests in sequentially
-    let headless = true;
-    const framework_options = new Options();
-    try {
-        // This is more convenient that setting fields one by one.
-        const args = [
-            "--variable", "DOC_PATH", opts["doc_folder"].split("\\").join("/"),
-            "--allow-file-access-from-files",
-        ];
-        if (opts["debug"]) {
-            debug = true;
-            args.push("--debug");
-        }
-        if (opts["show_text"]) {
-            args.push("--show-text");
-        }
-        if (opts["no_headless"]) {
-            args.push("--no-headless");
-            headless = false;
-        }
-        if (opts["executable_path"] !== null) {
-            args.push("--executable-path");
-            args.push(opts["executable_path"]);
-        }
-        framework_options.parseArguments(args);
-    } catch (error) {
-        console.error(`invalid argument: ${error}`);
-        process.exit(1);
-    }
-
-    let files;
-    if (opts["files"].length === 0) {
-        files = fs.readdirSync(opts["tests_folder"]);
-    } else {
-        files = opts["files"];
-    }
-    files = files.filter(file => path.extname(file) === ".goml");
-    if (files.length === 0) {
-        console.error("rustdoc-gui: No test selected");
-        process.exit(2);
-    }
-    files.forEach((file_name, index) => {
-        files[index] = path.join(opts["tests_folder"], file_name);
-    });
-    files.sort();
-
-    if (!headless) {
-        opts["jobs"] = 1;
-        console.log("`--no-headless` option is active, disabling concurrency for running tests.");
-    }
-
-    if (opts["jobs"] < 1) {
-        const len = files.length;
-        console.log(
-            `Running ${len} rustdoc-gui (UNBOUNDED concurrency; use "-j#" for a limit) ...`,
-        );
-        process.setMaxListeners(files.length + 1);
-    } else if (headless) {
-        console.log(`Running ${files.length} rustdoc-gui (${opts["jobs"]} concurrently) ...`);
-        process.setMaxListeners(opts["jobs"] + 1);
-    } else {
-        console.log(`Running ${files.length} rustdoc-gui ...`);
-    }
-
-    const originalFilesLen = files.length;
-    const results = createEmptyResults();
-    const status_bar = char_printer(files.length);
-
-    let new_results;
-    for (let it = 0; it < NB_RETRY && files.length > 0; ++it) {
-        new_results = createEmptyResults();
-        await runTests(opts, framework_options, files, new_results, status_bar, it + 1 >= NB_RETRY);
-        Array.prototype.push.apply(results.successful, new_results.successful);
-        // We generate the new list of files with the previously failing tests.
-        files = Array.prototype.concat(new_results.failed, new_results.errored).map(
-            f => f["file_name"]);
-        if (files.length > originalFilesLen / 2) {
-            // If we have too many failing tests, it's very likely not flaky failures anymore so
-            // no need to retry.
-            break;
-        }
-    }
-
-    status_bar.finish();
-
-    Array.prototype.push.apply(results.failed, new_results.failed);
-    Array.prototype.push.apply(results.errored, new_results.errored);
-
-    if (debug) {
-        results.successful.sort(by_filename);
-        results.successful.forEach(r => {
-            console.log(r.output);
-        });
-    }
-
-    if (results.failed.length > 0) {
-        console.log("");
-        results.failed.sort(by_filename);
-        results.failed.forEach(r => {
-            console.log(r.file_name, r.output);
-        });
-    }
-    if (results.errored.length > 0) {
-        console.log(os.EOL);
-        // print run errors on the bottom so developers see them better
-        results.errored.sort(by_filename);
-        results.errored.forEach(r => {
-            console.error(r.file_name, r.output);
-        });
-    }
-
-    if (results.failed.length > 0 || results.errored.length > 0) {
-        process.exit(1);
-    }
-    process.exit(0);
-}
-
-main(process.argv);
diff --git a/src/tools/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js
index 0baa179e16b..ff809d58e90 100644
--- a/src/tools/rustdoc-js/tester.js
+++ b/src/tools/rustdoc-js/tester.js
@@ -1,7 +1,8 @@
 /* global globalThis */
+
 const fs = require("fs");
 const path = require("path");
-
+const { isGeneratorObject } = require("util/types");
 
 function arrayToCode(array) {
     return array.map((value, index) => {
@@ -45,23 +46,16 @@ function shouldIgnoreField(fieldName) {
 }
 
 function valueMapper(key, testOutput) {
-    const isAlias = testOutput["is_alias"];
     let value = testOutput[key];
     // To make our life easier, if there is a "parent" type, we add it to the path.
     if (key === "path") {
-        if (testOutput["parent"] !== undefined) {
+        if (testOutput["parent"]) {
             if (value.length > 0) {
                 value += "::" + testOutput["parent"]["name"];
             } else {
                 value = testOutput["parent"]["name"];
             }
-        } else if (testOutput["is_alias"]) {
-            value = valueMapper(key, testOutput["original"]);
         }
-    } else if (isAlias && key === "alias") {
-        value = testOutput["name"];
-    } else if (isAlias && ["name"].includes(key)) {
-        value = testOutput["original"][key];
     }
     return value;
 }
@@ -237,7 +231,7 @@ async function runSearch(query, expected, doSearch, loadedFile, queryName) {
     const ignore_order = loadedFile.ignore_order;
     const exact_check = loadedFile.exact_check;
 
-    const results = await doSearch(query, loadedFile.FILTER_CRATE);
+    const { resultsTable } = await doSearch(query, loadedFile.FILTER_CRATE);
     const error_text = [];
 
     for (const key in expected) {
@@ -247,37 +241,38 @@ async function runSearch(query, expected, doSearch, loadedFile, queryName) {
         if (!Object.prototype.hasOwnProperty.call(expected, key)) {
             continue;
         }
-        if (!Object.prototype.hasOwnProperty.call(results, key)) {
+        if (!Object.prototype.hasOwnProperty.call(resultsTable, key)) {
             error_text.push("==> Unknown key \"" + key + "\"");
             break;
         }
         const entry = expected[key];
 
-        if (exact_check && entry.length !== results[key].length) {
+        if (exact_check && entry.length !== resultsTable[key].length) {
             error_text.push(queryName + "==> Expected exactly " + entry.length +
-                            " results but found " + results[key].length + " in '" + key + "'");
+                            " results but found " + resultsTable[key].length + " in '" + key + "'");
         }
 
         let prev_pos = -1;
         for (const [index, elem] of entry.entries()) {
-            const entry_pos = lookForEntry(elem, results[key]);
+            const entry_pos = lookForEntry(elem, resultsTable[key]);
             if (entry_pos === -1) {
                 error_text.push(queryName + "==> Result not found in '" + key + "': '" +
                                 JSON.stringify(elem) + "'");
                 // By default, we just compare the two first items.
                 let item_to_diff = 0;
-                if ((!ignore_order || exact_check) && index < results[key].length) {
+                if ((!ignore_order || exact_check) && index < resultsTable[key].length) {
                     item_to_diff = index;
                 }
                 error_text.push("Diff of first error:\n" +
-                    betterLookingDiff(elem, results[key][item_to_diff]));
+                    betterLookingDiff(elem, resultsTable[key][item_to_diff]));
             } else if (exact_check === true && prev_pos + 1 !== entry_pos) {
                 error_text.push(queryName + "==> Exact check failed at position " + (prev_pos + 1) +
                                 ": expected '" + JSON.stringify(elem) + "' but found '" +
-                                JSON.stringify(results[key][index]) + "'");
+                                JSON.stringify(resultsTable[key][index]) + "'");
             } else if (ignore_order === false && entry_pos < prev_pos) {
-                error_text.push(queryName + "==> '" + JSON.stringify(elem) + "' was supposed " +
-                                "to be before '" + JSON.stringify(results[key][prev_pos]) + "'");
+                error_text.push(queryName + "==> '" +
+                                JSON.stringify(elem) + "' was supposed to be before '" +
+                                JSON.stringify(resultsTable[key][prev_pos]) + "'");
             } else {
                 prev_pos = entry_pos;
             }
@@ -286,19 +281,20 @@ async function runSearch(query, expected, doSearch, loadedFile, queryName) {
     return error_text;
 }
 
-async function runCorrections(query, corrections, getCorrections, loadedFile) {
-    const qc = await getCorrections(query, loadedFile.FILTER_CRATE);
+async function runCorrections(query, corrections, doSearch, loadedFile) {
+    const { parsedQuery } = await doSearch(query, loadedFile.FILTER_CRATE);
+    const qc = parsedQuery.correction;
     const error_text = [];
 
     if (corrections === null) {
         if (qc !== null) {
-            error_text.push(`==> expected = null, found = ${qc}`);
+            error_text.push(`==> [correction] expected = null, found = ${qc}`);
         }
         return error_text;
     }
 
-    if (qc !== corrections.toLowerCase()) {
-        error_text.push(`==> expected = ${corrections}, found = ${qc}`);
+    if (qc.toLowerCase() !== corrections.toLowerCase()) {
+        error_text.push(`==> [correction] expected = ${corrections}, found = ${qc}`);
     }
 
     return error_text;
@@ -320,7 +316,7 @@ function checkResult(error_text, loadedFile, displaySuccess) {
     return 1;
 }
 
-async function runCheckInner(callback, loadedFile, entry, getCorrections, extra) {
+async function runCheckInner(callback, loadedFile, entry, extra, doSearch) {
     if (typeof entry.query !== "string") {
         console.log("FAILED");
         console.log("==> Missing `query` field");
@@ -338,7 +334,7 @@ async function runCheckInner(callback, loadedFile, entry, getCorrections, extra)
         error_text = await runCorrections(
             entry.query,
             entry.correction,
-            getCorrections,
+            doSearch,
             loadedFile,
         );
         if (checkResult(error_text, loadedFile, false) !== 0) {
@@ -348,16 +344,16 @@ async function runCheckInner(callback, loadedFile, entry, getCorrections, extra)
     return true;
 }
 
-async function runCheck(loadedFile, key, getCorrections, callback) {
+async function runCheck(loadedFile, key, doSearch, callback) {
     const expected = loadedFile[key];
 
     if (Array.isArray(expected)) {
         for (const entry of expected) {
-            if (!await runCheckInner(callback, loadedFile, entry, getCorrections, true)) {
+            if (!await runCheckInner(callback, loadedFile, entry, true, doSearch)) {
                 return 1;
             }
         }
-    } else if (!await runCheckInner(callback, loadedFile, expected, getCorrections, false)) {
+    } else if (!await runCheckInner(callback, loadedFile, expected, false, doSearch)) {
         return 1;
     }
     console.log("OK");
@@ -368,7 +364,7 @@ function hasCheck(content, checkName) {
     return content.startsWith(`const ${checkName}`) || content.includes(`\nconst ${checkName}`);
 }
 
-async function runChecks(testFile, doSearch, parseQuery, getCorrections) {
+async function runChecks(testFile, doSearch, parseQuery) {
     let checkExpected = false;
     let checkParsed = false;
     let testFileContent = readFile(testFile);
@@ -397,18 +393,36 @@ async function runChecks(testFile, doSearch, parseQuery, getCorrections) {
     let res = 0;
 
     if (checkExpected) {
-        res += await runCheck(loadedFile, "EXPECTED", getCorrections, (query, expected, text) => {
+        res += await runCheck(loadedFile, "EXPECTED", doSearch, (query, expected, text) => {
             return runSearch(query, expected, doSearch, loadedFile, text);
         });
     }
     if (checkParsed) {
-        res += await runCheck(loadedFile, "PARSED", getCorrections, (query, expected, text) => {
+        res += await runCheck(loadedFile, "PARSED", doSearch, (query, expected, text) => {
             return runParser(query, expected, parseQuery, text);
         });
     }
     return res;
 }
 
+function mostRecentMatch(staticFiles, regex) {
+    const matchingEntries = fs.readdirSync(staticFiles)
+        .filter(f => f.match(regex))
+        .map(f => {
+            const stats = fs.statSync(path.join(staticFiles, f));
+            return {
+                path: f,
+                time: stats.mtimeMs,
+            };
+        });
+    if (matchingEntries.length === 0) {
+        throw "No static file matching regex";
+    }
+    // We sort entries in descending order.
+    matchingEntries.sort((a, b) => b.time - a.time);
+    return matchingEntries[0].path;
+}
+
 /**
  * Load searchNNN.js and search-indexNNN.js.
  *
@@ -416,71 +430,89 @@ async function runChecks(testFile, doSearch, parseQuery, getCorrections) {
  * @param {string} resource_suffix - Version number between filename and .js, e.g. "1.59.0"
  * @returns {Object}               - Object containing keys: `doSearch`, which runs a search
  *   with the loaded index and returns a table of results; `parseQuery`, which is the
- *   `parseQuery` function exported from the search module; and `getCorrections`, which runs
+ *   `parseQuery` function exported from the search module, which runs
  *   a search but returns type name corrections instead of results.
  */
-function loadSearchJS(doc_folder, resource_suffix) {
-    const searchIndexJs = path.join(doc_folder, "search-index" + resource_suffix + ".js");
-    const searchIndex = require(searchIndexJs);
-
-    globalThis.searchState = {
-        descShards: new Map(),
-        loadDesc: async function({descShard, descIndex}) {
-            if (descShard.promise === null) {
-                descShard.promise = new Promise((resolve, reject) => {
-                    descShard.resolve = resolve;
-                    const ds = descShard;
-                    const fname = `${ds.crate}-desc-${ds.shard}-${resource_suffix}.js`;
-                    fs.readFile(
-                        `${doc_folder}/search.desc/${descShard.crate}/${fname}`,
-                        (err, data) => {
-                            if (err) {
-                                reject(err);
-                            } else {
-                                eval(data.toString("utf8"));
-                            }
-                        },
-                    );
-                });
-            }
-            const list = await descShard.promise;
-            return list[descIndex];
-        },
-        loadedDescShard: function(crate, shard, data) {
-            this.descShards.get(crate)[shard].resolve(data.split("\n"));
-        },
-    };
-
+async function loadSearchJS(doc_folder, resource_suffix) {
     const staticFiles = path.join(doc_folder, "static.files");
-    const searchJs = fs.readdirSync(staticFiles).find(f => f.match(/search.*\.js$/));
+    const stringdexJs = mostRecentMatch(staticFiles, /stringdex.*\.js$/);
+    const stringdexModule = require(path.join(staticFiles, stringdexJs));
+    const searchJs = mostRecentMatch(staticFiles, /search-[0-9a-f]{8}.*\.js$/);
     const searchModule = require(path.join(staticFiles, searchJs));
-    searchModule.initSearch(searchIndex.searchIndex);
-    const docSearch = searchModule.docSearch;
+    globalThis.nonnull = (x, msg) => {
+        if (x === null) {
+            throw (msg || "unexpected null value!");
+        } else {
+            return x;
+        }
+    };
+    const { docSearch, DocSearch } = await searchModule.initSearch(
+        stringdexModule.Stringdex,
+        stringdexModule.RoaringBitmap,
+        {
+            loadRoot: callbacks => {
+                for (const key in callbacks) {
+                    if (Object.hasOwn(callbacks, key)) {
+                        globalThis[key] = callbacks[key];
+                    }
+                }
+                const rootJs = readFile(path.join(doc_folder, "search.index/root" +
+                    resource_suffix + ".js"));
+                eval(rootJs);
+            },
+            loadTreeByHash: hashHex => {
+                const shardJs = readFile(path.join(doc_folder, "search.index/" + hashHex + ".js"));
+                eval(shardJs);
+            },
+            loadDataByNameAndHash: (name, hashHex) => {
+                const shardJs = readFile(path.join(doc_folder, "search.index/" + name + "/" +
+                    hashHex + ".js"));
+                eval(shardJs);
+            },
+        },
+    );
     return {
         doSearch: async function(queryStr, filterCrate, currentCrate) {
-            const result = await docSearch.execQuery(searchModule.parseQuery(queryStr),
-                filterCrate, currentCrate);
+            const parsedQuery = DocSearch.parseQuery(queryStr);
+            const result = await docSearch.execQuery(parsedQuery, filterCrate, currentCrate);
+            const resultsTable = {};
             for (const tab in result) {
                 if (!Object.prototype.hasOwnProperty.call(result, tab)) {
                     continue;
                 }
-                if (!(result[tab] instanceof Array)) {
+                if (!isGeneratorObject(result[tab])) {
                     continue;
                 }
-                for (const entry of result[tab]) {
+                resultsTable[tab] = [];
+                for await (const entry of result[tab]) {
+                    const flatEntry = Object.assign({
+                        crate: entry.item.crate,
+                        name: entry.item.name,
+                        path: entry.item.modulePath,
+                        exactPath: entry.item.exactModulePath,
+                        ty: entry.item.ty,
+                    }, entry);
                     for (const key in entry) {
                         if (!Object.prototype.hasOwnProperty.call(entry, key)) {
                             continue;
                         }
-                        if (key === "displayTypeSignature" && entry.displayTypeSignature !== null) {
-                            const {type, mappedNames, whereClause} =
-                                await entry.displayTypeSignature;
-                            entry.displayType = arrayToCode(type);
-                            entry.displayMappedNames = [...mappedNames.entries()]
+                        if (key === "desc" && entry.desc !== null) {
+                            flatEntry.desc = await entry.desc;
+                        } else if (key === "displayTypeSignature" &&
+                            entry.displayTypeSignature !== null
+                        ) {
+                            flatEntry.displayTypeSignature = await entry.displayTypeSignature;
+                            const {
+                                type,
+                                mappedNames,
+                                whereClause,
+                            } = flatEntry.displayTypeSignature;
+                            flatEntry.displayType = arrayToCode(type);
+                            flatEntry.displayMappedNames = [...mappedNames.entries()]
                                 .map(([name, qname]) => {
                                     return `${name} = ${qname}`;
                                 }).join(", ");
-                            entry.displayWhereClause = [...whereClause.entries()]
+                            flatEntry.displayWhereClause = [...whereClause.entries()]
                                 .flatMap(([name, value]) => {
                                     if (value.length === 0) {
                                         return [];
@@ -489,16 +521,12 @@ function loadSearchJS(doc_folder, resource_suffix) {
                                 }).join(", ");
                         }
                     }
+                    resultsTable[tab].push(flatEntry);
                 }
             }
-            return result;
+            return { resultsTable, parsedQuery };
         },
-        getCorrections: function(queryStr, filterCrate, currentCrate) {
-            const parsedQuery = searchModule.parseQuery(queryStr);
-            docSearch.execQuery(parsedQuery, filterCrate, currentCrate);
-            return parsedQuery.correction;
-        },
-        parseQuery: searchModule.parseQuery,
+        parseQuery: DocSearch.parseQuery,
     };
 }
 
@@ -570,7 +598,7 @@ async function main(argv) {
         return 1;
     }
 
-    const parseAndSearch = loadSearchJS(
+    const parseAndSearch = await loadSearchJS(
         opts["doc_folder"],
         opts["resource_suffix"],
     );
@@ -579,14 +607,11 @@ async function main(argv) {
     const doSearch = function(queryStr, filterCrate) {
         return parseAndSearch.doSearch(queryStr, filterCrate, opts["crate_name"]);
     };
-    const getCorrections = function(queryStr, filterCrate) {
-        return parseAndSearch.getCorrections(queryStr, filterCrate, opts["crate_name"]);
-    };
 
     if (opts["test_file"].length !== 0) {
         for (const file of opts["test_file"]) {
             process.stdout.write(`Testing ${file} ... `);
-            errors += await runChecks(file, doSearch, parseAndSearch.parseQuery, getCorrections);
+            errors += await runChecks(file, doSearch, parseAndSearch.parseQuery);
         }
     } else if (opts["test_folder"].length !== 0) {
         for (const file of fs.readdirSync(opts["test_folder"])) {
@@ -595,7 +620,7 @@ async function main(argv) {
             }
             process.stdout.write(`Testing ${file} ... `);
             errors += await runChecks(path.join(opts["test_folder"], file), doSearch,
-                    parseAndSearch.parseQuery, getCorrections);
+                    parseAndSearch.parseQuery);
         }
     }
     return errors > 0 ? 1 : 0;
diff --git a/src/tools/rustfmt/Cargo.toml b/src/tools/rustfmt/Cargo.toml
index e497b792342..6392ffbe409 100644
--- a/src/tools/rustfmt/Cargo.toml
+++ b/src/tools/rustfmt/Cargo.toml
@@ -40,7 +40,7 @@ cargo_metadata = "0.18"
 clap = { version = "4.4.2", features = ["derive"] }
 clap-cargo = "0.12.0"
 diff = "0.1"
-dirs = "5.0"
+dirs = "6.0"
 getopts = "0.2"
 ignore = "0.4"
 itertools = "0.12"
diff --git a/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs
index 57d4142ebe4..75e468b3525 100644
--- a/src/tools/rustfmt/src/items.rs
+++ b/src/tools/rustfmt/src/items.rs
@@ -958,20 +958,19 @@ fn format_impl_ref_and_type(
     offset: Indent,
 ) -> Option<String> {
     let ast::Impl {
-        safety,
-        polarity,
-        defaultness,
-        constness,
-        ref generics,
-        of_trait: ref trait_ref,
-        ref self_ty,
-        ..
-    } = *iimpl;
+        generics,
+        of_trait,
+        self_ty,
+        items: _,
+    } = iimpl;
     let mut result = String::with_capacity(128);
 
     result.push_str(&format_visibility(context, &item.vis));
-    result.push_str(format_defaultness(defaultness));
-    result.push_str(format_safety(safety));
+
+    if let Some(of_trait) = of_trait.as_deref() {
+        result.push_str(format_defaultness(of_trait.defaultness));
+        result.push_str(format_safety(of_trait.safety));
+    }
 
     let shape = if context.config.style_edition() >= StyleEdition::Edition2024 {
         Shape::indented(offset + last_line_width(&result), context.config)
@@ -984,28 +983,24 @@ fn format_impl_ref_and_type(
     };
     let generics_str = rewrite_generics(context, "impl", generics, shape).ok()?;
     result.push_str(&generics_str);
-    result.push_str(format_constness_right(constness));
-
-    let polarity_str = match polarity {
-        ast::ImplPolarity::Negative(_) => "!",
-        ast::ImplPolarity::Positive => "",
-    };
 
-    let polarity_overhead;
     let trait_ref_overhead;
-    if let Some(ref trait_ref) = *trait_ref {
+    if let Some(of_trait) = of_trait.as_deref() {
+        result.push_str(format_constness_right(of_trait.constness));
+        let polarity_str = match of_trait.polarity {
+            ast::ImplPolarity::Negative(_) => "!",
+            ast::ImplPolarity::Positive => "",
+        };
         let result_len = last_line_width(&result);
         result.push_str(&rewrite_trait_ref(
             context,
-            trait_ref,
+            &of_trait.trait_ref,
             offset,
             polarity_str,
             result_len,
         )?);
-        polarity_overhead = 0; // already written
         trait_ref_overhead = " for".len();
     } else {
-        polarity_overhead = polarity_str.len();
         trait_ref_overhead = 0;
     }
 
@@ -1020,17 +1015,15 @@ fn format_impl_ref_and_type(
     } else {
         0
     };
-    let used_space =
-        last_line_width(&result) + polarity_overhead + trait_ref_overhead + curly_brace_overhead;
+    let used_space = last_line_width(&result) + trait_ref_overhead + curly_brace_overhead;
     // 1 = space before the type.
     let budget = context.budget(used_space + 1);
     if let Some(self_ty_str) = self_ty.rewrite(context, Shape::legacy(budget, offset)) {
         if !self_ty_str.contains('\n') {
-            if trait_ref.is_some() {
+            if of_trait.is_some() {
                 result.push_str(" for ");
             } else {
                 result.push(' ');
-                result.push_str(polarity_str);
             }
             result.push_str(&self_ty_str);
             return Some(result);
@@ -1042,12 +1035,10 @@ fn format_impl_ref_and_type(
     // Add indentation of one additional tab.
     let new_line_offset = offset.block_indent(context.config);
     result.push_str(&new_line_offset.to_string(context.config));
-    if trait_ref.is_some() {
+    if of_trait.is_some() {
         result.push_str("for ");
-    } else {
-        result.push_str(polarity_str);
     }
-    let budget = context.budget(last_line_width(&result) + polarity_overhead);
+    let budget = context.budget(last_line_width(&result));
     let type_offset = match context.config.indent_style() {
         IndentStyle::Visual => new_line_offset + trait_ref_overhead,
         IndentStyle::Block => new_line_offset,
@@ -1184,8 +1175,8 @@ pub(crate) fn format_trait(
     let mut result = String::with_capacity(128);
     let header = format!(
         "{}{}{}{}trait ",
-        format_constness(constness),
         format_visibility(context, &item.vis),
+        format_constness(constness),
         format_safety(safety),
         format_auto(is_auto),
     );
@@ -3609,7 +3600,7 @@ pub(crate) fn rewrite_extern_crate(
 pub(crate) fn is_mod_decl(item: &ast::Item) -> bool {
     !matches!(
         item.kind,
-        ast::ItemKind::Mod(_, _, ast::ModKind::Loaded(_, ast::Inline::Yes, _, _))
+        ast::ItemKind::Mod(_, _, ast::ModKind::Loaded(_, ast::Inline::Yes, _))
     )
 }
 
diff --git a/src/tools/rustfmt/src/modules.rs b/src/tools/rustfmt/src/modules.rs
index 3bc656b64b3..af9feccb32e 100644
--- a/src/tools/rustfmt/src/modules.rs
+++ b/src/tools/rustfmt/src/modules.rs
@@ -316,11 +316,12 @@ impl<'ast, 'psess, 'c> ModResolver<'ast, 'psess> {
             self.directory = directory;
         }
         match (sub_mod.ast_mod_kind, sub_mod.items) {
-            (Some(Cow::Borrowed(ast::ModKind::Loaded(items, _, _, _))), _) => {
+            (Some(Cow::Borrowed(ast::ModKind::Loaded(items, _, _))), _) => {
                 self.visit_mod_from_ast(items)
             }
-            (Some(Cow::Owned(ast::ModKind::Loaded(items, _, _, _))), _)
-            | (_, Cow::Owned(items)) => self.visit_mod_outside_ast(items),
+            (Some(Cow::Owned(ast::ModKind::Loaded(items, _, _))), _) | (_, Cow::Owned(items)) => {
+                self.visit_mod_outside_ast(items)
+            }
             (_, _) => Ok(()),
         }
     }
diff --git a/src/tools/rustfmt/src/parse/parser.rs b/src/tools/rustfmt/src/parse/parser.rs
index 2ec8769c45f..63c6c8c99d0 100644
--- a/src/tools/rustfmt/src/parse/parser.rs
+++ b/src/tools/rustfmt/src/parse/parser.rs
@@ -3,6 +3,7 @@ use std::path::{Path, PathBuf};
 
 use rustc_ast::{ast, attr};
 use rustc_errors::Diag;
+use rustc_parse::lexer::StripTokens;
 use rustc_parse::parser::Parser as RawParser;
 use rustc_parse::{exp, new_parser_from_file, new_parser_from_source_str, unwrap_or_emit_fatal};
 use rustc_span::{Span, sym};
@@ -64,11 +65,14 @@ impl<'a> ParserBuilder<'a> {
         input: Input,
     ) -> Result<RawParser<'a>, Vec<Diag<'a>>> {
         match input {
-            Input::File(ref file) => new_parser_from_file(psess, file, None),
+            Input::File(ref file) => {
+                new_parser_from_file(psess, file, StripTokens::ShebangAndFrontmatter, None)
+            }
             Input::Text(text) => new_parser_from_source_str(
                 psess,
                 rustc_span::FileName::Custom("stdin".to_owned()),
                 text,
+                StripTokens::ShebangAndFrontmatter,
             ),
         }
     }
@@ -104,8 +108,12 @@ impl<'a> Parser<'a> {
         span: Span,
     ) -> Result<(ast::AttrVec, ThinVec<Box<ast::Item>>, Span), ParserError> {
         let result = catch_unwind(AssertUnwindSafe(|| {
-            let mut parser =
-                unwrap_or_emit_fatal(new_parser_from_file(psess.inner(), path, Some(span)));
+            let mut parser = unwrap_or_emit_fatal(new_parser_from_file(
+                psess.inner(),
+                path,
+                StripTokens::ShebangAndFrontmatter,
+                Some(span),
+            ));
             match parser.parse_mod(exp!(Eof)) {
                 Ok((a, i, spans)) => Some((a, i, spans.inner_span)),
                 Err(e) => {
diff --git a/src/tools/rustfmt/src/patterns.rs b/src/tools/rustfmt/src/patterns.rs
index d212ecf392a..848bd0766e7 100644
--- a/src/tools/rustfmt/src/patterns.rs
+++ b/src/tools/rustfmt/src/patterns.rs
@@ -303,7 +303,7 @@ impl Rewrite for Pat {
                 qself,
                 path,
                 fields,
-                rest == ast::PatFieldsRest::Rest,
+                matches!(rest, ast::PatFieldsRest::Rest(_)),
                 self.span,
                 context,
                 shape,
diff --git a/src/tools/rustfmt/src/visitor.rs b/src/tools/rustfmt/src/visitor.rs
index 23d07c930d9..a3acbb218ff 100644
--- a/src/tools/rustfmt/src/visitor.rs
+++ b/src/tools/rustfmt/src/visitor.rs
@@ -942,7 +942,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
         let ident_str = rewrite_ident(&self.get_context(), ident).to_owned();
         self.push_str(&ident_str);
 
-        if let ast::ModKind::Loaded(ref items, ast::Inline::Yes, ref spans, _) = mod_kind {
+        if let ast::ModKind::Loaded(ref items, ast::Inline::Yes, ref spans) = mod_kind {
             let ast::ModSpans {
                 inner_span,
                 inject_use_span: _,
diff --git a/src/tools/rustfmt/tests/source/frontmatter_compact.rs b/src/tools/rustfmt/tests/source/frontmatter_compact.rs
new file mode 100644
index 00000000000..21d4c6f4b61
--- /dev/null
+++ b/src/tools/rustfmt/tests/source/frontmatter_compact.rs
@@ -0,0 +1,8 @@
+#!/usr/bin/env cargo
+---identifier
+[dependencies]
+regex = "1"
+---
+#![feature(frontmatter)]
+
+fn main() {}
diff --git a/src/tools/rustfmt/tests/source/frontmatter_escaped.rs b/src/tools/rustfmt/tests/source/frontmatter_escaped.rs
new file mode 100644
index 00000000000..0d026377566
--- /dev/null
+++ b/src/tools/rustfmt/tests/source/frontmatter_escaped.rs
@@ -0,0 +1,13 @@
+#!/usr/bin/env cargo
+------------
+package.description = """
+Header
+-----
+
+Body
+"""
+------------
+
+#![feature(frontmatter)]
+
+fn main() {}
diff --git a/src/tools/rustfmt/tests/source/frontmatter_spaced.rs b/src/tools/rustfmt/tests/source/frontmatter_spaced.rs
new file mode 100644
index 00000000000..ee0bb81705c
--- /dev/null
+++ b/src/tools/rustfmt/tests/source/frontmatter_spaced.rs
@@ -0,0 +1,16 @@
+#!/usr/bin/env cargo
+
+
+---   identifier
+[dependencies]
+regex = "1"
+
+---
+
+
+
+
+
+#![feature(frontmatter)]
+
+fn main() {}
diff --git a/src/tools/rustfmt/tests/source/negative-impl.rs b/src/tools/rustfmt/tests/source/negative-impl.rs
index da242d4f3dc..e8f9508e656 100644
--- a/src/tools/rustfmt/tests/source/negative-impl.rs
+++ b/src/tools/rustfmt/tests/source/negative-impl.rs
@@ -1,7 +1,3 @@
 impl ! Display for JoinHandle { }
 
-impl ! Box < JoinHandle > { }
-
 impl ! std :: fmt :: Display for JoinHandle < T : std :: future :: Future + std :: marker :: Send + std :: marker :: Sync > { }
-
-impl ! JoinHandle < T : std :: future :: Future < Output > + std :: marker :: Send + std :: marker :: Sync + 'static > + 'static { }
diff --git a/src/tools/rustfmt/tests/target/frontmatter_compact.rs b/src/tools/rustfmt/tests/target/frontmatter_compact.rs
new file mode 100644
index 00000000000..21d4c6f4b61
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/frontmatter_compact.rs
@@ -0,0 +1,8 @@
+#!/usr/bin/env cargo
+---identifier
+[dependencies]
+regex = "1"
+---
+#![feature(frontmatter)]
+
+fn main() {}
diff --git a/src/tools/rustfmt/tests/target/frontmatter_escaped.rs b/src/tools/rustfmt/tests/target/frontmatter_escaped.rs
new file mode 100644
index 00000000000..0d026377566
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/frontmatter_escaped.rs
@@ -0,0 +1,13 @@
+#!/usr/bin/env cargo
+------------
+package.description = """
+Header
+-----
+
+Body
+"""
+------------
+
+#![feature(frontmatter)]
+
+fn main() {}
diff --git a/src/tools/rustfmt/tests/target/frontmatter_spaced.rs b/src/tools/rustfmt/tests/target/frontmatter_spaced.rs
new file mode 100644
index 00000000000..ee0bb81705c
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/frontmatter_spaced.rs
@@ -0,0 +1,16 @@
+#!/usr/bin/env cargo
+
+
+---   identifier
+[dependencies]
+regex = "1"
+
+---
+
+
+
+
+
+#![feature(frontmatter)]
+
+fn main() {}
diff --git a/src/tools/rustfmt/tests/target/negative-impl.rs b/src/tools/rustfmt/tests/target/negative-impl.rs
index 16ce7e26a99..bb53048dbc6 100644
--- a/src/tools/rustfmt/tests/target/negative-impl.rs
+++ b/src/tools/rustfmt/tests/target/negative-impl.rs
@@ -1,14 +1,6 @@
 impl !Display for JoinHandle {}
 
-impl !Box<JoinHandle> {}
-
 impl !std::fmt::Display
     for JoinHandle<T: std::future::Future + std::marker::Send + std::marker::Sync>
 {
 }
-
-impl
-    !JoinHandle<T: std::future::Future<Output> + std::marker::Send + std::marker::Sync + 'static>
-        + 'static
-{
-}
diff --git a/src/tools/tidy/Readme.md b/src/tools/tidy/Readme.md
new file mode 100644
index 00000000000..fc9dc6350e9
--- /dev/null
+++ b/src/tools/tidy/Readme.md
@@ -0,0 +1,112 @@
+# Tidy
+Tidy is the Rust project's custom internal linter and a crucial part of our testing and continuous integration (CI) infrastructure. It is designed to enforce a consistent style and formatting across the entire codebase, but its role extends beyond simple linting. Tidy also helps with infrastructure, policy, and documentation, ensuring the project remains organized, functional, and... tidy.
+
+This document will cover how to use tidy, the specific checks tidy performs, and using tidy directives to manage its behavior. By understanding and utilizing tidy, you can help us maintain the high standards of the Rust project.
+## Tidy Checks
+### Style and Code Quality
+These lints focus on enforcing consistent formatting, style, and general code health.
+* [`alphabetical`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/alphabetical/index.html): Checks that lists are sorted alphabetically
+* [`style`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/style/index.html): Check to enforce various stylistic guidelines on the Rust codebase.
+* [`filenames`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/filenames/index.html): Check to prevent invalid characters in file names.
+* [`pal`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/pal/index.html): Check to enforce rules about platform-specific code in std.
+* [`target_policy`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/target_policy/index.html): Check for target tier policy compliance.
+* [`error_codes`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/error_codes/index.html): Check to ensure error codes are properly documented and tested.
+
+### Infrastructure
+These checks focus on the integrity of the project's dependencies, internal tools, and documentation.
+* [`bins`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/bins/index.html): Prevent stray binaries from being checked into the source tree.
+* [`fluent_alphabetical`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/fluent_alphabetical/index.html) / [`fluent_period`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/fluent_period/index.html) / [`fluent_used`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/fluent_used/index.html): Various checks related to [Fluent](https://github.com/projectfluent) for localization and natural language translation.
+* [`deps`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/deps/index.html) / [`extdeps`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/extdeps/index.html): Check for valid licenses and sources for external dependencies.
+* [`gcc_submodule`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/gcc_submodule/index.html): Check that the `src/gcc` submodule version is valid.
+* [`triagebot`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/triagebot/index.html): Check to ensure paths mentioned in `triagebot.toml` exist in the project.
+* [`x_version`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/x_version/index.html): Validates the current version of the `x` tool.
+
+* [`edition`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/edition/index.html) / [`features`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/features/index.html): Check for a valid Rust edition and proper ordering of unstable features.
+* [`rustdoc_css_themes`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/rustdoc_css_themes/index.html) / [`rustdoc_templates`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/rustdoc_templates/index.html): Verify that Rust documentation templates and themes are correct.
+* [`unstable_book`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/unstable_book/index.html): Synchronizes the unstable book with unstable features.
+### Testing
+These checks ensure that tests are correctly structured, cleaned up, and free of common errors.
+* [`tests_placement`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/tests_placement/index.html) / [`unit_tests`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/unit_tests/index.html): Verify that tests are located in the correct directories and are not using improper attributes.
+* [`known_bug`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/known_bug/index.html) / [`unknown_revision`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/unknown_revision/index.html): Ensure that test directives and annotations are used correctly.
+* [`debug_artifacts`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/debug_artifacts/index.html) / [`mir_opt_tests`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/mir_opt_tests/index.html): Prevent unnecessary artifacts and stale files in test directories.
+* [`tests_revision_unpaired_stdout_stderr`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/tests_revision_unpaired_stdout_stderr/index.html) / [`ui_tests`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/ui_tests/index.html): Check for unpaired or stray test output files.
+* [`target_specific_tests`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/target_specific_tests/index.html): Check to ensure that all target specific tests (those that require a --target flag) also require the pre-requisite LLVM components to run.
+* [`rustdoc_gui_tests`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/rustdoc_gui_tests/index.html): Checks that rustdoc gui tests start with a small description
+* [`rustdoc_json`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/rustdoc_json/index.html): Verify that `FORMAT_VERSION` is in sync with `rust-json-types`.
+## Using Tidy
+
+Tidy is used in a number of different ways.
+* Every time `./x test` is used tidy will run automatically.
+
+* On every pull request, tidy will run automatically during CI checks.
+* Optionally, with the use of git-hooks, tidy can run locally on every push. This can be setup with `./x setup`. See the [rustc-dev-guide](https://rustc-dev-guide.rust-lang.org/building/suggested.html#installing-a-pre-push-hook) for more information.
+
+You can run tidy manually with:
+
+`./x test tidy`
+
+To first run the relevant formatter and then run tidy you can add `--bless`.
+
+`./x test tidy --bless`
+### Extra Checks
+[`extra_checks`](https://doc.rust-lang.org/nightly/nightly-rustc/tidy/extra_checks/index.html) are optional checks primarily focused on other file types and programming languages.
+
+Example usage:
+
+`./x test tidy --extra-checks=py,cpp,js,spellcheck`
+
+All options for `--extra-checks`:
+* `cpp`, `cpp:fmt`
+* `py`, `py:lint`, `py:fmt`
+* `js`, `js:lint`, `js:fmt`, `js:typecheck`
+* `shell`, `shell:lint`
+* `spellcheck`
+
+Default values for tidy's `extra-checks` can be set in `bootstrap.toml`. For example, `build.tidy-extra-checks = "js,py"`.
+
+Any argument without a suffix (eg. `py` or `js`) will include all possible checks. For example, `--extra-checks=js` is the same as `extra-checks=js:lint,js:fmt,js:typecheck`.
+
+Any argument can be prefixed with `auto:` to only run if relevant files are modified (eg. `--extra-checks=auto:py`).
+
+A specific configuration file or folder can be passed to tidy after a double dash (`--extra-checks=py -- foo.py`)
+
+## Tidy Directives
+
+Tidy directives are special comments that help tidy operate.
+
+Tidy directives can be used in the following types of comments:
+* `// `
+* `# `
+* `/* {...} */`
+* `<!-- {...} -->`
+
+You might find yourself needing to ignore a specific tidy style check and can do so with:
+* `ignore-tidy-cr`
+* `ignore-tidy-undocumented-unsafe`
+* `ignore-tidy-tab`
+* `ignore-tidy-linelength`
+* `ignore-tidy-filelength`
+
+* `ignore-tidy-end-whitespace`
+* `ignore-tidy-trailing-newlines`
+* `ignore-tidy-leading-newlines`
+* `ignore-tidy-copyright`
+* `ignore-tidy-dbg`
+* `ignore-tidy-odd-backticks`
+* `ignore-tidy-todo`
+
+Some checks, like `alphabetical`, require a tidy directive to use:
+```
+// tidy-alphabetical-start
+fn aaa() {}
+fn eee() {}
+fn z() {}
+// tidy-alphabetical-end
+```
+<!--ignore-tidy-todo-->While not exactly a tidy directive, // TODO will fail tidy and make sure you can't merge a PR with unfinished work.
+
+### Test Specific Directives
+
+`target-specific-tests` can be ignored with `// ignore-tidy-target-specific-tests`
+
+Tidy's `unknown_revision` check can be suppressed by adding the revision name to `//@ unused-revision-names:{revision}` or with `//@ unused-revision-names:*`.
diff --git a/src/tools/tidy/src/alphabetical.rs b/src/tools/tidy/src/alphabetical.rs
index 9ddce725106..f93d3b56113 100644
--- a/src/tools/tidy/src/alphabetical.rs
+++ b/src/tools/tidy/src/alphabetical.rs
@@ -24,6 +24,7 @@ use std::fmt::Display;
 use std::iter::Peekable;
 use std::path::Path;
 
+use crate::diagnostics::{CheckId, DiagCtx, RunningCheck};
 use crate::walk::{filter_dirs, walk};
 
 #[cfg(test)]
@@ -43,8 +44,7 @@ const END_MARKER: &str = "tidy-alphabetical-end";
 fn check_section<'a>(
     file: impl Display,
     lines: impl Iterator<Item = (usize, &'a str)>,
-    err: &mut dyn FnMut(&str) -> std::io::Result<()>,
-    bad: &mut bool,
+    check: &mut RunningCheck,
 ) {
     let mut prev_line = String::new();
     let mut first_indent = None;
@@ -56,12 +56,10 @@ fn check_section<'a>(
         }
 
         if line.contains(START_MARKER) {
-            tidy_error_ext!(
-                err,
-                bad,
+            check.error(format!(
                 "{file}:{} found `{START_MARKER}` expecting `{END_MARKER}`",
                 idx + 1
-            );
+            ));
             return;
         }
 
@@ -104,45 +102,44 @@ fn check_section<'a>(
         let prev_line_trimmed_lowercase = prev_line.trim_start_matches(' ');
 
         if version_sort(trimmed_line, prev_line_trimmed_lowercase).is_lt() {
-            tidy_error_ext!(err, bad, "{file}:{}: line not in alphabetical order", idx + 1);
+            check.error(format!("{file}:{}: line not in alphabetical order", idx + 1));
         }
 
         prev_line = line;
     }
 
-    tidy_error_ext!(err, bad, "{file}: reached end of file expecting `{END_MARKER}`")
+    check.error(format!("{file}: reached end of file expecting `{END_MARKER}`"));
 }
 
 fn check_lines<'a>(
     file: &impl Display,
     mut lines: impl Iterator<Item = (usize, &'a str)>,
-    err: &mut dyn FnMut(&str) -> std::io::Result<()>,
-    bad: &mut bool,
+    check: &mut RunningCheck,
 ) {
     while let Some((idx, line)) = lines.next() {
         if line.contains(END_MARKER) {
-            tidy_error_ext!(
-                err,
-                bad,
+            check.error(format!(
                 "{file}:{} found `{END_MARKER}` expecting `{START_MARKER}`",
                 idx + 1
-            )
+            ));
         }
 
         if line.contains(START_MARKER) {
-            check_section(file, &mut lines, err, bad);
+            check_section(file, &mut lines, check);
         }
     }
 }
 
-pub fn check(path: &Path, bad: &mut bool) {
+pub fn check(path: &Path, diag_ctx: DiagCtx) {
+    let mut check = diag_ctx.start_check(CheckId::new("alphabetical").path(path));
+
     let skip =
         |path: &_, _is_dir| filter_dirs(path) || path.ends_with("tidy/src/alphabetical/tests.rs");
 
     walk(path, skip, &mut |entry, contents| {
         let file = &entry.path().display();
         let lines = contents.lines().enumerate();
-        check_lines(file, lines, &mut crate::tidy_error, bad)
+        check_lines(file, lines, &mut check)
     });
 }
 
diff --git a/src/tools/tidy/src/alphabetical/tests.rs b/src/tools/tidy/src/alphabetical/tests.rs
index 4d05bc33ced..b181ab8f744 100644
--- a/src/tools/tidy/src/alphabetical/tests.rs
+++ b/src/tools/tidy/src/alphabetical/tests.rs
@@ -1,19 +1,22 @@
-use std::io::Write;
-use std::str::from_utf8;
+use std::path::Path;
 
-use super::*;
+use crate::alphabetical::check_lines;
+use crate::diagnostics::DiagCtx;
 
 #[track_caller]
 fn test(lines: &str, name: &str, expected_msg: &str, expected_bad: bool) {
-    let mut actual_msg = Vec::new();
-    let mut actual_bad = false;
-    let mut err = |args: &_| {
-        write!(&mut actual_msg, "{args}")?;
-        Ok(())
-    };
-    check_lines(&name, lines.lines().enumerate(), &mut err, &mut actual_bad);
-    assert_eq!(expected_msg, from_utf8(&actual_msg).unwrap());
-    assert_eq!(expected_bad, actual_bad);
+    let diag_ctx = DiagCtx::new(Path::new("/"), false);
+    let mut check = diag_ctx.start_check("alphabetical-test");
+    check_lines(&name, lines.lines().enumerate(), &mut check);
+
+    assert_eq!(expected_bad, check.is_bad());
+    let errors = check.get_errors();
+    if expected_bad {
+        assert_eq!(errors.len(), 1);
+        assert_eq!(expected_msg, errors[0]);
+    } else {
+        assert!(errors.is_empty());
+    }
 }
 
 #[track_caller]
diff --git a/src/tools/tidy/src/bins.rs b/src/tools/tidy/src/bins.rs
index a18f549844b..10b61869971 100644
--- a/src/tools/tidy/src/bins.rs
+++ b/src/tools/tidy/src/bins.rs
@@ -12,11 +12,13 @@ pub use os_impl::*;
 mod os_impl {
     use std::path::Path;
 
+    use crate::diagnostics::DiagCtx;
+
     pub fn check_filesystem_support(_sources: &[&Path], _output: &Path) -> bool {
         return false;
     }
 
-    pub fn check(_path: &Path, _bad: &mut bool) {}
+    pub fn check(_path: &Path, _diag_ctx: DiagCtx) {}
 }
 
 #[cfg(unix)]
@@ -36,6 +38,8 @@ mod os_impl {
 
     use FilesystemSupport::*;
 
+    use crate::diagnostics::DiagCtx;
+
     fn is_executable(path: &Path) -> std::io::Result<bool> {
         Ok(path.metadata()?.mode() & 0o111 != 0)
     }
@@ -106,14 +110,16 @@ mod os_impl {
     }
 
     #[cfg(unix)]
-    pub fn check(path: &Path, bad: &mut bool) {
+    pub fn check(path: &Path, diag_ctx: DiagCtx) {
+        let mut check = diag_ctx.start_check("bins");
+
         use std::ffi::OsStr;
 
         const ALLOWED: &[&str] = &["configure", "x"];
 
         for p in RI_EXCLUSION_LIST {
             if !path.join(Path::new(p)).exists() {
-                tidy_error!(bad, "rust-installer test bins missed: {p}");
+                check.error(format!("rust-installer test bins missed: {p}"));
             }
         }
 
@@ -153,7 +159,7 @@ mod os_impl {
                         });
                     let path_bytes = rel_path.as_os_str().as_bytes();
                     if output.status.success() && output.stdout.starts_with(path_bytes) {
-                        tidy_error!(bad, "binary checked into source: {}", file.display());
+                        check.error(format!("binary checked into source: {}", file.display()));
                     }
                 }
             },
diff --git a/src/tools/tidy/src/debug_artifacts.rs b/src/tools/tidy/src/debug_artifacts.rs
index 645534cc827..19effaede81 100644
--- a/src/tools/tidy/src/debug_artifacts.rs
+++ b/src/tools/tidy/src/debug_artifacts.rs
@@ -2,24 +2,25 @@
 
 use std::path::Path;
 
+use crate::diagnostics::{CheckId, DiagCtx};
 use crate::walk::{filter_dirs, filter_not_rust, walk};
 
 const GRAPHVIZ_POSTFLOW_MSG: &str = "`borrowck_graphviz_postflow` attribute in test";
 
-pub fn check(test_dir: &Path, bad: &mut bool) {
+pub fn check(test_dir: &Path, diag_ctx: DiagCtx) {
+    let mut check = diag_ctx.start_check(CheckId::new("debug_artifacts").path(test_dir));
+
     walk(
         test_dir,
         |path, _is_dir| filter_dirs(path) || filter_not_rust(path),
         &mut |entry, contents| {
             for (i, line) in contents.lines().enumerate() {
                 if line.contains("borrowck_graphviz_postflow") {
-                    tidy_error!(
-                        bad,
-                        "{}:{}: {}",
+                    check.error(format!(
+                        "{}:{}: {GRAPHVIZ_POSTFLOW_MSG}",
                         entry.path().display(),
-                        i + 1,
-                        GRAPHVIZ_POSTFLOW_MSG
-                    );
+                        i + 1
+                    ));
                 }
             }
         },
diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs
index 858b058cb7d..247080102fb 100644
--- a/src/tools/tidy/src/deps.rs
+++ b/src/tools/tidy/src/deps.rs
@@ -9,6 +9,8 @@ use build_helper::ci::CiEnv;
 use cargo_metadata::semver::Version;
 use cargo_metadata::{Metadata, Package, PackageId};
 
+use crate::diagnostics::{DiagCtx, RunningCheck};
+
 #[path = "../../../bootstrap/src/utils/proc_macro_deps.rs"]
 mod proc_macro_deps;
 
@@ -48,40 +50,116 @@ const LICENSES: &[&str] = &[
 
 type ExceptionList = &'static [(&'static str, &'static str)];
 
+#[derive(Clone, Copy)]
+pub(crate) struct WorkspaceInfo<'a> {
+    /// Path to the directory containing the workspace root Cargo.toml file.
+    pub(crate) path: &'a str,
+    /// The list of license exceptions.
+    pub(crate) exceptions: ExceptionList,
+    /// Optionally:
+    /// * A list of crates for which dependencies need to be explicitly allowed.
+    /// * The list of allowed dependencies.
+    /// * The source code location of the allowed dependencies list
+    crates_and_deps: Option<(&'a [&'a str], &'a [&'a str], ListLocation)>,
+    /// Submodules required for the workspace
+    pub(crate) submodules: &'a [&'a str],
+}
+
 /// The workspaces to check for licensing and optionally permitted dependencies.
-///
-/// Each entry consists of a tuple with the following elements:
-///
-/// * The path to the workspace root Cargo.toml file.
-/// * The list of license exceptions.
-/// * Optionally a tuple of:
-///     * A list of crates for which dependencies need to be explicitly allowed.
-///     * The list of allowed dependencies.
-/// * Submodules required for the workspace.
 // FIXME auto detect all cargo workspaces
-pub(crate) const WORKSPACES: &[(&str, ExceptionList, Option<(&[&str], &[&str])>, &[&str])] = &[
+pub(crate) const WORKSPACES: &[WorkspaceInfo<'static>] = &[
     // The root workspace has to be first for check_rustfix to work.
-    (".", EXCEPTIONS, Some((&["rustc-main"], PERMITTED_RUSTC_DEPENDENCIES)), &[]),
-    ("library", EXCEPTIONS_STDLIB, Some((&["sysroot"], PERMITTED_STDLIB_DEPENDENCIES)), &[]),
-    // Outside of the alphabetical section because rustfmt formats it using multiple lines.
-    (
-        "compiler/rustc_codegen_cranelift",
-        EXCEPTIONS_CRANELIFT,
-        Some((&["rustc_codegen_cranelift"], PERMITTED_CRANELIFT_DEPENDENCIES)),
-        &[],
-    ),
-    // tidy-alphabetical-start
-    ("compiler/rustc_codegen_gcc", EXCEPTIONS_GCC, None, &[]),
-    ("src/bootstrap", EXCEPTIONS_BOOTSTRAP, None, &[]),
-    ("src/tools/cargo", EXCEPTIONS_CARGO, None, &["src/tools/cargo"]),
-    //("src/tools/miri/test-cargo-miri", &[], None), // FIXME uncomment once all deps are vendored
-    //("src/tools/miri/test_dependencies", &[], None), // FIXME uncomment once all deps are vendored
-    ("src/tools/rust-analyzer", EXCEPTIONS_RUST_ANALYZER, None, &[]),
-    ("src/tools/rustbook", EXCEPTIONS_RUSTBOOK, None, &["src/doc/book", "src/doc/reference"]),
-    ("src/tools/rustc-perf", EXCEPTIONS_RUSTC_PERF, None, &["src/tools/rustc-perf"]),
-    ("src/tools/test-float-parse", EXCEPTIONS, None, &[]),
-    ("tests/run-make/uefi-qemu/uefi_qemu_test", EXCEPTIONS_UEFI_QEMU_TEST, None, &[]),
-    // tidy-alphabetical-end
+    WorkspaceInfo {
+        path: ".",
+        exceptions: EXCEPTIONS,
+        crates_and_deps: Some((
+            &["rustc-main"],
+            PERMITTED_RUSTC_DEPENDENCIES,
+            PERMITTED_RUSTC_DEPS_LOCATION,
+        )),
+        submodules: &[],
+    },
+    WorkspaceInfo {
+        path: "library",
+        exceptions: EXCEPTIONS_STDLIB,
+        crates_and_deps: Some((
+            &["sysroot"],
+            PERMITTED_STDLIB_DEPENDENCIES,
+            PERMITTED_STDLIB_DEPS_LOCATION,
+        )),
+        submodules: &[],
+    },
+    {
+        WorkspaceInfo {
+            path: "compiler/rustc_codegen_cranelift",
+            exceptions: EXCEPTIONS_CRANELIFT,
+            crates_and_deps: Some((
+                &["rustc_codegen_cranelift"],
+                PERMITTED_CRANELIFT_DEPENDENCIES,
+                PERMITTED_CRANELIFT_DEPS_LOCATION,
+            )),
+            submodules: &[],
+        }
+    },
+    WorkspaceInfo {
+        path: "compiler/rustc_codegen_gcc",
+        exceptions: EXCEPTIONS_GCC,
+        crates_and_deps: None,
+        submodules: &[],
+    },
+    WorkspaceInfo {
+        path: "src/bootstrap",
+        exceptions: EXCEPTIONS_BOOTSTRAP,
+        crates_and_deps: None,
+        submodules: &[],
+    },
+    WorkspaceInfo {
+        path: "src/tools/cargo",
+        exceptions: EXCEPTIONS_CARGO,
+        crates_and_deps: None,
+        submodules: &["src/tools/cargo"],
+    },
+    // FIXME uncomment once all deps are vendored
+    //  WorkspaceInfo {
+    //      path: "src/tools/miri/test-cargo-miri",
+    //      crates_and_deps: None
+    //      submodules: &[],
+    //  },
+    // WorkspaceInfo {
+    //      path: "src/tools/miri/test_dependencies",
+    //      crates_and_deps: None,
+    //      submodules: &[],
+    //  }
+    WorkspaceInfo {
+        path: "src/tools/rust-analyzer",
+        exceptions: EXCEPTIONS_RUST_ANALYZER,
+        crates_and_deps: None,
+        submodules: &[],
+    },
+    WorkspaceInfo {
+        path: "src/tools/rustbook",
+        exceptions: EXCEPTIONS_RUSTBOOK,
+        crates_and_deps: None,
+        submodules: &["src/doc/book", "src/doc/reference"],
+    },
+    WorkspaceInfo {
+        path: "src/tools/rustc-perf",
+        exceptions: EXCEPTIONS_RUSTC_PERF,
+        crates_and_deps: None,
+        submodules: &["src/tools/rustc-perf"],
+    },
+    WorkspaceInfo {
+        path: "src/tools/test-float-parse",
+        exceptions: EXCEPTIONS,
+        crates_and_deps: None,
+        submodules: &[],
+    },
+    WorkspaceInfo {
+        path: "tests/run-make-cargo/uefi-qemu/uefi_qemu_test",
+        exceptions: EXCEPTIONS_UEFI_QEMU_TEST,
+        crates_and_deps: None,
+        submodules: &[],
+    },
 ];
 
 /// These are exceptions to Rust's permissive licensing policy, and
@@ -226,7 +304,20 @@ const EXCEPTIONS_UEFI_QEMU_TEST: ExceptionList = &[
     ("r-efi", "MIT OR Apache-2.0 OR LGPL-2.1-or-later"), // LGPL is not acceptable, but we use it under MIT OR Apache-2.0
 ];
 
-const PERMITTED_DEPS_LOCATION: &str = concat!(file!(), ":", line!());
+#[derive(Clone, Copy)]
+struct ListLocation {
+    path: &'static str,
+    line: u32,
+}
+
+/// Creates a [`ListLocation`] for the current location (with an additional offset to the actual list start);
+macro_rules! location {
+    (+ $offset:literal) => {
+        ListLocation { path: file!(), line: line!() + $offset }
+    };
+}
+
+const PERMITTED_RUSTC_DEPS_LOCATION: ListLocation = location!(+6);
 
 /// Crates rustc is allowed to depend on. Avoid adding to the list if possible.
 ///
@@ -267,6 +358,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
     "digest",
     "displaydoc",
     "dissimilar",
+    "dyn-clone",
     "either",
     "elsa",
     "ena",
@@ -275,6 +367,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
     "expect-test",
     "fallible-iterator", // dependency of `thorin`
     "fastrand",
+    "find-msvc-tools",
     "flate2",
     "fluent-bundle",
     "fluent-langneg",
@@ -287,14 +380,12 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
     "gimli",
     "gsgdt",
     "hashbrown",
+    "icu_collections",
     "icu_list",
-    "icu_list_data",
-    "icu_locid",
-    "icu_locid_transform",
-    "icu_locid_transform_data",
+    "icu_locale",
+    "icu_locale_core",
+    "icu_locale_data",
     "icu_provider",
-    "icu_provider_adapters",
-    "icu_provider_macros",
     "ident_case",
     "indexmap",
     "intl-memoizer",
@@ -332,6 +423,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
     "polonius-engine",
     "portable-atomic", // dependency for platforms doesn't support `AtomicU64` in std
     "portable-atomic-util",
+    "potential_utf",
     "ppv-lite86",
     "proc-macro-hack",
     "proc-macro2",
@@ -347,6 +439,8 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
     "rand_xorshift", // dependency for doc-tests in rustc_thread_pool
     "rand_xoshiro",
     "redox_syscall",
+    "ref-cast",
+    "ref-cast-impl",
     "regex",
     "regex-automata",
     "regex-syntax",
@@ -358,12 +452,14 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
     "rustix",
     "ruzstd", // via object in thorin-dwp
     "ryu",
+    "schemars",
+    "schemars_derive",
     "scoped-tls",
     "scopeguard",
     "self_cell",
-    "semver",
     "serde",
     "serde_derive",
+    "serde_derive_internals",
     "serde_json",
     "serde_path_to_error",
     "sha1",
@@ -448,11 +544,14 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
     "zerocopy-derive",
     "zerofrom",
     "zerofrom-derive",
+    "zerotrie",
     "zerovec",
     "zerovec-derive",
     // tidy-alphabetical-end
 ];
 
+const PERMITTED_STDLIB_DEPS_LOCATION: ListLocation = location!(+2);
+
 const PERMITTED_STDLIB_DEPENDENCIES: &[&str] = &[
     // tidy-alphabetical-start
     "addr2line",
@@ -478,7 +577,6 @@ const PERMITTED_STDLIB_DEPENDENCIES: &[&str] = &[
     "rustc-demangle",
     "rustc-literal-escaper",
     "shlex",
-    "unicode-width",
     "unwinding",
     "wasi",
     "windows-sys",
@@ -491,9 +589,12 @@ const PERMITTED_STDLIB_DEPENDENCIES: &[&str] = &[
     "windows_x86_64_gnu",
     "windows_x86_64_gnullvm",
     "windows_x86_64_msvc",
+    "wit-bindgen",
     // tidy-alphabetical-end
 ];
 
+const PERMITTED_CRANELIFT_DEPS_LOCATION: ListLocation = location!(+2);
+
 const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[
     // tidy-alphabetical-start
     "allocator-api2",
@@ -563,35 +664,47 @@ const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[
 ///
 /// `root` is path to the directory with the root `Cargo.toml` (for the workspace). `cargo` is path
 /// to the cargo executable.
-pub fn check(root: &Path, cargo: &Path, bless: bool, bad: &mut bool) {
+pub fn check(root: &Path, cargo: &Path, bless: bool, diag_ctx: DiagCtx) {
+    let mut check = diag_ctx.start_check("deps");
+
     let mut checked_runtime_licenses = false;
 
-    check_proc_macro_dep_list(root, cargo, bless, bad);
+    check_proc_macro_dep_list(root, cargo, bless, &mut check);
 
-    for &(workspace, exceptions, permitted_deps, submodules) in WORKSPACES {
+    for &WorkspaceInfo { path, exceptions, crates_and_deps, submodules } in WORKSPACES {
         if has_missing_submodule(root, submodules) {
             continue;
         }
 
-        if !root.join(workspace).join("Cargo.lock").exists() {
-            tidy_error!(bad, "the `{workspace}` workspace doesn't have a Cargo.lock");
+        if !root.join(path).join("Cargo.lock").exists() {
+            check.error(format!("the `{path}` workspace doesn't have a Cargo.lock"));
             continue;
         }
 
         let mut cmd = cargo_metadata::MetadataCommand::new();
         cmd.cargo_path(cargo)
-            .manifest_path(root.join(workspace).join("Cargo.toml"))
+            .manifest_path(root.join(path).join("Cargo.toml"))
             .features(cargo_metadata::CargoOpt::AllFeatures)
             .other_options(vec!["--locked".to_owned()]);
         let metadata = t!(cmd.exec());
 
-        check_license_exceptions(&metadata, workspace, exceptions, bad);
-        if let Some((crates, permitted_deps)) = permitted_deps {
-            check_permitted_dependencies(&metadata, workspace, permitted_deps, crates, bad);
+        check_license_exceptions(&metadata, path, exceptions, &mut check);
+        if let Some((crates, permitted_deps, location)) = crates_and_deps {
+            let descr = crates.get(0).unwrap_or(&path);
+            check_permitted_dependencies(
+                &metadata,
+                descr,
+                permitted_deps,
+                crates,
+                location,
+                &mut check,
+            );
         }
 
-        if workspace == "library" {
-            check_runtime_license_exceptions(&metadata, bad);
+        if path == "library" {
+            check_runtime_license_exceptions(&metadata, &mut check);
+            check_runtime_no_duplicate_dependencies(&metadata, &mut check);
+            check_runtime_no_proc_macros(&metadata, &mut check);
             checked_runtime_licenses = true;
         }
     }
@@ -602,7 +715,7 @@ pub fn check(root: &Path, cargo: &Path, bless: bool, bad: &mut bool) {
 }
 
 /// Ensure the list of proc-macro crate transitive dependencies is up to date
-fn check_proc_macro_dep_list(root: &Path, cargo: &Path, bless: bool, bad: &mut bool) {
+fn check_proc_macro_dep_list(root: &Path, cargo: &Path, bless: bool, check: &mut RunningCheck) {
     let mut cmd = cargo_metadata::MetadataCommand::new();
     cmd.cargo_path(cargo)
         .manifest_path(root.join("Cargo.toml"))
@@ -649,22 +762,22 @@ pub static CRATES: &[&str] = &[
         )
         .unwrap();
     } else {
-        let old_bad = *bad;
+        let mut error_found = false;
 
         for missing in proc_macro_deps.difference(&expected) {
-            tidy_error!(
-                bad,
+            error_found = true;
+            check.error(format!(
                 "proc-macro crate dependency `{missing}` is not registered in `src/bootstrap/src/utils/proc_macro_deps.rs`",
-            );
+            ));
         }
         for extra in expected.difference(&proc_macro_deps) {
-            tidy_error!(
-                bad,
+            error_found = true;
+            check.error(format!(
                 "`{extra}` is registered in `src/bootstrap/src/utils/proc_macro_deps.rs`, but is not a proc-macro crate dependency",
-            );
+            ));
         }
-        if *bad != old_bad {
-            eprintln!("Run `./x.py test tidy --bless` to regenerate the list");
+        if error_found {
+            check.message("Run `./x.py test tidy --bless` to regenerate the list");
         }
     }
 }
@@ -686,7 +799,7 @@ pub fn has_missing_submodule(root: &Path, submodules: &[&str]) -> bool {
 ///
 /// Unlike for tools we don't allow exceptions to the `LICENSES` list for the runtime with the sole
 /// exception of `fortanix-sgx-abi` which is only used on x86_64-fortanix-unknown-sgx.
-fn check_runtime_license_exceptions(metadata: &Metadata, bad: &mut bool) {
+fn check_runtime_license_exceptions(metadata: &Metadata, check: &mut RunningCheck) {
     for pkg in &metadata.packages {
         if pkg.source.is_none() {
             // No need to check local packages.
@@ -695,7 +808,8 @@ fn check_runtime_license_exceptions(metadata: &Metadata, bad: &mut bool) {
         let license = match &pkg.license {
             Some(license) => license,
             None => {
-                tidy_error!(bad, "dependency `{}` does not define a license expression", pkg.id);
+                check
+                    .error(format!("dependency `{}` does not define a license expression", pkg.id));
                 continue;
             }
         };
@@ -708,7 +822,7 @@ fn check_runtime_license_exceptions(metadata: &Metadata, bad: &mut bool) {
                 continue;
             }
 
-            tidy_error!(bad, "invalid license `{}` in `{}`", license, pkg.id);
+            check.error(format!("invalid license `{}` in `{}`", license, pkg.id));
         }
     }
 }
@@ -720,37 +834,32 @@ fn check_license_exceptions(
     metadata: &Metadata,
     workspace: &str,
     exceptions: &[(&str, &str)],
-    bad: &mut bool,
+    check: &mut RunningCheck,
 ) {
     // Validate the EXCEPTIONS list hasn't changed.
     for (name, license) in exceptions {
         // Check that the package actually exists.
         if !metadata.packages.iter().any(|p| *p.name == *name) {
-            tidy_error!(
-                bad,
-                "could not find exception package `{}` in workspace `{workspace}`\n\
+            check.error(format!(
+                "could not find exception package `{name}` in workspace `{workspace}`\n\
                 Remove from EXCEPTIONS list if it is no longer used.",
-                name
-            );
+            ));
         }
         // Check that the license hasn't changed.
         for pkg in metadata.packages.iter().filter(|p| *p.name == *name) {
             match &pkg.license {
                 None => {
-                    tidy_error!(
-                        bad,
+                    check.error(format!(
                         "dependency exception `{}` in workspace `{workspace}` does not declare a license expression",
                         pkg.id
-                    );
+                    ));
                 }
                 Some(pkg_license) => {
                     if pkg_license.as_str() != *license {
-                        println!(
-                            "dependency exception `{name}` license in workspace `{workspace}` has changed"
-                        );
-                        println!("    previously `{license}` now `{pkg_license}`");
-                        println!("    update EXCEPTIONS for the new license");
-                        *bad = true;
+                        check.error(format!(r#"dependency exception `{name}` license in workspace `{workspace}` has changed
+    previously `{license}` now `{pkg_license}`
+    update EXCEPTIONS for the new license
+"#));
                     }
                 }
             }
@@ -771,21 +880,50 @@ fn check_license_exceptions(
         let license = match &pkg.license {
             Some(license) => license,
             None => {
-                tidy_error!(
-                    bad,
+                check.error(format!(
                     "dependency `{}` in workspace `{workspace}` does not define a license expression",
                     pkg.id
-                );
+                ));
                 continue;
             }
         };
         if !LICENSES.contains(&license.as_str()) {
-            tidy_error!(
-                bad,
+            check.error(format!(
                 "invalid license `{}` for package `{}` in workspace `{workspace}`",
-                license,
-                pkg.id
-            );
+                license, pkg.id
+            ));
+        }
+    }
+}
+
+fn check_runtime_no_duplicate_dependencies(metadata: &Metadata, check: &mut RunningCheck) {
+    let mut seen_pkgs = HashSet::new();
+    for pkg in &metadata.packages {
+        if pkg.source.is_none() {
+            continue;
+        }
+
+        // Skip the `wasi` crate here which the standard library explicitly
+        // depends on two version of (one for the `wasm32-wasip1` target and
+        // another for the `wasm32-wasip2` target).
+        if pkg.name.to_string() != "wasi" && !seen_pkgs.insert(&*pkg.name) {
+            check.error(format!(
+                "duplicate package `{}` is not allowed for the standard library",
+                pkg.name
+            ));
+        }
+    }
+}
+
+fn check_runtime_no_proc_macros(metadata: &Metadata, check: &mut RunningCheck) {
+    for pkg in &metadata.packages {
+        if pkg.targets.iter().any(|target| target.is_proc_macro()) {
+            check.error(format!(
+                "proc macro `{}` is not allowed as standard library dependency.\n\
+                Using proc macros in the standard library would break cross-compilation \
+                as proc-macros don't get shipped for the host tuple.",
+                pkg.name
+            ));
         }
     }
 }
@@ -799,7 +937,8 @@ fn check_permitted_dependencies(
     descr: &str,
     permitted_dependencies: &[&'static str],
     restricted_dependency_crates: &[&'static str],
-    bad: &mut bool,
+    permitted_location: ListLocation,
+    check: &mut RunningCheck,
 ) {
     let mut has_permitted_dep_error = false;
     let mut deps = HashSet::new();
@@ -821,11 +960,10 @@ fn check_permitted_dependencies(
             }
         }
         if !deps.iter().any(|dep_id| compare(pkg_from_id(metadata, dep_id), permitted)) {
-            tidy_error!(
-                bad,
+            check.error(format!(
                 "could not find allowed package `{permitted}`\n\
                 Remove from PERMITTED_DEPENDENCIES list if it is no longer used.",
-            );
+            ));
             has_permitted_dep_error = true;
         }
     }
@@ -852,14 +990,14 @@ fn check_permitted_dependencies(
                 false
             };
             if !is_eq {
-                tidy_error!(bad, "Dependency for {descr} not explicitly permitted: {}", dep.id);
+                check.error(format!("Dependency for {descr} not explicitly permitted: {}", dep.id));
                 has_permitted_dep_error = true;
             }
         }
     }
 
     if has_permitted_dep_error {
-        eprintln!("Go to `{PERMITTED_DEPS_LOCATION}` for the list.");
+        eprintln!("Go to `{}:{}` for the list.", permitted_location.path, permitted_location.line);
     }
 }
 
diff --git a/src/tools/tidy/src/diagnostics.rs b/src/tools/tidy/src/diagnostics.rs
new file mode 100644
index 00000000000..6e95f97d010
--- /dev/null
+++ b/src/tools/tidy/src/diagnostics.rs
@@ -0,0 +1,243 @@
+use std::collections::HashSet;
+use std::fmt::{Display, Formatter};
+use std::path::{Path, PathBuf};
+use std::sync::{Arc, Mutex};
+
+use termcolor::{Color, WriteColor};
+
+/// Collects diagnostics from all tidy steps, and contains shared information
+/// that determines how should message and logs be presented.
+///
+/// Since checks are executed in parallel, the context is internally synchronized, to avoid
+/// all checks to lock it explicitly.
+#[derive(Clone)]
+pub struct DiagCtx(Arc<Mutex<DiagCtxInner>>);
+
+impl DiagCtx {
+    pub fn new(root_path: &Path, verbose: bool) -> Self {
+        Self(Arc::new(Mutex::new(DiagCtxInner {
+            running_checks: Default::default(),
+            finished_checks: Default::default(),
+            root_path: root_path.to_path_buf(),
+            verbose,
+        })))
+    }
+
+    pub fn start_check<Id: Into<CheckId>>(&self, id: Id) -> RunningCheck {
+        let mut id = id.into();
+
+        let mut ctx = self.0.lock().unwrap();
+
+        // Shorten path for shorter diagnostics
+        id.path = match id.path {
+            Some(path) => Some(path.strip_prefix(&ctx.root_path).unwrap_or(&path).to_path_buf()),
+            None => None,
+        };
+
+        ctx.start_check(id.clone());
+        RunningCheck {
+            id,
+            bad: false,
+            ctx: self.0.clone(),
+            #[cfg(test)]
+            errors: vec![],
+        }
+    }
+
+    pub fn into_failed_checks(self) -> Vec<FinishedCheck> {
+        let ctx = Arc::into_inner(self.0).unwrap().into_inner().unwrap();
+        assert!(ctx.running_checks.is_empty(), "Some checks are still running");
+        ctx.finished_checks.into_iter().filter(|c| c.bad).collect()
+    }
+}
+
+struct DiagCtxInner {
+    running_checks: HashSet<CheckId>,
+    finished_checks: HashSet<FinishedCheck>,
+    verbose: bool,
+    root_path: PathBuf,
+}
+
+impl DiagCtxInner {
+    fn start_check(&mut self, id: CheckId) {
+        if self.has_check_id(&id) {
+            panic!("Starting a check named `{id:?}` for the second time");
+        }
+
+        self.running_checks.insert(id);
+    }
+
+    fn finish_check(&mut self, check: FinishedCheck) {
+        assert!(
+            self.running_checks.remove(&check.id),
+            "Finishing check `{:?}` that was not started",
+            check.id
+        );
+
+        if check.bad {
+            output_message("FAIL", Some(&check.id), Some(COLOR_ERROR));
+        } else if self.verbose {
+            output_message("OK", Some(&check.id), Some(COLOR_SUCCESS));
+        }
+
+        self.finished_checks.insert(check);
+    }
+
+    fn has_check_id(&self, id: &CheckId) -> bool {
+        self.running_checks
+            .iter()
+            .chain(self.finished_checks.iter().map(|c| &c.id))
+            .any(|c| c == id)
+    }
+}
+
+/// Identifies a single step
+#[derive(PartialEq, Eq, Hash, Clone, Debug)]
+pub struct CheckId {
+    pub name: String,
+    pub path: Option<PathBuf>,
+}
+
+impl CheckId {
+    pub fn new(name: &'static str) -> Self {
+        Self { name: name.to_string(), path: None }
+    }
+
+    pub fn path(self, path: &Path) -> Self {
+        Self { path: Some(path.to_path_buf()), ..self }
+    }
+}
+
+impl From<&'static str> for CheckId {
+    fn from(name: &'static str) -> Self {
+        Self::new(name)
+    }
+}
+
+impl Display for CheckId {
+    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+        write!(f, "{}", self.name)?;
+        if let Some(path) = &self.path {
+            write!(f, " ({})", path.display())?;
+        }
+        Ok(())
+    }
+}
+
+#[derive(PartialEq, Eq, Hash, Debug)]
+pub struct FinishedCheck {
+    id: CheckId,
+    bad: bool,
+}
+
+impl FinishedCheck {
+    pub fn id(&self) -> &CheckId {
+        &self.id
+    }
+}
+
+/// Represents a single tidy check, identified by its `name`, running.
+pub struct RunningCheck {
+    id: CheckId,
+    bad: bool,
+    ctx: Arc<Mutex<DiagCtxInner>>,
+    #[cfg(test)]
+    errors: Vec<String>,
+}
+
+impl RunningCheck {
+    /// Creates a new instance of a running check without going through the diag
+    /// context.
+    /// Useful if you want to run some functions from tidy without configuring
+    /// diagnostics.
+    pub fn new_noop() -> Self {
+        let ctx = DiagCtx::new(Path::new(""), false);
+        ctx.start_check("noop")
+    }
+
+    /// Immediately output an error and mark the check as failed.
+    pub fn error<T: Display>(&mut self, msg: T) {
+        self.mark_as_bad();
+        let msg = msg.to_string();
+        output_message(&msg, Some(&self.id), Some(COLOR_ERROR));
+        #[cfg(test)]
+        self.errors.push(msg);
+    }
+
+    /// Immediately output a warning.
+    pub fn warning<T: Display>(&mut self, msg: T) {
+        output_message(&msg.to_string(), Some(&self.id), Some(COLOR_WARNING));
+    }
+
+    /// Output an informational message
+    pub fn message<T: Display>(&mut self, msg: T) {
+        output_message(&msg.to_string(), Some(&self.id), None);
+    }
+
+    /// Output a message only if verbose output is enabled.
+    pub fn verbose_msg<T: Display>(&mut self, msg: T) {
+        if self.is_verbose_enabled() {
+            self.message(msg);
+        }
+    }
+
+    /// Has an error already occured for this check?
+    pub fn is_bad(&self) -> bool {
+        self.bad
+    }
+
+    /// Is verbose output enabled?
+    pub fn is_verbose_enabled(&self) -> bool {
+        self.ctx.lock().unwrap().verbose
+    }
+
+    #[cfg(test)]
+    pub fn get_errors(&self) -> Vec<String> {
+        self.errors.clone()
+    }
+
+    fn mark_as_bad(&mut self) {
+        self.bad = true;
+    }
+}
+
+impl Drop for RunningCheck {
+    fn drop(&mut self) {
+        self.ctx.lock().unwrap().finish_check(FinishedCheck { id: self.id.clone(), bad: self.bad })
+    }
+}
+
+pub const COLOR_SUCCESS: Color = Color::Green;
+pub const COLOR_ERROR: Color = Color::Red;
+pub const COLOR_WARNING: Color = Color::Yellow;
+
+/// Output a message to stderr.
+/// The message can be optionally scoped to a certain check, and it can also have a certain color.
+pub fn output_message(msg: &str, id: Option<&CheckId>, color: Option<Color>) {
+    use std::io::Write;
+
+    use termcolor::{ColorChoice, ColorSpec, StandardStream};
+
+    let mut stderr = StandardStream::stderr(ColorChoice::Auto);
+    if let Some(color) = &color {
+        stderr.set_color(ColorSpec::new().set_fg(Some(*color))).unwrap();
+    }
+
+    match id {
+        Some(id) => {
+            write!(&mut stderr, "tidy [{}", id.name).unwrap();
+            if let Some(path) = &id.path {
+                write!(&mut stderr, " ({})", path.display()).unwrap();
+            }
+            write!(&mut stderr, "]").unwrap();
+        }
+        None => {
+            write!(&mut stderr, "tidy").unwrap();
+        }
+    }
+    if color.is_some() {
+        stderr.set_color(&ColorSpec::new()).unwrap();
+    }
+
+    writeln!(&mut stderr, ": {msg}").unwrap();
+}
diff --git a/src/tools/tidy/src/edition.rs b/src/tools/tidy/src/edition.rs
index 08f6a3909f8..448e0b0e0a8 100644
--- a/src/tools/tidy/src/edition.rs
+++ b/src/tools/tidy/src/edition.rs
@@ -2,9 +2,11 @@
 
 use std::path::Path;
 
+use crate::diagnostics::{CheckId, DiagCtx};
 use crate::walk::{filter_dirs, walk};
 
-pub fn check(path: &Path, bad: &mut bool) {
+pub fn check(path: &Path, diag_ctx: DiagCtx) {
+    let mut check = diag_ctx.start_check(CheckId::new("edition").path(path));
     walk(path, |path, _is_dir| filter_dirs(path), &mut |entry, contents| {
         let file = entry.path();
         let filename = file.file_name().unwrap();
@@ -23,11 +25,10 @@ pub fn check(path: &Path, bad: &mut bool) {
         // Check that all packages use the 2021 edition. Virtual workspaces don't allow setting an
         // edition, so these shouldn't be checked.
         if is_package && !is_current_edition {
-            tidy_error!(
-                bad,
+            check.error(format!(
                 "{} doesn't have `edition = \"2021\"` or `edition = \"2024\"` on a separate line",
                 file.display()
-            );
+            ));
         }
     });
 }
diff --git a/src/tools/tidy/src/error_codes.rs b/src/tools/tidy/src/error_codes.rs
index 65aa89fe801..83fbefa43d9 100644
--- a/src/tools/tidy/src/error_codes.rs
+++ b/src/tools/tidy/src/error_codes.rs
@@ -22,6 +22,7 @@ use std::path::Path;
 
 use regex::Regex;
 
+use crate::diagnostics::{DiagCtx, RunningCheck};
 use crate::walk::{filter_dirs, walk, walk_many};
 
 const ERROR_CODES_PATH: &str = "compiler/rustc_error_codes/src/lib.rs";
@@ -35,71 +36,50 @@ const IGNORE_DOCTEST_CHECK: &[&str] = &["E0464", "E0570", "E0601", "E0602", "E07
 const IGNORE_UI_TEST_CHECK: &[&str] =
     &["E0461", "E0465", "E0514", "E0554", "E0640", "E0717", "E0729"];
 
-macro_rules! verbose_print {
-    ($verbose:expr, $($fmt:tt)*) => {
-        if $verbose {
-            println!("{}", format_args!($($fmt)*));
-        }
-    };
-}
-
-pub fn check(
-    root_path: &Path,
-    search_paths: &[&Path],
-    verbose: bool,
-    ci_info: &crate::CiInfo,
-    bad: &mut bool,
-) {
-    let mut errors = Vec::new();
+pub fn check(root_path: &Path, search_paths: &[&Path], ci_info: &crate::CiInfo, diag_ctx: DiagCtx) {
+    let mut check = diag_ctx.start_check("error_codes");
 
     // Check that no error code explanation was removed.
-    check_removed_error_code_explanation(ci_info, bad);
+    check_removed_error_code_explanation(ci_info, &mut check);
 
     // Stage 1: create list
-    let error_codes = extract_error_codes(root_path, &mut errors);
-    if verbose {
-        println!("Found {} error codes", error_codes.len());
-        println!("Highest error code: `{}`", error_codes.iter().max().unwrap());
-    }
+    let error_codes = extract_error_codes(root_path, &mut check);
+    check.verbose_msg(format!("Found {} error codes", error_codes.len()));
+    check.verbose_msg(format!("Highest error code: `{}`", error_codes.iter().max().unwrap()));
 
     // Stage 2: check list has docs
-    let no_longer_emitted = check_error_codes_docs(root_path, &error_codes, &mut errors, verbose);
+    let no_longer_emitted = check_error_codes_docs(root_path, &error_codes, &mut check);
 
     // Stage 3: check list has UI tests
-    check_error_codes_tests(root_path, &error_codes, &mut errors, verbose, &no_longer_emitted);
+    check_error_codes_tests(root_path, &error_codes, &mut check, &no_longer_emitted);
 
     // Stage 4: check list is emitted by compiler
-    check_error_codes_used(search_paths, &error_codes, &mut errors, &no_longer_emitted, verbose);
-
-    // Print any errors.
-    for error in errors {
-        tidy_error!(bad, "{}", error);
-    }
+    check_error_codes_used(search_paths, &error_codes, &mut check, &no_longer_emitted);
 }
 
-fn check_removed_error_code_explanation(ci_info: &crate::CiInfo, bad: &mut bool) {
+fn check_removed_error_code_explanation(ci_info: &crate::CiInfo, check: &mut RunningCheck) {
     let Some(base_commit) = &ci_info.base_commit else {
-        eprintln!("Skipping error code explanation removal check");
+        check.verbose_msg("Skipping error code explanation removal check");
         return;
     };
     let Some(diff) = crate::git_diff(base_commit, "--name-status") else {
-        *bad = true;
-        eprintln!("removed error code explanation tidy check: Failed to run git diff");
+        check.error(format!("removed error code explanation: Failed to run git diff"));
         return;
     };
     if diff.lines().any(|line| {
         line.starts_with('D') && line.contains("compiler/rustc_error_codes/src/error_codes/")
     }) {
-        *bad = true;
-        eprintln!("tidy check error: Error code explanations should never be removed!");
-        eprintln!("Take a look at E0001 to see how to handle it.");
+        check.error(format!(
+            r#"Error code explanations should never be removed!
+Take a look at E0001 to see how to handle it."#
+        ));
         return;
     }
-    println!("No error code explanation was removed!");
+    check.verbose_msg("No error code explanation was removed!");
 }
 
 /// Stage 1: Parses a list of error codes from `error_codes.rs`.
-fn extract_error_codes(root_path: &Path, errors: &mut Vec<String>) -> Vec<String> {
+fn extract_error_codes(root_path: &Path, check: &mut RunningCheck) -> Vec<String> {
     let path = root_path.join(Path::new(ERROR_CODES_PATH));
     let file =
         fs::read_to_string(&path).unwrap_or_else(|e| panic!("failed to read `{path:?}`: {e}"));
@@ -117,7 +97,7 @@ fn extract_error_codes(root_path: &Path, errors: &mut Vec<String>) -> Vec<String
             // Extract the error code from the line. Emit a fatal error if it is not in the correct
             // format.
             let Some(split_line) = split_line else {
-                errors.push(format!(
+                check.error(format!(
                     "{path}:{line_index}: Expected a line with the format `Eabcd: abcd, \
                     but got \"{line}\" without a `:` delimiter",
                 ));
@@ -128,8 +108,9 @@ fn extract_error_codes(root_path: &Path, errors: &mut Vec<String>) -> Vec<String
 
             // If this is a duplicate of another error code, emit a fatal error.
             if error_codes.contains(&err_code) {
-                errors
-                    .push(format!("{path}:{line_index}: Found duplicate error code: `{err_code}`"));
+                check.error(format!(
+                    "{path}:{line_index}: Found duplicate error code: `{err_code}`"
+                ));
                 continue;
             }
 
@@ -140,14 +121,14 @@ fn extract_error_codes(root_path: &Path, errors: &mut Vec<String>) -> Vec<String
             // Ensure that the line references the correct markdown file.
             let rest = split_line.1.split_once(',');
             let Some(rest) = rest else {
-                errors.push(format!(
+                check.error(format!(
                     "{path}:{line_index}: Expected a line with the format `Eabcd: abcd, \
                     but got \"{line}\" without a `,` delimiter",
                 ));
                 continue;
             };
             if error_num_as_str != rest.0.trim() {
-                errors.push(format!(
+                check.error(format!(
                     "{path}:{line_index}: `{}:` should be followed by `{},` but instead found `{}` in \
                     `compiler/rustc_error_codes/src/lib.rs`",
                     err_code,
@@ -157,7 +138,7 @@ fn extract_error_codes(root_path: &Path, errors: &mut Vec<String>) -> Vec<String
                 continue;
             }
             if !rest.1.trim().is_empty() && !rest.1.trim().starts_with("//") {
-                errors.push(format!("{path}:{line_index}: should only have one error per line"));
+                check.error(format!("{path}:{line_index}: should only have one error per line"));
                 continue;
             }
 
@@ -172,8 +153,7 @@ fn extract_error_codes(root_path: &Path, errors: &mut Vec<String>) -> Vec<String
 fn check_error_codes_docs(
     root_path: &Path,
     error_codes: &[String],
-    errors: &mut Vec<String>,
-    verbose: bool,
+    check: &mut RunningCheck,
 ) -> Vec<String> {
     let docs_path = root_path.join(Path::new(ERROR_DOCS_PATH));
 
@@ -184,7 +164,7 @@ fn check_error_codes_docs(
 
         // Error if the file isn't markdown.
         if path.extension() != Some(OsStr::new("md")) {
-            errors.push(format!(
+            check.error(format!(
                 "Found unexpected non-markdown file in error code docs directory: {}",
                 path.display()
             ));
@@ -196,7 +176,7 @@ fn check_error_codes_docs(
         let err_code = filename.unwrap().0; // `unwrap` is ok because we know the filename is in the correct format.
 
         if error_codes.iter().all(|e| e != err_code) {
-            errors.push(format!(
+            check.error(format!(
                 "Found valid file `{}` in error code docs directory without corresponding \
                 entry in `rustc_error_codes/src/lib.rs`",
                 path.display()
@@ -208,11 +188,10 @@ fn check_error_codes_docs(
             check_explanation_has_doctest(contents, err_code);
 
         if emit_ignore_warning {
-            verbose_print!(
-                verbose,
+            check.verbose_msg(format!(
                 "warning: Error code `{err_code}` uses the ignore header. This should not be used, add the error code to the \
                 `IGNORE_DOCTEST_CHECK` constant instead."
-            );
+            ));
         }
 
         if no_longer_emitted {
@@ -220,11 +199,10 @@ fn check_error_codes_docs(
         }
 
         if !found_code_example {
-            verbose_print!(
-                verbose,
+            check.verbose_msg(format!(
                 "warning: Error code `{err_code}` doesn't have a code example, all error codes are expected to have one \
                 (even if untested)."
-            );
+            ));
             return;
         }
 
@@ -232,12 +210,12 @@ fn check_error_codes_docs(
 
         // Check that the explanation has a doctest, and if it shouldn't, that it doesn't
         if !found_proper_doctest && !test_ignored {
-            errors.push(format!(
+            check.error(format!(
                 "`{}` doesn't use its own error code in compile_fail example",
                 path.display(),
             ));
         } else if found_proper_doctest && test_ignored {
-            errors.push(format!(
+            check.error(format!(
                 "`{}` has a compile_fail doctest with its own error code, it shouldn't \
                 be listed in `IGNORE_DOCTEST_CHECK`",
                 path.display(),
@@ -289,8 +267,7 @@ fn check_explanation_has_doctest(explanation: &str, err_code: &str) -> (bool, bo
 fn check_error_codes_tests(
     root_path: &Path,
     error_codes: &[String],
-    errors: &mut Vec<String>,
-    verbose: bool,
+    check: &mut RunningCheck,
     no_longer_emitted: &[String],
 ) {
     let tests_path = root_path.join(Path::new(ERROR_TESTS_PATH));
@@ -299,15 +276,14 @@ fn check_error_codes_tests(
         let test_path = tests_path.join(format!("{code}.stderr"));
 
         if !test_path.exists() && !IGNORE_UI_TEST_CHECK.contains(&code.as_str()) {
-            verbose_print!(
-                verbose,
+            check.verbose_msg(format!(
                 "warning: Error code `{code}` needs to have at least one UI test in the `tests/error-codes/` directory`!"
-            );
+            ));
             continue;
         }
         if IGNORE_UI_TEST_CHECK.contains(&code.as_str()) {
             if test_path.exists() {
-                errors.push(format!(
+                check.error(format!(
                     "Error code `{code}` has a UI test in `tests/ui/error-codes/{code}.rs`, it shouldn't be listed in `EXEMPTED_FROM_TEST`!"
                 ));
             }
@@ -317,11 +293,10 @@ fn check_error_codes_tests(
         let file = match fs::read_to_string(&test_path) {
             Ok(file) => file,
             Err(err) => {
-                verbose_print!(
-                    verbose,
+                check.verbose_msg(format!(
                     "warning: Failed to read UI test file (`{}`) for `{code}` but the file exists. The test is assumed to work:\n{err}",
                     test_path.display()
-                );
+                ));
                 continue;
             }
         };
@@ -343,10 +318,9 @@ fn check_error_codes_tests(
         }
 
         if !found_code {
-            verbose_print!(
-                verbose,
+            check.verbose_msg(format!(
                 "warning: Error code `{code}` has a UI test file, but doesn't contain its own error code!"
-            );
+            ));
         }
     }
 }
@@ -355,9 +329,8 @@ fn check_error_codes_tests(
 fn check_error_codes_used(
     search_paths: &[&Path],
     error_codes: &[String],
-    errors: &mut Vec<String>,
+    check: &mut RunningCheck,
     no_longer_emitted: &[String],
-    verbose: bool,
 ) {
     // Search for error codes in the form `E0123`.
     let regex = Regex::new(r#"\bE\d{4}\b"#).unwrap();
@@ -384,7 +357,7 @@ fn check_error_codes_used(
 
                     if !error_codes.contains(&error_code) {
                         // This error code isn't properly defined, we must error.
-                        errors.push(format!("Error code `{error_code}` is used in the compiler but not defined and documented in `compiler/rustc_error_codes/src/lib.rs`."));
+                        check.error(format!("Error code `{error_code}` is used in the compiler but not defined and documented in `compiler/rustc_error_codes/src/lib.rs`."));
                         continue;
                     }
 
@@ -397,7 +370,7 @@ fn check_error_codes_used(
 
     for code in error_codes {
         if !found_codes.contains(code) && !no_longer_emitted.contains(code) {
-            errors.push(format!(
+            check.error(format!(
                 "Error code `{code}` exists, but is not emitted by the compiler!\n\
                 Please mark the code as no longer emitted by adding the following note to the top of the `EXXXX.md` file:\n\
                 `#### Note: this error code is no longer emitted by the compiler`\n\
@@ -406,10 +379,9 @@ fn check_error_codes_used(
         }
 
         if found_codes.contains(code) && no_longer_emitted.contains(code) {
-            verbose_print!(
-                verbose,
+            check.verbose_msg(format!(
                 "warning: Error code `{code}` is used when it's marked as \"no longer emitted\""
-            );
+            ));
         }
     }
 }
diff --git a/src/tools/tidy/src/extdeps.rs b/src/tools/tidy/src/extdeps.rs
index bc217a55cc1..f75de13b45c 100644
--- a/src/tools/tidy/src/extdeps.rs
+++ b/src/tools/tidy/src/extdeps.rs
@@ -3,6 +3,9 @@
 use std::fs;
 use std::path::Path;
 
+use crate::deps::WorkspaceInfo;
+use crate::diagnostics::DiagCtx;
+
 /// List of allowed sources for packages.
 const ALLOWED_SOURCES: &[&str] = &[
     r#""registry+https://github.com/rust-lang/crates.io-index""#,
@@ -12,23 +15,25 @@ const ALLOWED_SOURCES: &[&str] = &[
 
 /// Checks for external package sources. `root` is the path to the directory that contains the
 /// workspace `Cargo.toml`.
-pub fn check(root: &Path, bad: &mut bool) {
-    for &(workspace, _, _, submodules) in crate::deps::WORKSPACES {
+pub fn check(root: &Path, diag_ctx: DiagCtx) {
+    let mut check = diag_ctx.start_check("extdeps");
+
+    for &WorkspaceInfo { path, submodules, .. } in crate::deps::WORKSPACES {
         if crate::deps::has_missing_submodule(root, submodules) {
             continue;
         }
 
         // FIXME check other workspaces too
         // `Cargo.lock` of rust.
-        let path = root.join(workspace).join("Cargo.lock");
+        let lockfile = root.join(path).join("Cargo.lock");
 
-        if !path.exists() {
-            tidy_error!(bad, "the `{workspace}` workspace doesn't have a Cargo.lock");
+        if !lockfile.exists() {
+            check.error(format!("the `{path}` workspace doesn't have a Cargo.lock"));
             continue;
         }
 
         // Open and read the whole file.
-        let cargo_lock = t!(fs::read_to_string(&path));
+        let cargo_lock = t!(fs::read_to_string(&lockfile));
 
         // Process each line.
         for line in cargo_lock.lines() {
@@ -42,7 +47,7 @@ pub fn check(root: &Path, bad: &mut bool) {
 
             // Ensure source is allowed.
             if !ALLOWED_SOURCES.contains(&source) {
-                tidy_error!(bad, "invalid source: {}", source);
+                check.error(format!("invalid source: {}", source));
             }
         }
     }
diff --git a/src/tools/tidy/src/extra_checks/mod.rs b/src/tools/tidy/src/extra_checks/mod.rs
index f90f716cd95..9f2f2da2e86 100644
--- a/src/tools/tidy/src/extra_checks/mod.rs
+++ b/src/tools/tidy/src/extra_checks/mod.rs
@@ -24,6 +24,7 @@ use std::str::FromStr;
 use std::{fmt, fs, io};
 
 use crate::CiInfo;
+use crate::diagnostics::DiagCtx;
 
 mod rustdoc_js;
 
@@ -41,7 +42,6 @@ const RUFF_CONFIG_PATH: &[&str] = &["src", "tools", "tidy", "config", "ruff.toml
 const RUFF_CACHE_PATH: &[&str] = &["cache", "ruff_cache"];
 const PIP_REQ_PATH: &[&str] = &["src", "tools", "tidy", "config", "requirements.txt"];
 
-// this must be kept in sync with with .github/workflows/spellcheck.yml
 const SPELLCHECK_DIRS: &[&str] = &["compiler", "library", "src/bootstrap", "src/librustdoc"];
 
 pub fn check(
@@ -51,11 +51,14 @@ pub fn check(
     librustdoc_path: &Path,
     tools_path: &Path,
     npm: &Path,
+    cargo: &Path,
     bless: bool,
     extra_checks: Option<&str>,
     pos_args: &[String],
-    bad: &mut bool,
+    diag_ctx: DiagCtx,
 ) {
+    let mut check = diag_ctx.start_check("extra_checks");
+
     if let Err(e) = check_impl(
         root_path,
         outdir,
@@ -63,11 +66,12 @@ pub fn check(
         librustdoc_path,
         tools_path,
         npm,
+        cargo,
         bless,
         extra_checks,
         pos_args,
     ) {
-        tidy_error!(bad, "{e}");
+        check.error(e);
     }
 }
 
@@ -78,6 +82,7 @@ fn check_impl(
     librustdoc_path: &Path,
     tools_path: &Path,
     npm: &Path,
+    cargo: &Path,
     bless: bool,
     extra_checks: Option<&str>,
     pos_args: &[String],
@@ -122,6 +127,12 @@ fn check_impl(
         };
     }
 
+    let rerun_with_bless = |mode: &str, action: &str| {
+        if !bless {
+            eprintln!("rerun tidy with `--extra-checks={mode} --bless` to {action}");
+        }
+    };
+
     let python_lint = extra_check!(Py, Lint);
     let python_fmt = extra_check!(Py, Fmt);
     let shell_lint = extra_check!(Shell, Lint);
@@ -145,14 +156,21 @@ fn check_impl(
     }
 
     if python_lint {
-        eprintln!("linting python files");
         let py_path = py_path.as_ref().unwrap();
-        let res = run_ruff(root_path, outdir, py_path, &cfg_args, &file_args, &["check".as_ref()]);
+        let args: &[&OsStr] = if bless {
+            eprintln!("linting python files and applying suggestions");
+            &["check".as_ref(), "--fix".as_ref()]
+        } else {
+            eprintln!("linting python files");
+            &["check".as_ref()]
+        };
+
+        let res = run_ruff(root_path, outdir, py_path, &cfg_args, &file_args, args);
 
-        if res.is_err() && show_diff {
+        if res.is_err() && show_diff && !bless {
             eprintln!("\npython linting failed! Printing diff suggestions:");
 
-            let _ = run_ruff(
+            let diff_res = run_ruff(
                 root_path,
                 outdir,
                 py_path,
@@ -160,6 +178,10 @@ fn check_impl(
                 &file_args,
                 &["check".as_ref(), "--diff".as_ref()],
             );
+            // `ruff check --diff` will return status 0 if there are no suggestions.
+            if diff_res.is_err() {
+                rerun_with_bless("py:lint", "apply ruff suggestions");
+            }
         }
         // Rethrow error
         res?;
@@ -190,7 +212,7 @@ fn check_impl(
                     &["format".as_ref(), "--diff".as_ref()],
                 );
             }
-            eprintln!("rerun tidy with `--extra-checks=py:fmt --bless` to reformat Python code");
+            rerun_with_bless("py:fmt", "reformat Python code");
         }
 
         // Rethrow error
@@ -223,7 +245,7 @@ fn check_impl(
         let args = merge_args(&cfg_args_clang_format, &file_args_clang_format);
         let res = py_runner(py_path.as_ref().unwrap(), false, None, "clang-format", &args);
 
-        if res.is_err() && show_diff {
+        if res.is_err() && show_diff && !bless {
             eprintln!("\nclang-format linting failed! Printing diff suggestions:");
 
             let mut cfg_args_clang_format_diff = cfg_args.clone();
@@ -263,6 +285,7 @@ fn check_impl(
                     );
                 }
             }
+            rerun_with_bless("cpp:fmt", "reformat C++ code");
         }
         // Rethrow error
         res?;
@@ -288,12 +311,16 @@ fn check_impl(
         args.extend_from_slice(SPELLCHECK_DIRS);
 
         if bless {
-            eprintln!("spellcheck files and fix");
+            eprintln!("spellchecking files and fixing typos");
             args.push("--write-changes");
         } else {
-            eprintln!("spellcheck files");
+            eprintln!("spellchecking files");
+        }
+        let res = spellcheck_runner(root_path, &outdir, &cargo, &args);
+        if res.is_err() {
+            rerun_with_bless("spellcheck", "fix typos");
         }
-        spellcheck_runner(&args)?;
+        res?;
     }
 
     if js_lint || js_typecheck {
@@ -301,11 +328,21 @@ fn check_impl(
     }
 
     if js_lint {
-        rustdoc_js::lint(outdir, librustdoc_path, tools_path)?;
+        if bless {
+            eprintln!("linting javascript files");
+        } else {
+            eprintln!("linting javascript files and applying suggestions");
+        }
+        let res = rustdoc_js::lint(outdir, librustdoc_path, tools_path, bless);
+        if res.is_err() {
+            rerun_with_bless("js:lint", "apply eslint suggestions");
+        }
+        res?;
         rustdoc_js::es_check(outdir, librustdoc_path)?;
     }
 
     if js_typecheck {
+        eprintln!("typechecking javascript files");
         rustdoc_js::typecheck(outdir, librustdoc_path)?;
     }
 
@@ -576,34 +613,25 @@ fn shellcheck_runner(args: &[&OsStr]) -> Result<(), Error> {
     if status.success() { Ok(()) } else { Err(Error::FailedCheck("shellcheck")) }
 }
 
-/// Check that spellchecker is installed then run it at the given path
-fn spellcheck_runner(args: &[&str]) -> Result<(), Error> {
-    // sync version with .github/workflows/spellcheck.yml
-    let expected_version = "typos-cli 1.34.0";
-    match Command::new("typos").arg("--version").output() {
-        Ok(o) => {
-            let stdout = String::from_utf8_lossy(&o.stdout);
-            if stdout.trim() != expected_version {
-                return Err(Error::Version {
-                    program: "typos",
-                    required: expected_version,
-                    installed: stdout.trim().to_string(),
-                });
+/// Ensure that spellchecker is installed then run it at the given path
+fn spellcheck_runner(
+    src_root: &Path,
+    outdir: &Path,
+    cargo: &Path,
+    args: &[&str],
+) -> Result<(), Error> {
+    let bin_path =
+        crate::ensure_version_or_cargo_install(outdir, cargo, "typos-cli", "typos", "1.34.0")?;
+    match Command::new(bin_path).current_dir(src_root).args(args).status() {
+        Ok(status) => {
+            if status.success() {
+                Ok(())
+            } else {
+                Err(Error::FailedCheck("typos"))
             }
         }
-        Err(e) if e.kind() == io::ErrorKind::NotFound => {
-            return Err(Error::MissingReq(
-                "typos",
-                "spellcheck file checks",
-                // sync version with .github/workflows/spellcheck.yml
-                Some("install tool via `cargo install typos-cli@1.34.0`".to_owned()),
-            ));
-        }
-        Err(e) => return Err(e.into()),
+        Err(err) => Err(Error::Generic(format!("failed to run typos tool: {err:?}"))),
     }
-
-    let status = Command::new("typos").args(args).status()?;
-    if status.success() { Ok(()) } else { Err(Error::FailedCheck("typos")) }
 }
 
 /// Check git for tracked files matching an extension
@@ -727,21 +755,19 @@ impl ExtraCheckArg {
         if !self.auto {
             return true;
         }
-        let ext = match self.lang {
-            ExtraCheckLang::Py => ".py",
-            ExtraCheckLang::Cpp => ".cpp",
-            ExtraCheckLang::Shell => ".sh",
-            ExtraCheckLang::Js => ".js",
+        let exts: &[&str] = match self.lang {
+            ExtraCheckLang::Py => &[".py"],
+            ExtraCheckLang::Cpp => &[".cpp"],
+            ExtraCheckLang::Shell => &[".sh"],
+            ExtraCheckLang::Js => &[".js", ".ts"],
             ExtraCheckLang::Spellcheck => {
-                for dir in SPELLCHECK_DIRS {
-                    if Path::new(filepath).starts_with(dir) {
-                        return true;
-                    }
+                if SPELLCHECK_DIRS.iter().any(|dir| Path::new(filepath).starts_with(dir)) {
+                    return true;
                 }
-                return false;
+                &[]
             }
         };
-        filepath.ends_with(ext)
+        exts.iter().any(|ext| filepath.ends_with(ext))
     }
 
     fn has_supported_kind(&self) -> bool {
diff --git a/src/tools/tidy/src/extra_checks/rustdoc_js.rs b/src/tools/tidy/src/extra_checks/rustdoc_js.rs
index 7708b128e23..5137e618367 100644
--- a/src/tools/tidy/src/extra_checks/rustdoc_js.rs
+++ b/src/tools/tidy/src/extra_checks/rustdoc_js.rs
@@ -40,19 +40,24 @@ fn rustdoc_js_files(librustdoc_path: &Path) -> Vec<PathBuf> {
     return files;
 }
 
-fn run_eslint(outdir: &Path, args: &[PathBuf], config_folder: PathBuf) -> Result<(), super::Error> {
-    let mut child = spawn_cmd(
-        Command::new(node_module_bin(outdir, "eslint"))
-            .arg("-c")
-            .arg(config_folder.join(".eslintrc.js"))
-            .args(args),
-    )?;
+fn run_eslint(
+    outdir: &Path,
+    args: &[PathBuf],
+    config_folder: PathBuf,
+    bless: bool,
+) -> Result<(), super::Error> {
+    let mut cmd = Command::new(node_module_bin(outdir, "eslint"));
+    if bless {
+        cmd.arg("--fix");
+    }
+    cmd.arg("-c").arg(config_folder.join(".eslintrc.js")).args(args);
+    let mut child = spawn_cmd(&mut cmd)?;
     match child.wait() {
         Ok(exit_status) => {
             if exit_status.success() {
                 return Ok(());
             }
-            Err(super::Error::FailedCheck("eslint command failed"))
+            Err(super::Error::FailedCheck("eslint"))
         }
         Err(error) => Err(super::Error::Generic(format!("eslint command failed: {error:?}"))),
     }
@@ -62,16 +67,17 @@ pub(super) fn lint(
     outdir: &Path,
     librustdoc_path: &Path,
     tools_path: &Path,
+    bless: bool,
 ) -> Result<(), super::Error> {
     let files_to_check = rustdoc_js_files(librustdoc_path);
     println!("Running eslint on rustdoc JS files");
-    run_eslint(outdir, &files_to_check, librustdoc_path.join("html/static"))?;
+    run_eslint(outdir, &files_to_check, librustdoc_path.join("html/static"), bless)?;
 
-    run_eslint(outdir, &[tools_path.join("rustdoc-js/tester.js")], tools_path.join("rustdoc-js"))?;
     run_eslint(
         outdir,
-        &[tools_path.join("rustdoc-gui/tester.js")],
-        tools_path.join("rustdoc-gui"),
+        &[tools_path.join("rustdoc-js/tester.js")],
+        tools_path.join("rustdoc-js"),
+        bless,
     )?;
     Ok(())
 }
@@ -88,7 +94,7 @@ pub(super) fn typecheck(outdir: &Path, librustdoc_path: &Path) -> Result<(), sup
             if exit_status.success() {
                 return Ok(());
             }
-            Err(super::Error::FailedCheck("tsc command failed"))
+            Err(super::Error::FailedCheck("tsc"))
         }
         Err(error) => Err(super::Error::Generic(format!("tsc command failed: {error:?}"))),
     }
@@ -106,7 +112,7 @@ pub(super) fn es_check(outdir: &Path, librustdoc_path: &Path) -> Result<(), supe
             if exit_status.success() {
                 return Ok(());
             }
-            Err(super::Error::FailedCheck("es-check command failed"))
+            Err(super::Error::FailedCheck("es-check"))
         }
         Err(error) => Err(super::Error::Generic(format!("es-check command failed: {error:?}"))),
     }
diff --git a/src/tools/tidy/src/features.rs b/src/tools/tidy/src/features.rs
index 6618ba24be6..0a0ba217c63 100644
--- a/src/tools/tidy/src/features.rs
+++ b/src/tools/tidy/src/features.rs
@@ -16,6 +16,7 @@ use std::num::NonZeroU32;
 use std::path::{Path, PathBuf};
 use std::{fmt, fs};
 
+use crate::diagnostics::{DiagCtx, RunningCheck};
 use crate::walk::{filter_dirs, filter_not_rust, walk, walk_many};
 
 #[cfg(test)]
@@ -91,13 +92,14 @@ pub fn check(
     tests_path: &Path,
     compiler_path: &Path,
     lib_path: &Path,
-    bad: &mut bool,
-    verbose: bool,
+    diag_ctx: DiagCtx,
 ) -> CollectedFeatures {
-    let mut features = collect_lang_features(compiler_path, bad);
+    let mut check = diag_ctx.start_check("features");
+
+    let mut features = collect_lang_features(compiler_path, &mut check);
     assert!(!features.is_empty());
 
-    let lib_features = get_and_check_lib_features(lib_path, bad, &features);
+    let lib_features = get_and_check_lib_features(lib_path, &mut check, &features);
     assert!(!lib_features.is_empty());
 
     walk_many(
@@ -121,7 +123,7 @@ pub fn check(
 
             for (i, line) in contents.lines().enumerate() {
                 let mut err = |msg: &str| {
-                    tidy_error!(bad, "{}:{}: {}", file.display(), i + 1, msg);
+                    check.error(format!("{}:{}: {}", file.display(), i + 1, msg));
                 };
 
                 let gate_test_str = "gate-test-";
@@ -175,7 +177,7 @@ pub fn check(
     }
 
     if !gate_untested.is_empty() {
-        tidy_error!(bad, "Found {} features without a gate test.", gate_untested.len());
+        check.error(format!("Found {} features without a gate test.", gate_untested.len()));
     }
 
     let (version, channel) = get_version_and_channel(src_path);
@@ -189,39 +191,32 @@ pub fn check(
         let file = feature.file.display();
         let line = feature.line;
         if since > version && since != Version::CurrentPlaceholder {
-            tidy_error!(
-                bad,
+            check.error(format!(
                 "{file}:{line}: The stabilization version {since} of {kind} feature `{feature_name}` is newer than the current {version}"
-            );
+            ));
         }
         if channel == "nightly" && since == version {
-            tidy_error!(
-                bad,
+            check.error(format!(
                 "{file}:{line}: The stabilization version {since} of {kind} feature `{feature_name}` is written out but should be {}",
                 version::VERSION_PLACEHOLDER
-            );
+            ));
         }
         if channel != "nightly" && since == Version::CurrentPlaceholder {
-            tidy_error!(
-                bad,
+            check.error(format!(
                 "{file}:{line}: The placeholder use of {kind} feature `{feature_name}` is not allowed on the {channel} channel",
-            );
+            ));
         }
     }
 
-    if *bad {
-        return CollectedFeatures { lib: lib_features, lang: features };
-    }
-
-    if verbose {
+    if !check.is_bad() && check.is_verbose_enabled() {
         let mut lines = Vec::new();
         lines.extend(format_features(&features, "lang"));
         lines.extend(format_features(&lib_features, "lib"));
-
         lines.sort();
-        for line in lines {
-            println!("* {line}");
-        }
+
+        check.verbose_msg(
+            lines.into_iter().map(|l| format!("* {l}")).collect::<Vec<String>>().join("\n"),
+        );
     }
 
     CollectedFeatures { lib: lib_features, lang: features }
@@ -275,15 +270,20 @@ fn test_filen_gate<'f>(filen_underscore: &'f str, features: &mut Features) -> Op
     None
 }
 
-pub fn collect_lang_features(base_compiler_path: &Path, bad: &mut bool) -> Features {
+pub fn collect_lang_features(base_compiler_path: &Path, check: &mut RunningCheck) -> Features {
     let mut features = Features::new();
-    collect_lang_features_in(&mut features, base_compiler_path, "accepted.rs", bad);
-    collect_lang_features_in(&mut features, base_compiler_path, "removed.rs", bad);
-    collect_lang_features_in(&mut features, base_compiler_path, "unstable.rs", bad);
+    collect_lang_features_in(&mut features, base_compiler_path, "accepted.rs", check);
+    collect_lang_features_in(&mut features, base_compiler_path, "removed.rs", check);
+    collect_lang_features_in(&mut features, base_compiler_path, "unstable.rs", check);
     features
 }
 
-fn collect_lang_features_in(features: &mut Features, base: &Path, file: &str, bad: &mut bool) {
+fn collect_lang_features_in(
+    features: &mut Features,
+    base: &Path,
+    file: &str,
+    check: &mut RunningCheck,
+) {
     let path = base.join("rustc_feature").join("src").join(file);
     let contents = t!(fs::read_to_string(&path));
 
@@ -315,13 +315,11 @@ fn collect_lang_features_in(features: &mut Features, base: &Path, file: &str, ba
 
         if line.starts_with(FEATURE_GROUP_START_PREFIX) {
             if in_feature_group {
-                tidy_error!(
-                    bad,
-                    "{}:{}: \
+                check.error(format!(
+                    "{}:{line_number}: \
                         new feature group is started without ending the previous one",
-                    path.display(),
-                    line_number,
-                );
+                    path.display()
+                ));
             }
 
             in_feature_group = true;
@@ -353,14 +351,10 @@ fn collect_lang_features_in(features: &mut Features, base: &Path, file: &str, ba
         let since = match since_str.parse() {
             Ok(since) => Some(since),
             Err(err) => {
-                tidy_error!(
-                    bad,
-                    "{}:{}: failed to parse since: {} ({:?})",
-                    path.display(),
-                    line_number,
-                    since_str,
-                    err,
-                );
+                check.error(format!(
+                    "{}:{line_number}: failed to parse since: {since_str} ({err:?})",
+                    path.display()
+                ));
                 None
             }
         };
@@ -371,13 +365,10 @@ fn collect_lang_features_in(features: &mut Features, base: &Path, file: &str, ba
                 let correct_index = match prev_names.binary_search(&name) {
                     Ok(_) => {
                         // This only occurs when the feature name has already been declared.
-                        tidy_error!(
-                            bad,
-                            "{}:{}: duplicate feature {}",
-                            path.display(),
-                            line_number,
-                            name,
-                        );
+                        check.error(format!(
+                            "{}:{line_number}: duplicate feature {name}",
+                            path.display()
+                        ));
                         // skip any additional checks for this line
                         continue;
                     }
@@ -398,14 +389,10 @@ fn collect_lang_features_in(features: &mut Features, base: &Path, file: &str, ba
                     )
                 };
 
-                tidy_error!(
-                    bad,
-                    "{}:{}: feature {} is not sorted by feature name (should be {})",
+                check.error(format!(
+                    "{}:{line_number}: feature {name} is not sorted by feature name (should be {correct_placement})",
                     path.display(),
-                    line_number,
-                    name,
-                    correct_placement,
-                );
+                ));
             }
             prev_names.push(name);
         }
@@ -413,13 +400,10 @@ fn collect_lang_features_in(features: &mut Features, base: &Path, file: &str, ba
         let issue_str = parts.next().unwrap().trim();
         let tracking_issue = if issue_str.starts_with("None") {
             if level == Status::Unstable && !next_feature_omits_tracking_issue {
-                tidy_error!(
-                    bad,
-                    "{}:{}: no tracking issue for feature {}",
+                check.error(format!(
+                    "{}:{line_number}: no tracking issue for feature {name}",
                     path.display(),
-                    line_number,
-                    name,
-                );
+                ));
             }
             None
         } else {
@@ -428,13 +412,11 @@ fn collect_lang_features_in(features: &mut Features, base: &Path, file: &str, ba
         };
         match features.entry(name.to_owned()) {
             Entry::Occupied(e) => {
-                tidy_error!(
-                    bad,
-                    "{}:{} feature {name} already specified with status '{}'",
+                check.error(format!(
+                    "{}:{line_number} feature {name} already specified with status '{}'",
                     path.display(),
-                    line_number,
                     e.get().level,
-                );
+                ));
             }
             Entry::Vacant(e) => {
                 e.insert(Feature {
@@ -458,7 +440,7 @@ fn collect_lang_features_in(features: &mut Features, base: &Path, file: &str, ba
 
 fn get_and_check_lib_features(
     base_src_path: &Path,
-    bad: &mut bool,
+    check: &mut RunningCheck,
     lang_features: &Features,
 ) -> Features {
     let mut lib_features = Features::new();
@@ -469,16 +451,12 @@ fn get_and_check_lib_features(
                     && f.tracking_issue != s.tracking_issue
                     && f.level != Status::Accepted
                 {
-                    tidy_error!(
-                        bad,
-                        "{}:{}: feature gate {} has inconsistent `issue`: \"{}\" mismatches the {} `issue` of \"{}\"",
+                    check.error(format!(
+                        "{}:{line}: feature gate {name} has inconsistent `issue`: \"{}\" mismatches the {display} `issue` of \"{}\"",
                         file.display(),
-                        line,
-                        name,
                         f.tracking_issue_display(),
-                        display,
                         s.tracking_issue_display(),
-                    );
+                    ));
                 }
             };
             check_features(&f, lang_features, "corresponding lang feature");
@@ -486,7 +464,7 @@ fn get_and_check_lib_features(
             lib_features.insert(name.to_owned(), f);
         }
         Err(msg) => {
-            tidy_error!(bad, "{}:{}: {}", file.display(), line, msg);
+            check.error(format!("{}:{line}: {msg}", file.display()));
         }
     });
     lib_features
diff --git a/src/tools/tidy/src/filenames.rs b/src/tools/tidy/src/filenames.rs
index 53115f4eaa4..835cbefbf69 100644
--- a/src/tools/tidy/src/filenames.rs
+++ b/src/tools/tidy/src/filenames.rs
@@ -10,7 +10,10 @@
 use std::path::Path;
 use std::process::Command;
 
-pub fn check(root_path: &Path, bad: &mut bool) {
+use crate::diagnostics::DiagCtx;
+
+pub fn check(root_path: &Path, diag_ctx: DiagCtx) {
+    let mut check = diag_ctx.start_check("filenames");
     let stat_output = Command::new("git")
         .arg("-C")
         .arg(root_path)
@@ -20,20 +23,17 @@ pub fn check(root_path: &Path, bad: &mut bool) {
         .stdout;
     for filename in stat_output.split(|&b| b == 0) {
         match str::from_utf8(filename) {
-            Err(_) => tidy_error!(
-                bad,
+            Err(_) => check.error(format!(
                 r#"non-UTF8 file names are not supported: "{}""#,
                 String::from_utf8_lossy(filename),
-            ),
-            Ok(name) if name.chars().any(|c| c.is_control()) => tidy_error!(
-                bad,
+            )),
+            Ok(name) if name.chars().any(|c| c.is_control()) => check.error(format!(
                 r#"control characters are not supported in file names: "{}""#,
                 String::from_utf8_lossy(filename),
-            ),
-            Ok(name) if name.contains(':') => tidy_error!(
-                bad,
+            )),
+            Ok(name) if name.contains(':') => check.error(format!(
                 r#"":" is not supported in file names because of Windows compatibility: "{name}""#,
-            ),
+            )),
             _ => (),
         }
     }
diff --git a/src/tools/tidy/src/fluent_alphabetical.rs b/src/tools/tidy/src/fluent_alphabetical.rs
index 48d14a37514..02b914c21ae 100644
--- a/src/tools/tidy/src/fluent_alphabetical.rs
+++ b/src/tools/tidy/src/fluent_alphabetical.rs
@@ -7,6 +7,7 @@ use std::path::Path;
 
 use regex::Regex;
 
+use crate::diagnostics::{CheckId, DiagCtx, RunningCheck};
 use crate::walk::{filter_dirs, walk};
 
 fn message() -> &'static Regex {
@@ -20,19 +21,17 @@ fn is_fluent(path: &Path) -> bool {
 fn check_alphabetic(
     filename: &str,
     fluent: &str,
-    bad: &mut bool,
+    check: &mut RunningCheck,
     all_defined_msgs: &mut HashMap<String, String>,
 ) {
     let mut matches = message().captures_iter(fluent).peekable();
     while let Some(m) = matches.next() {
         let name = m.get(1).unwrap();
         if let Some(defined_filename) = all_defined_msgs.get(name.as_str()) {
-            tidy_error!(
-                bad,
-                "{filename}: message `{}` is already defined in {}",
+            check.error(format!(
+                "{filename}: message `{}` is already defined in {defined_filename}",
                 name.as_str(),
-                defined_filename,
-            );
+            ));
         }
 
         all_defined_msgs.insert(name.as_str().to_owned(), filename.to_owned());
@@ -40,13 +39,12 @@ fn check_alphabetic(
         if let Some(next) = matches.peek() {
             let next = next.get(1).unwrap();
             if name.as_str() > next.as_str() {
-                tidy_error!(
-                    bad,
+                check.error(format!(
                     "{filename}: message `{}` appears before `{}`, but is alphabetically later than it
 run `./x.py test tidy --bless` to sort the file correctly",
                     name.as_str(),
                     next.as_str()
-                );
+                ));
             }
         } else {
             break;
@@ -57,7 +55,7 @@ run `./x.py test tidy --bless` to sort the file correctly",
 fn sort_messages(
     filename: &str,
     fluent: &str,
-    bad: &mut bool,
+    check: &mut RunningCheck,
     all_defined_msgs: &mut HashMap<String, String>,
 ) -> String {
     let mut chunks = vec![];
@@ -65,12 +63,10 @@ fn sort_messages(
     for line in fluent.lines() {
         if let Some(name) = message().find(line) {
             if let Some(defined_filename) = all_defined_msgs.get(name.as_str()) {
-                tidy_error!(
-                    bad,
-                    "{filename}: message `{}` is already defined in {}",
+                check.error(format!(
+                    "{filename}: message `{}` is already defined in {defined_filename}",
                     name.as_str(),
-                    defined_filename,
-                );
+                ));
             }
 
             all_defined_msgs.insert(name.as_str().to_owned(), filename.to_owned());
@@ -88,7 +84,9 @@ fn sort_messages(
     out
 }
 
-pub fn check(path: &Path, bless: bool, bad: &mut bool) {
+pub fn check(path: &Path, bless: bool, diag_ctx: DiagCtx) {
+    let mut check = diag_ctx.start_check(CheckId::new("fluent_alphabetical").path(path));
+
     let mut all_defined_msgs = HashMap::new();
     walk(
         path,
@@ -98,7 +96,7 @@ pub fn check(path: &Path, bless: bool, bad: &mut bool) {
                 let sorted = sort_messages(
                     ent.path().to_str().unwrap(),
                     contents,
-                    bad,
+                    &mut check,
                     &mut all_defined_msgs,
                 );
                 if sorted != contents {
@@ -110,12 +108,12 @@ pub fn check(path: &Path, bless: bool, bad: &mut bool) {
                 check_alphabetic(
                     ent.path().to_str().unwrap(),
                     contents,
-                    bad,
+                    &mut check,
                     &mut all_defined_msgs,
                 );
             }
         },
     );
 
-    crate::fluent_used::check(path, all_defined_msgs, bad);
+    crate::fluent_used::check(path, all_defined_msgs, diag_ctx);
 }
diff --git a/src/tools/tidy/src/fluent_lowercase.rs b/src/tools/tidy/src/fluent_lowercase.rs
new file mode 100644
index 00000000000..1d80fda8f3b
--- /dev/null
+++ b/src/tools/tidy/src/fluent_lowercase.rs
@@ -0,0 +1,65 @@
+//! Checks that the error messages start with a lowercased letter (except when allowed to).
+
+use std::path::Path;
+
+use fluent_syntax::ast::{Entry, Message, PatternElement};
+
+use crate::diagnostics::{CheckId, DiagCtx, RunningCheck};
+use crate::walk::{filter_dirs, walk};
+
+#[rustfmt::skip]
+const ALLOWED_CAPITALIZED_WORDS: &[&str] = &[
+    // tidy-alphabetical-start
+    "ABI",
+    "ABIs",
+    "ADT",
+    "C",
+    "CGU",
+    "Ferris",
+    "MIR",
+    "OK",
+    "Rust",
+    "VS", // VS Code
+    // tidy-alphabetical-end
+];
+
+fn filter_fluent(path: &Path) -> bool {
+    if let Some(ext) = path.extension() { ext.to_str() != Some("ftl") } else { true }
+}
+
+fn is_allowed_capitalized_word(msg: &str) -> bool {
+    ALLOWED_CAPITALIZED_WORDS.iter().any(|word| {
+        msg.strip_prefix(word)
+            .map(|tail| tail.chars().next().map(|c| c == '-' || c.is_whitespace()).unwrap_or(true))
+            .unwrap_or_default()
+    })
+}
+
+fn check_lowercase(filename: &str, contents: &str, check: &mut RunningCheck) {
+    let (Ok(parse) | Err((parse, _))) = fluent_syntax::parser::parse(contents);
+
+    for entry in &parse.body {
+        if let Entry::Message(msg) = entry
+            && let Message { value: Some(pattern), .. } = msg
+            && let [first_pattern, ..] = &pattern.elements[..]
+            && let PatternElement::TextElement { value } = first_pattern
+            && value.chars().next().is_some_and(char::is_uppercase)
+            && !is_allowed_capitalized_word(value)
+        {
+            check.error(format!(
+                "{filename}: message `{value}` starts with an uppercase letter. Fix it or add it to `ALLOWED_CAPITALIZED_WORDS`"
+            ));
+        }
+    }
+}
+
+pub fn check(path: &Path, diag_ctx: DiagCtx) {
+    let mut check = diag_ctx.start_check(CheckId::new("fluent_lowercase").path(path));
+    walk(
+        path,
+        |path, is_dir| filter_dirs(path) || (!is_dir && filter_fluent(path)),
+        &mut |ent, contents| {
+            check_lowercase(ent.path().to_str().unwrap(), contents, &mut check);
+        },
+    );
+}
diff --git a/src/tools/tidy/src/fluent_period.rs b/src/tools/tidy/src/fluent_period.rs
index 836b5699289..c7c760b8d54 100644
--- a/src/tools/tidy/src/fluent_period.rs
+++ b/src/tools/tidy/src/fluent_period.rs
@@ -4,6 +4,7 @@ use std::path::Path;
 
 use fluent_syntax::ast::{Entry, PatternElement};
 
+use crate::diagnostics::{CheckId, DiagCtx, RunningCheck};
 use crate::walk::{filter_dirs, walk};
 
 fn filter_fluent(path: &Path) -> bool {
@@ -20,7 +21,7 @@ const ALLOWLIST: &[&str] = &[
     "incremental_corrupt_file",
 ];
 
-fn check_period(filename: &str, contents: &str, bad: &mut bool) {
+fn check_period(filename: &str, contents: &str, check: &mut RunningCheck) {
     if filename.contains("codegen") {
         // FIXME: Too many codegen messages have periods right now...
         return;
@@ -40,7 +41,7 @@ fn check_period(filename: &str, contents: &str, bad: &mut bool) {
                 if value.ends_with(".") && !value.ends_with("...") {
                     let ll = find_line(contents, value);
                     let name = m.id.name;
-                    tidy_error!(bad, "{filename}:{ll}: message `{name}` ends in a period");
+                    check.error(format!("{filename}:{ll}: message `{name}` ends in a period"));
                 }
             }
 
@@ -56,7 +57,7 @@ fn check_period(filename: &str, contents: &str, bad: &mut bool) {
                 {
                     let ll = find_line(contents, value);
                     let name = attr.id.name;
-                    tidy_error!(bad, "{filename}:{ll}: attr `{name}` ends in a period");
+                    check.error(format!("{filename}:{ll}: attr `{name}` ends in a period"));
                 }
             }
         }
@@ -74,12 +75,14 @@ fn find_line(haystack: &str, needle: &str) -> usize {
     1
 }
 
-pub fn check(path: &Path, bad: &mut bool) {
+pub fn check(path: &Path, diag_ctx: DiagCtx) {
+    let mut check = diag_ctx.start_check(CheckId::new("fluent_period").path(path));
+
     walk(
         path,
         |path, is_dir| filter_dirs(path) || (!is_dir && filter_fluent(path)),
         &mut |ent, contents| {
-            check_period(ent.path().to_str().unwrap(), contents, bad);
+            check_period(ent.path().to_str().unwrap(), contents, &mut check);
         },
     );
 }
diff --git a/src/tools/tidy/src/fluent_used.rs b/src/tools/tidy/src/fluent_used.rs
index 909bf482ddf..2047089631b 100644
--- a/src/tools/tidy/src/fluent_used.rs
+++ b/src/tools/tidy/src/fluent_used.rs
@@ -3,6 +3,7 @@
 use std::collections::HashMap;
 use std::path::Path;
 
+use crate::diagnostics::{CheckId, DiagCtx};
 use crate::walk::{filter_dirs, walk};
 
 fn filter_used_messages(
@@ -27,13 +28,15 @@ fn filter_used_messages(
     }
 }
 
-pub fn check(path: &Path, mut all_defined_msgs: HashMap<String, String>, bad: &mut bool) {
+pub fn check(path: &Path, mut all_defined_msgs: HashMap<String, String>, diag_ctx: DiagCtx) {
+    let mut check = diag_ctx.start_check(CheckId::new("fluent_used").path(path));
+
     let mut msgs_appear_only_once = HashMap::new();
     walk(path, |path, _| filter_dirs(path), &mut |_, contents| {
         filter_used_messages(contents, &mut all_defined_msgs, &mut msgs_appear_only_once);
     });
 
     for (name, filename) in msgs_appear_only_once {
-        tidy_error!(bad, "{filename}: message `{}` is not used", name,);
+        check.error(format!("{filename}: message `{name}` is not used"));
     }
 }
diff --git a/src/tools/tidy/src/gcc_submodule.rs b/src/tools/tidy/src/gcc_submodule.rs
index 5d726c3ea48..3a6e3247de6 100644
--- a/src/tools/tidy/src/gcc_submodule.rs
+++ b/src/tools/tidy/src/gcc_submodule.rs
@@ -4,7 +4,11 @@
 use std::path::Path;
 use std::process::Command;
 
-pub fn check(root_path: &Path, compiler_path: &Path, bad: &mut bool) {
+use crate::diagnostics::DiagCtx;
+
+pub fn check(root_path: &Path, compiler_path: &Path, diag_ctx: DiagCtx) {
+    let mut check = diag_ctx.start_check("gcc_submodule");
+
     let cg_gcc_version_path = compiler_path.join("rustc_codegen_gcc/libgccjit.version");
     let cg_gcc_version = std::fs::read_to_string(&cg_gcc_version_path)
         .unwrap_or_else(|_| {
@@ -24,6 +28,12 @@ pub fn check(root_path: &Path, compiler_path: &Path, bad: &mut bool) {
         .output()
         .expect("Cannot determine git SHA of the src/gcc checkout");
 
+    // Git is not available or we are in a tarball
+    if !git_output.status.success() {
+        check.message("Cannot figure out the SHA of the GCC submodule");
+        return;
+    }
+
     // This can return e.g.
     // -e607be166673a8de9fc07f6f02c60426e556c5f2 src/gcc
     //  e607be166673a8de9fc07f6f02c60426e556c5f2 src/gcc (master-e607be166673a8de9fc07f6f02c60426e556c5f2.e607be)
@@ -37,12 +47,11 @@ pub fn check(root_path: &Path, compiler_path: &Path, bad: &mut bool) {
     // The SHA can start with + if the submodule is modified or - if it is not checked out.
     let gcc_submodule_sha = git_output.trim_start_matches(['+', '-']);
     if gcc_submodule_sha != cg_gcc_version {
-        *bad = true;
-        eprintln!(
+        check.error(format!(
             r#"Commit SHA of the src/gcc submodule (`{gcc_submodule_sha}`) does not match the required GCC version of the GCC codegen backend (`{cg_gcc_version}`).
 Make sure to set the src/gcc submodule to commit {cg_gcc_version}.
 The GCC codegen backend commit is configured at {}."#,
             cg_gcc_version_path.display(),
-        );
+        ));
     }
 }
diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt
index ee06707415f..849dcb9e88f 100644
--- a/src/tools/tidy/src/issues.txt
+++ b/src/tools/tidy/src/issues.txt
@@ -2021,7 +2021,6 @@ ui/parser/issues/issue-5806.rs
 ui/parser/issues/issue-58094-missing-right-square-bracket.rs
 ui/parser/issues/issue-58856-1.rs
 ui/parser/issues/issue-58856-2.rs
-ui/parser/issues/issue-59418.rs
 ui/parser/issues/issue-60075.rs
 ui/parser/issues/issue-61858.rs
 ui/parser/issues/issue-62524.rs
diff --git a/src/tools/tidy/src/known_bug.rs b/src/tools/tidy/src/known_bug.rs
index e1921715ab9..d3b75e0cf5b 100644
--- a/src/tools/tidy/src/known_bug.rs
+++ b/src/tools/tidy/src/known_bug.rs
@@ -2,9 +2,11 @@
 
 use std::path::Path;
 
+use crate::diagnostics::{CheckId, DiagCtx};
 use crate::walk::*;
 
-pub fn check(filepath: &Path, bad: &mut bool) {
+pub fn check(filepath: &Path, diag_ctx: DiagCtx) {
+    let mut check = diag_ctx.start_check(CheckId::new("known_bug").path(filepath));
     walk(filepath, |path, _is_dir| filter_not_rust(path), &mut |entry, contents| {
         let file: &Path = entry.path();
 
@@ -19,11 +21,10 @@ pub fn check(filepath: &Path, bad: &mut bool) {
             [.., "tests", "crashes", "auxiliary", _aux_file_rs]
         ) && !contents.lines().any(|line| line.starts_with("//@ known-bug: "))
         {
-            tidy_error!(
-                bad,
+            check.error(format!(
                 "{} crash/ice test does not have a \"//@ known-bug: \" directive",
                 file.display()
-            );
+            ));
         }
     });
 }
diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs
index 4ea9d051ddb..0bfee93796b 100644
--- a/src/tools/tidy/src/lib.rs
+++ b/src/tools/tidy/src/lib.rs
@@ -4,12 +4,15 @@
 //! to be used by tools.
 
 use std::ffi::OsStr;
+use std::path::{Path, PathBuf};
 use std::process::Command;
+use std::{env, io};
 
 use build_helper::ci::CiEnv;
 use build_helper::git::{GitConfig, get_closest_upstream_commit};
 use build_helper::stage0_parser::{Stage0Config, parse_stage0_file};
-use termcolor::WriteColor;
+
+use crate::diagnostics::{DiagCtx, RunningCheck};
 
 macro_rules! static_regex {
     ($re:literal) => {{
@@ -41,35 +44,6 @@ macro_rules! t {
     };
 }
 
-macro_rules! tidy_error {
-    ($bad:expr, $($fmt:tt)*) => ({
-        $crate::tidy_error(&format_args!($($fmt)*).to_string()).expect("failed to output error");
-        *$bad = true;
-    });
-}
-
-macro_rules! tidy_error_ext {
-    ($tidy_error:path, $bad:expr, $($fmt:tt)*) => ({
-        $tidy_error(&format_args!($($fmt)*).to_string()).expect("failed to output error");
-        *$bad = true;
-    });
-}
-
-fn tidy_error(args: &str) -> std::io::Result<()> {
-    use std::io::Write;
-
-    use termcolor::{Color, ColorChoice, ColorSpec, StandardStream};
-
-    let mut stderr = StandardStream::stdout(ColorChoice::Auto);
-    stderr.set_color(ColorSpec::new().set_fg(Some(Color::Red)))?;
-
-    write!(&mut stderr, "tidy error")?;
-    stderr.set_color(&ColorSpec::new())?;
-
-    writeln!(&mut stderr, ": {args}")?;
-    Ok(())
-}
-
 pub struct CiInfo {
     pub git_merge_commit_email: String,
     pub nightly_branch: String,
@@ -78,7 +52,9 @@ pub struct CiInfo {
 }
 
 impl CiInfo {
-    pub fn new(bad: &mut bool) -> Self {
+    pub fn new(diag_ctx: DiagCtx) -> Self {
+        let mut check = diag_ctx.start_check("CI history");
+
         let stage0 = parse_stage0_file();
         let Stage0Config { nightly_branch, git_merge_commit_email, .. } = stage0.config;
 
@@ -91,11 +67,14 @@ impl CiInfo {
         let base_commit = match get_closest_upstream_commit(None, &info.git_config(), info.ci_env) {
             Ok(Some(commit)) => Some(commit),
             Ok(None) => {
-                info.error_if_in_ci("no base commit found", bad);
+                info.error_if_in_ci("no base commit found", &mut check);
                 None
             }
             Err(error) => {
-                info.error_if_in_ci(&format!("failed to retrieve base commit: {error}"), bad);
+                info.error_if_in_ci(
+                    &format!("failed to retrieve base commit: {error}"),
+                    &mut check,
+                );
                 None
             }
         };
@@ -110,12 +89,11 @@ impl CiInfo {
         }
     }
 
-    pub fn error_if_in_ci(&self, msg: &str, bad: &mut bool) {
+    pub fn error_if_in_ci(&self, msg: &str, check: &mut RunningCheck) {
         if self.ci_env.is_running_in_ci() {
-            *bad = true;
-            eprintln!("tidy check error: {msg}");
+            check.error(msg);
         } else {
-            eprintln!("tidy check warning: {msg}. Some checks will be skipped.");
+            check.warning(format!("{msg}. Some checks will be skipped."));
         }
     }
 }
@@ -180,10 +158,75 @@ pub fn files_modified(ci_info: &CiInfo, pred: impl Fn(&str) -> bool) -> bool {
     !v.is_empty()
 }
 
+/// If the given executable is installed with the given version, use that,
+/// otherwise install via cargo.
+pub fn ensure_version_or_cargo_install(
+    build_dir: &Path,
+    cargo: &Path,
+    pkg_name: &str,
+    bin_name: &str,
+    version: &str,
+) -> io::Result<PathBuf> {
+    // ignore the process exit code here and instead just let the version number check fail.
+    // we also importantly don't return if the program wasn't installed,
+    // instead we want to continue to the fallback.
+    'ck: {
+        // FIXME: rewrite as if-let chain once this crate is 2024 edition.
+        let Ok(output) = Command::new(bin_name).arg("--version").output() else {
+            break 'ck;
+        };
+        let Ok(s) = str::from_utf8(&output.stdout) else {
+            break 'ck;
+        };
+        let Some(v) = s.trim().split_whitespace().last() else {
+            break 'ck;
+        };
+        if v == version {
+            return Ok(PathBuf::from(bin_name));
+        }
+    }
+
+    let tool_root_dir = build_dir.join("misc-tools");
+    let tool_bin_dir = tool_root_dir.join("bin");
+    eprintln!("building external tool {bin_name} from package {pkg_name}@{version}");
+    // use --force to ensure that if the required version is bumped, we update it.
+    // use --target-dir to ensure we have a build cache so repeated invocations aren't slow.
+    // modify PATH so that cargo doesn't print a warning telling the user to modify the path.
+    let cargo_exit_code = Command::new(cargo)
+        .args(["install", "--locked", "--force", "--quiet"])
+        .arg("--root")
+        .arg(&tool_root_dir)
+        .arg("--target-dir")
+        .arg(tool_root_dir.join("target"))
+        .arg(format!("{pkg_name}@{version}"))
+        .env(
+            "PATH",
+            env::join_paths(
+                env::split_paths(&env::var("PATH").unwrap())
+                    .chain(std::iter::once(tool_bin_dir.clone())),
+            )
+            .expect("build dir contains invalid char"),
+        )
+        .env("RUSTFLAGS", "-Copt-level=0")
+        .spawn()?
+        .wait()?;
+    if !cargo_exit_code.success() {
+        return Err(io::Error::other("cargo install failed"));
+    }
+    let bin_path = tool_bin_dir.join(bin_name).with_extension(env::consts::EXE_EXTENSION);
+    assert!(
+        matches!(bin_path.try_exists(), Ok(true)),
+        "cargo install did not produce the expected binary"
+    );
+    eprintln!("finished building tool {bin_name}");
+    Ok(bin_path)
+}
+
 pub mod alphabetical;
 pub mod bins;
 pub mod debug_artifacts;
 pub mod deps;
+pub mod diagnostics;
 pub mod edition;
 pub mod error_codes;
 pub mod extdeps;
@@ -191,6 +234,7 @@ pub mod extra_checks;
 pub mod features;
 pub mod filenames;
 pub mod fluent_alphabetical;
+pub mod fluent_lowercase;
 pub mod fluent_period;
 mod fluent_used;
 pub mod gcc_submodule;
diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs
index cd2567ddb64..93bc1611199 100644
--- a/src/tools/tidy/src/main.rs
+++ b/src/tools/tidy/src/main.rs
@@ -8,10 +8,10 @@ use std::collections::VecDeque;
 use std::num::NonZeroUsize;
 use std::path::PathBuf;
 use std::str::FromStr;
-use std::sync::atomic::{AtomicBool, Ordering};
 use std::thread::{self, ScopedJoinHandle, scope};
 use std::{env, process};
 
+use tidy::diagnostics::{COLOR_ERROR, COLOR_SUCCESS, DiagCtx, output_message};
 use tidy::*;
 
 fn main() {
@@ -50,9 +50,8 @@ fn main() {
     let extra_checks =
         cfg_args.iter().find(|s| s.starts_with("--extra-checks=")).map(String::as_str);
 
-    let mut bad = false;
-    let ci_info = CiInfo::new(&mut bad);
-    let bad = std::sync::Arc::new(AtomicBool::new(bad));
+    let diag_ctx = DiagCtx::new(&root_path, verbose);
+    let ci_info = CiInfo::new(diag_ctx.clone());
 
     let drain_handles = |handles: &mut VecDeque<ScopedJoinHandle<'_, ()>>| {
         // poll all threads for completion before awaiting the oldest one
@@ -87,12 +86,9 @@ fn main() {
             (@ $p:ident, name=$name:expr $(, $args:expr)* ) => {
                 drain_handles(&mut handles);
 
+                let diag_ctx = diag_ctx.clone();
                 let handle = thread::Builder::new().name($name).spawn_scoped(s, || {
-                    let mut flag = false;
-                    $p::check($($args, )* &mut flag);
-                    if (flag) {
-                        bad.store(true, Ordering::Relaxed);
-                    }
+                    $p::check($($args, )* diag_ctx);
                 }).unwrap();
                 handles.push_back(handle);
             }
@@ -118,9 +114,10 @@ fn main() {
         check!(unknown_revision, &tests_path);
 
         // Checks that only make sense for the compiler.
-        check!(error_codes, &root_path, &[&compiler_path, &librustdoc_path], verbose, &ci_info);
+        check!(error_codes, &root_path, &[&compiler_path, &librustdoc_path], &ci_info);
         check!(fluent_alphabetical, &compiler_path, bless);
         check!(fluent_period, &compiler_path);
+        check!(fluent_lowercase, &compiler_path);
         check!(target_policy, &root_path);
         check!(gcc_submodule, &root_path, &compiler_path);
 
@@ -154,25 +151,12 @@ fn main() {
         check!(x_version, &root_path, &cargo);
 
         check!(triagebot, &root_path);
-
         check!(filenames, &root_path);
 
         let collected = {
             drain_handles(&mut handles);
 
-            let mut flag = false;
-            let r = features::check(
-                &src_path,
-                &tests_path,
-                &compiler_path,
-                &library_path,
-                &mut flag,
-                verbose,
-            );
-            if flag {
-                bad.store(true, Ordering::Relaxed);
-            }
-            r
+            features::check(&src_path, &tests_path, &compiler_path, &library_path, diag_ctx.clone())
         };
         check!(unstable_book, &src_path, collected);
 
@@ -184,14 +168,29 @@ fn main() {
             &librustdoc_path,
             &tools_path,
             &npm,
+            &cargo,
             bless,
             extra_checks,
             pos_args
         );
     });
 
-    if bad.load(Ordering::Relaxed) {
-        eprintln!("some tidy checks failed");
+    let failed_checks = diag_ctx.into_failed_checks();
+    if !failed_checks.is_empty() {
+        let mut failed: Vec<String> =
+            failed_checks.into_iter().map(|c| c.id().to_string()).collect();
+        failed.sort();
+        output_message(
+            &format!(
+                "The following check{} failed: {}",
+                if failed.len() > 1 { "s" } else { "" },
+                failed.join(", ")
+            ),
+            None,
+            Some(COLOR_ERROR),
+        );
         process::exit(1);
+    } else {
+        output_message("All tidy checks succeeded", None, Some(COLOR_SUCCESS));
     }
 }
diff --git a/src/tools/tidy/src/mir_opt_tests.rs b/src/tools/tidy/src/mir_opt_tests.rs
index 6119eb58383..0f9fab51d09 100644
--- a/src/tools/tidy/src/mir_opt_tests.rs
+++ b/src/tools/tidy/src/mir_opt_tests.rs
@@ -5,9 +5,10 @@ use std::path::{Path, PathBuf};
 
 use miropt_test_tools::PanicStrategy;
 
+use crate::diagnostics::{CheckId, DiagCtx, RunningCheck};
 use crate::walk::walk_no_read;
 
-fn check_unused_files(path: &Path, bless: bool, bad: &mut bool) {
+fn check_unused_files(path: &Path, bless: bool, check: &mut RunningCheck) {
     let mut rs_files = Vec::<PathBuf>::new();
     let mut output_files = HashSet::<PathBuf>::new();
 
@@ -37,18 +38,17 @@ fn check_unused_files(path: &Path, bless: bool, bad: &mut bool) {
 
     for extra in output_files {
         if !bless {
-            tidy_error!(
-                bad,
+            check.error(format!(
                 "the following output file is not associated with any mir-opt test, you can remove it: {}",
                 extra.display()
-            );
+            ));
         } else {
             let _ = std::fs::remove_file(extra);
         }
     }
 }
 
-fn check_dash_files(path: &Path, bless: bool, bad: &mut bool) {
+fn check_dash_files(path: &Path, bless: bool, check: &mut RunningCheck) {
     for file in walkdir::WalkDir::new(path.join("mir-opt"))
         .into_iter()
         .filter_map(Result::ok)
@@ -60,11 +60,10 @@ fn check_dash_files(path: &Path, bless: bool, bad: &mut bool) {
             && name.contains('-')
         {
             if !bless {
-                tidy_error!(
-                    bad,
+                check.error(format!(
                     "mir-opt test files should not have dashes in them: {}",
                     path.display()
-                );
+                ));
             } else {
                 let new_name = name.replace('-', "_");
                 let mut new_path = path.to_owned();
@@ -75,7 +74,9 @@ fn check_dash_files(path: &Path, bless: bool, bad: &mut bool) {
     }
 }
 
-pub fn check(path: &Path, bless: bool, bad: &mut bool) {
-    check_unused_files(path, bless, bad);
-    check_dash_files(path, bless, bad);
+pub fn check(path: &Path, bless: bool, diag_ctx: DiagCtx) {
+    let mut check = diag_ctx.start_check(CheckId::new("mir_opt_tests").path(path));
+
+    check_unused_files(path, bless, &mut check);
+    check_dash_files(path, bless, &mut check);
 }
diff --git a/src/tools/tidy/src/pal.rs b/src/tools/tidy/src/pal.rs
index 5b8b44429bb..cefad7d9596 100644
--- a/src/tools/tidy/src/pal.rs
+++ b/src/tools/tidy/src/pal.rs
@@ -32,6 +32,7 @@
 
 use std::path::Path;
 
+use crate::diagnostics::{CheckId, DiagCtx, RunningCheck};
 use crate::walk::{filter_dirs, walk};
 
 // Paths that may contain platform-specific code.
@@ -53,6 +54,7 @@ const EXCEPTION_PATHS: &[&str] = &[
     // core::ffi contains platform-specific type and linkage configuration
     "library/core/src/ffi/mod.rs",
     "library/core/src/ffi/primitives.rs",
+    "library/core/src/os", // Platform-specific public interfaces
     "library/std/src/sys", // Platform-specific code for std lives here.
     "library/std/src/os",  // Platform-specific public interfaces
     // Temporary `std` exceptions
@@ -66,7 +68,9 @@ const EXCEPTION_PATHS: &[&str] = &[
     "library/std/src/io/error.rs", // Repr unpacked needed for UEFI
 ];
 
-pub fn check(path: &Path, bad: &mut bool) {
+pub fn check(path: &Path, diag_ctx: DiagCtx) {
+    let mut check = diag_ctx.start_check(CheckId::new("pal").path(path));
+
     // Sanity check that the complex parsing here works.
     let mut saw_target_arch = false;
     let mut saw_cfg_bang = false;
@@ -87,7 +91,7 @@ pub fn check(path: &Path, bad: &mut bool) {
             return;
         }
 
-        check_cfgs(contents, file, bad, &mut saw_target_arch, &mut saw_cfg_bang);
+        check_cfgs(contents, file, &mut check, &mut saw_target_arch, &mut saw_cfg_bang);
     });
 
     assert!(saw_target_arch);
@@ -97,7 +101,7 @@ pub fn check(path: &Path, bad: &mut bool) {
 fn check_cfgs(
     contents: &str,
     file: &Path,
-    bad: &mut bool,
+    check: &mut RunningCheck,
     saw_target_arch: &mut bool,
     saw_cfg_bang: &mut bool,
 ) {
@@ -114,7 +118,7 @@ fn check_cfgs(
             Ok(_) => unreachable!(),
             Err(i) => i + 1,
         };
-        tidy_error!(bad, "{}:{}: platform-specific cfg: {}", file.display(), line, cfg);
+        check.error(format!("{}:{line}: platform-specific cfg: {cfg}", file.display()));
     };
 
     for (idx, cfg) in cfgs {
diff --git a/src/tools/tidy/src/rustdoc_css_themes.rs b/src/tools/tidy/src/rustdoc_css_themes.rs
index af36f9ba58e..8d4af7a3bd5 100644
--- a/src/tools/tidy/src/rustdoc_css_themes.rs
+++ b/src/tools/tidy/src/rustdoc_css_themes.rs
@@ -3,7 +3,11 @@
 
 use std::path::Path;
 
-pub fn check(librustdoc_path: &Path, bad: &mut bool) {
+use crate::diagnostics::{CheckId, DiagCtx, RunningCheck};
+
+pub fn check(librustdoc_path: &Path, diag_ctx: DiagCtx) {
+    let mut check = diag_ctx.start_check(CheckId::new("rustdoc_css_themes").path(librustdoc_path));
+
     let rustdoc_css = "html/static/css/rustdoc.css";
     let noscript_css = "html/static/css/noscript.css";
     let rustdoc_css_contents = std::fs::read_to_string(librustdoc_path.join(rustdoc_css))
@@ -14,13 +18,13 @@ pub fn check(librustdoc_path: &Path, bad: &mut bool) {
         "light",
         rustdoc_css_contents.lines().enumerate().map(|(i, l)| (i + 1, l.trim())),
         noscript_css_contents.lines().enumerate().map(|(i, l)| (i + 1, l.trim())),
-        bad,
+        &mut check,
     );
     compare_themes_from_files(
         "dark",
         rustdoc_css_contents.lines().enumerate(),
         noscript_css_contents.lines().enumerate(),
-        bad,
+        &mut check,
     );
 }
 
@@ -28,7 +32,7 @@ fn compare_themes_from_files<'a>(
     name: &str,
     mut rustdoc_css_lines: impl Iterator<Item = (usize, &'a str)>,
     mut noscript_css_lines: impl Iterator<Item = (usize, &'a str)>,
-    bad: &mut bool,
+    check: &mut RunningCheck,
 ) {
     let begin_theme_pat = format!("/* Begin theme: {name}");
     let mut found_theme = None;
@@ -38,10 +42,9 @@ fn compare_themes_from_files<'a>(
             continue;
         }
         if let Some(found_theme) = found_theme {
-            tidy_error!(
-                bad,
+            check.error(format!(
                 "rustdoc.css contains two {name} themes on lines {rustdoc_css_line_number} and {found_theme}",
-            );
+            ));
             return;
         }
         found_theme = Some(rustdoc_css_line_number);
@@ -50,14 +53,13 @@ fn compare_themes_from_files<'a>(
                 continue;
             }
             if let Some(found_theme_noscript) = found_theme_noscript {
-                tidy_error!(
-                    bad,
+                check.error(format!(
                     "noscript.css contains two {name} themes on lines {noscript_css_line_number} and {found_theme_noscript}",
-                );
+                ));
                 return;
             }
             found_theme_noscript = Some(noscript_css_line_number);
-            compare_themes(name, &mut rustdoc_css_lines, &mut noscript_css_lines, bad);
+            compare_themes(name, &mut rustdoc_css_lines, &mut noscript_css_lines, check);
         }
     }
 }
@@ -66,7 +68,7 @@ fn compare_themes<'a>(
     name: &str,
     rustdoc_css_lines: impl Iterator<Item = (usize, &'a str)>,
     noscript_css_lines: impl Iterator<Item = (usize, &'a str)>,
-    bad: &mut bool,
+    check: &mut RunningCheck,
 ) {
     let end_theme_pat = format!("/* End theme: {name}");
     for (
@@ -90,12 +92,11 @@ fn compare_themes<'a>(
             break;
         }
         if rustdoc_css_line != noscript_css_line {
-            tidy_error!(
-                bad,
-                "noscript.css:{noscript_css_line_number} and rustdoc.css:{rustdoc_css_line_number} contain copies of {name} theme that are not the same",
-            );
-            eprintln!("- {noscript_css_line}");
-            eprintln!("+ {rustdoc_css_line}");
+            check.error(format!(
+                r#"noscript.css:{noscript_css_line_number} and rustdoc.css:{rustdoc_css_line_number} contain copies of {name} theme that are not the same
+- {noscript_css_line}
++ {rustdoc_css_line}"#,
+            ));
             return;
         }
     }
diff --git a/src/tools/tidy/src/rustdoc_gui_tests.rs b/src/tools/tidy/src/rustdoc_gui_tests.rs
index 3b995f219d2..8ec300c42ce 100644
--- a/src/tools/tidy/src/rustdoc_gui_tests.rs
+++ b/src/tools/tidy/src/rustdoc_gui_tests.rs
@@ -2,18 +2,21 @@
 
 use std::path::Path;
 
-pub fn check(path: &Path, bad: &mut bool) {
+use crate::diagnostics::{CheckId, DiagCtx};
+
+pub fn check(path: &Path, diag_ctx: DiagCtx) {
+    let mut check = diag_ctx.start_check(CheckId::new("rustdoc_gui_tests").path(path));
+
     crate::walk::walk(
         &path.join("rustdoc-gui"),
         |p, is_dir| !is_dir && p.extension().is_none_or(|e| e != "goml"),
         &mut |entry, content| {
             for line in content.lines() {
                 if !line.starts_with("// ") {
-                    tidy_error!(
-                        bad,
+                    check.error(format!(
                         "{}: rustdoc-gui tests must start with a small description",
                         entry.path().display(),
-                    );
+                    ));
                     return;
                 } else if line.starts_with("// ") {
                     let parts = line[2..].trim();
diff --git a/src/tools/tidy/src/rustdoc_json.rs b/src/tools/tidy/src/rustdoc_json.rs
index 722e1ebd0ca..ade774616c7 100644
--- a/src/tools/tidy/src/rustdoc_json.rs
+++ b/src/tools/tidy/src/rustdoc_json.rs
@@ -4,21 +4,26 @@
 use std::path::Path;
 use std::str::FromStr;
 
+use crate::diagnostics::{CheckId, DiagCtx};
+
 const RUSTDOC_JSON_TYPES: &str = "src/rustdoc-json-types";
 
-pub fn check(src_path: &Path, ci_info: &crate::CiInfo, bad: &mut bool) {
-    println!("Checking tidy rustdoc_json...");
+pub fn check(src_path: &Path, ci_info: &crate::CiInfo, diag_ctx: DiagCtx) {
+    let mut check = diag_ctx.start_check(CheckId::new("rustdoc_json").path(src_path));
+
     let Some(base_commit) = &ci_info.base_commit else {
-        eprintln!("No base commit, skipping rustdoc_json check");
+        check.verbose_msg("No base commit, skipping rustdoc_json check");
         return;
     };
 
     // First we check that `src/rustdoc-json-types` was modified.
-    if !crate::files_modified(ci_info, |p| p == RUSTDOC_JSON_TYPES) {
+    if !crate::files_modified(ci_info, |p| p.starts_with(RUSTDOC_JSON_TYPES)) {
         // `rustdoc-json-types` was not modified so nothing more to check here.
-        println!("`rustdoc-json-types` was not modified.");
         return;
     }
+
+    check.message("`rustdoc-json-types` modified, checking format version");
+
     // Then we check that if `FORMAT_VERSION` was updated, the `Latest feature:` was also updated.
     match crate::git_diff(base_commit, src_path.join("rustdoc-json-types")) {
         Some(output) => {
@@ -45,34 +50,29 @@ pub fn check(src_path: &Path, ci_info: &crate::CiInfo, bad: &mut bool) {
                 }
             }
             if format_version_updated != latest_feature_comment_updated {
-                *bad = true;
-                if latest_feature_comment_updated {
-                    eprintln!(
-                        "error in `rustdoc_json` tidy check: `Latest feature` comment was updated \
-                         whereas `FORMAT_VERSION` wasn't in `{RUSTDOC_JSON_TYPES}/lib.rs`"
-                    );
+                let msg = if latest_feature_comment_updated {
+                    format!(
+                        "`Latest feature` comment was updated whereas `FORMAT_VERSION` wasn't in `{RUSTDOC_JSON_TYPES}/lib.rs`"
+                    )
                 } else {
-                    eprintln!(
-                        "error in `rustdoc_json` tidy check: `Latest feature` comment was not \
-                         updated whereas `FORMAT_VERSION` was in `{RUSTDOC_JSON_TYPES}/lib.rs`"
-                    );
-                }
+                    format!(
+                        "`Latest feature` comment was not updated whereas `FORMAT_VERSION` was in `{RUSTDOC_JSON_TYPES}/lib.rs`"
+                    )
+                };
+                check.error(msg);
             }
             match (new_version, old_version) {
                 (Some(new_version), Some(old_version)) if new_version != old_version + 1 => {
-                    *bad = true;
-                    eprintln!(
-                        "error in `rustdoc_json` tidy check: invalid `FORMAT_VERSION` increase in \
-                         `{RUSTDOC_JSON_TYPES}/lib.rs`, should be `{}`, found `{new_version}`",
+                    check.error(format!(
+                        "invalid `FORMAT_VERSION` increase in `{RUSTDOC_JSON_TYPES}/lib.rs`, should be `{}`, found `{new_version}`",
                         old_version + 1,
-                    );
+                    ));
                 }
                 _ => {}
             }
         }
         None => {
-            *bad = true;
-            eprintln!("error: failed to run `git diff` in rustdoc_json check");
+            check.error("failed to run `git diff` in rustdoc_json check");
         }
     }
 }
diff --git a/src/tools/tidy/src/rustdoc_templates.rs b/src/tools/tidy/src/rustdoc_templates.rs
index 597290a6a9a..4e5b9988d53 100644
--- a/src/tools/tidy/src/rustdoc_templates.rs
+++ b/src/tools/tidy/src/rustdoc_templates.rs
@@ -6,12 +6,15 @@ use std::path::Path;
 
 use ignore::DirEntry;
 
+use crate::diagnostics::{CheckId, DiagCtx};
 use crate::walk::walk;
 
 // Array containing `("beginning of tag", "end of tag")`.
 const TAGS: &[(&str, &str)] = &[("{#", "#}"), ("{%", "%}"), ("{{", "}}")];
 
-pub fn check(librustdoc_path: &Path, bad: &mut bool) {
+pub fn check(librustdoc_path: &Path, diag_ctx: DiagCtx) {
+    let mut check = diag_ctx.start_check(CheckId::new("rustdoc_templates").path(librustdoc_path));
+
     walk(
         &librustdoc_path.join("html/templates"),
         |path, is_dir| is_dir || path.extension().is_none_or(|ext| ext != OsStr::new("html")),
@@ -46,12 +49,11 @@ pub fn check(librustdoc_path: &Path, bad: &mut bool) {
                         })
                     {
                         // It seems like ending this line with a jinja tag is not needed after all.
-                        tidy_error!(
-                            bad,
+                        check.error(format!(
                             "`{}` at line {}: unneeded `{{# #}}` tag at the end of the line",
                             path.path().display(),
                             pos + 1,
-                        );
+                        ));
                     }
                     continue;
                 }
@@ -67,12 +69,11 @@ pub fn check(librustdoc_path: &Path, bad: &mut bool) {
                 }) {
                     None => {
                         // No it's not, let's error.
-                        tidy_error!(
-                            bad,
+                        check.error(format!(
                             "`{}` at line {}: missing `{{# #}}` at the end of the line",
                             path.path().display(),
                             pos + 1,
-                        );
+                        ));
                     }
                     Some(end_tag) => {
                         // We skip the tag.
diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs
index fca097c091b..d17278edc84 100644
--- a/src/tools/tidy/src/style.rs
+++ b/src/tools/tidy/src/style.rs
@@ -24,6 +24,7 @@ use std::sync::LazyLock;
 use regex::RegexSetBuilder;
 use rustc_hash::FxHashMap;
 
+use crate::diagnostics::{CheckId, DiagCtx};
 use crate::walk::{filter_dirs, walk};
 
 #[cfg(test)]
@@ -338,7 +339,9 @@ fn is_unexplained_ignore(extension: &str, line: &str) -> bool {
     true
 }
 
-pub fn check(path: &Path, bad: &mut bool) {
+pub fn check(path: &Path, diag_ctx: DiagCtx) {
+    let mut check = diag_ctx.start_check(CheckId::new("style").path(path));
+
     fn skip(path: &Path, is_dir: bool) -> bool {
         if path.file_name().is_some_and(|name| name.to_string_lossy().starts_with(".#")) {
             // vim or emacs temporary file
@@ -391,7 +394,7 @@ pub fn check(path: &Path, bad: &mut bool) {
             });
 
         if contents.is_empty() {
-            tidy_error!(bad, "{}: empty file", file.display());
+            check.error(format!("{}: empty file", file.display()));
         }
 
         let extension = file.extension().unwrap().to_string_lossy();
@@ -467,7 +470,7 @@ pub fn check(path: &Path, bad: &mut bool) {
             }
 
             let mut err = |msg: &str| {
-                tidy_error!(bad, "{}:{}: {}", file.display(), i + 1, msg);
+                check.error(format!("{}:{}: {msg}", file.display(), i + 1));
             };
 
             if trimmed.contains("dbg!")
@@ -611,7 +614,7 @@ pub fn check(path: &Path, bad: &mut bool) {
                     && backtick_count % 2 == 1
                 {
                     let mut err = |msg: &str| {
-                        tidy_error!(bad, "{}:{start_line}: {msg}", file.display());
+                        check.error(format!("{}:{start_line}: {msg}", file.display()));
                     };
                     let block_len = (i + 1) - start_line;
                     if block_len == 1 {
@@ -632,12 +635,12 @@ pub fn check(path: &Path, bad: &mut bool) {
         }
         if leading_new_lines {
             let mut err = |_| {
-                tidy_error!(bad, "{}: leading newline", file.display());
+                check.error(format!("{}: leading newline", file.display()));
             };
             suppressible_tidy_err!(err, skip_leading_newlines, "missing leading newline");
         }
         let mut err = |msg: &str| {
-            tidy_error!(bad, "{}: {}", file.display(), msg);
+            check.error(format!("{}: {}", file.display(), msg));
         };
         match trailing_new_lines {
             0 => suppressible_tidy_err!(err, skip_trailing_newlines, "missing trailing newline"),
@@ -650,38 +653,36 @@ pub fn check(path: &Path, bad: &mut bool) {
         };
         if lines > LINES {
             let mut err = |_| {
-                tidy_error!(
-                    bad,
-                    "{}: too many lines ({}) (add `// \
+                check.error(format!(
+                    "{}: too many lines ({lines}) (add `// \
                      ignore-tidy-filelength` to the file to suppress this error)",
                     file.display(),
-                    lines
-                );
+                ));
             };
             suppressible_tidy_err!(err, skip_file_length, "");
         }
 
         if let Directive::Ignore(false) = skip_cr {
-            tidy_error!(bad, "{}: ignoring CR characters unnecessarily", file.display());
+            check.error(format!("{}: ignoring CR characters unnecessarily", file.display()));
         }
         if let Directive::Ignore(false) = skip_tab {
-            tidy_error!(bad, "{}: ignoring tab characters unnecessarily", file.display());
+            check.error(format!("{}: ignoring tab characters unnecessarily", file.display()));
         }
         if let Directive::Ignore(false) = skip_end_whitespace {
-            tidy_error!(bad, "{}: ignoring trailing whitespace unnecessarily", file.display());
+            check.error(format!("{}: ignoring trailing whitespace unnecessarily", file.display()));
         }
         if let Directive::Ignore(false) = skip_trailing_newlines {
-            tidy_error!(bad, "{}: ignoring trailing newlines unnecessarily", file.display());
+            check.error(format!("{}: ignoring trailing newlines unnecessarily", file.display()));
         }
         if let Directive::Ignore(false) = skip_leading_newlines {
-            tidy_error!(bad, "{}: ignoring leading newlines unnecessarily", file.display());
+            check.error(format!("{}: ignoring leading newlines unnecessarily", file.display()));
         }
         if let Directive::Ignore(false) = skip_copyright {
-            tidy_error!(bad, "{}: ignoring copyright unnecessarily", file.display());
+            check.error(format!("{}: ignoring copyright unnecessarily", file.display()));
         }
         // We deliberately do not warn about these being unnecessary,
         // that would just lead to annoying churn.
         let _unused = skip_line_length;
         let _unused = skip_file_length;
-    })
+    });
 }
diff --git a/src/tools/tidy/src/target_policy.rs b/src/tools/tidy/src/target_policy.rs
index 550932dbfdc..cfcfcaf2435 100644
--- a/src/tools/tidy/src/target_policy.rs
+++ b/src/tools/tidy/src/target_policy.rs
@@ -5,6 +5,7 @@
 use std::collections::HashSet;
 use std::path::Path;
 
+use crate::diagnostics::DiagCtx;
 use crate::walk::{filter_not_rust, walk};
 
 const TARGET_DEFINITIONS_PATH: &str = "compiler/rustc_target/src/spec/targets/";
@@ -23,7 +24,9 @@ const EXCEPTIONS: &[&str] = &[
     "xtensa_esp32s3_espidf",
 ];
 
-pub fn check(root_path: &Path, bad: &mut bool) {
+pub fn check(root_path: &Path, diag_ctx: DiagCtx) {
+    let mut check = diag_ctx.start_check("target_policy");
+
     let mut targets_to_find = HashSet::new();
 
     let definitions_path = root_path.join(TARGET_DEFINITIONS_PATH);
@@ -55,7 +58,7 @@ pub fn check(root_path: &Path, bad: &mut bool) {
 
     for target in targets_to_find {
         if !EXCEPTIONS.contains(&target.as_str()) {
-            tidy_error!(bad, "{ASSEMBLY_LLVM_TEST_PATH}: missing assembly test for {target}")
+            check.error(format!("{ASSEMBLY_LLVM_TEST_PATH}: missing assembly test for {target}"));
         }
     }
 }
diff --git a/src/tools/tidy/src/target_specific_tests.rs b/src/tools/tidy/src/target_specific_tests.rs
index b2d5f259eb2..c1db3874ad5 100644
--- a/src/tools/tidy/src/target_specific_tests.rs
+++ b/src/tools/tidy/src/target_specific_tests.rs
@@ -4,6 +4,7 @@
 use std::collections::BTreeMap;
 use std::path::Path;
 
+use crate::diagnostics::{CheckId, DiagCtx};
 use crate::iter_header::{HeaderLine, iter_header};
 use crate::walk::filter_not_rust;
 
@@ -16,7 +17,9 @@ struct RevisionInfo<'a> {
     llvm_components: Option<Vec<&'a str>>,
 }
 
-pub fn check(tests_path: &Path, bad: &mut bool) {
+pub fn check(tests_path: &Path, diag_ctx: DiagCtx) {
+    let mut check = diag_ctx.start_check(CheckId::new("target-specific-tests").path(tests_path));
+
     crate::walk::walk(tests_path, |path, _is_dir| filter_not_rust(path), &mut |entry, content| {
         if content.contains("// ignore-tidy-target-specific-tests") {
             return;
@@ -44,8 +47,7 @@ pub fn check(tests_path: &Path, bad: &mut bool) {
                 } else if let Some((arch, _)) = v.split_once("-") {
                     info.target_arch.replace(Some(arch));
                 } else {
-                    eprintln!("{file}: seems to have a malformed --target value");
-                    *bad = true;
+                    check.error(format!("{file}: seems to have a malformed --target value"));
                 }
             }
         });
@@ -62,25 +64,22 @@ pub fn check(tests_path: &Path, bad: &mut bool) {
                 (Some(target_arch), None) => {
                     let llvm_component =
                         target_arch.map_or_else(|| "<arch>".to_string(), arch_to_llvm_component);
-                    eprintln!(
+                    check.error(format!(
                         "{file}: revision {rev} should specify `{LLVM_COMPONENTS_HEADER} {llvm_component}` as it has `--target` set"
-                    );
-                    *bad = true;
+                    ));
                 }
                 (None, Some(_)) => {
-                    eprintln!(
+                    check.error(format!(
                         "{file}: revision {rev} should not specify `{LLVM_COMPONENTS_HEADER}` as it doesn't need `--target`"
-                    );
-                    *bad = true;
+                    ));
                 }
                 (Some(target_arch), Some(llvm_components)) => {
                     if let Some(target_arch) = target_arch {
                         let llvm_component = arch_to_llvm_component(target_arch);
                         if !llvm_components.contains(&llvm_component.as_str()) {
-                            eprintln!(
+                            check.error(format!(
                                 "{file}: revision {rev} should specify `{LLVM_COMPONENTS_HEADER} {llvm_component}` as it has `--target` set"
-                            );
-                            *bad = true;
+                            ));
                         }
                     }
                 }
diff --git a/src/tools/tidy/src/tests_placement.rs b/src/tools/tidy/src/tests_placement.rs
index 9d0057df8bc..8ba8cf552bd 100644
--- a/src/tools/tidy/src/tests_placement.rs
+++ b/src/tools/tidy/src/tests_placement.rs
@@ -1,15 +1,18 @@
 use std::path::Path;
 
+use crate::diagnostics::DiagCtx;
+
 const FORBIDDEN_PATH: &str = "src/test";
 const ALLOWED_PATH: &str = "tests";
 
-pub fn check(root_path: impl AsRef<Path>, bad: &mut bool) {
-    if root_path.as_ref().join(FORBIDDEN_PATH).exists() {
-        tidy_error!(
-            bad,
+pub fn check(root_path: &Path, diag_ctx: DiagCtx) {
+    let mut check = diag_ctx.start_check("tests_placement");
+
+    if root_path.join(FORBIDDEN_PATH).exists() {
+        check.error(format!(
             "Tests have been moved, please move them from {} to {}",
-            root_path.as_ref().join(FORBIDDEN_PATH).display(),
-            root_path.as_ref().join(ALLOWED_PATH).display()
-        )
+            root_path.join(FORBIDDEN_PATH).display(),
+            root_path.join(ALLOWED_PATH).display()
+        ));
     }
 }
diff --git a/src/tools/tidy/src/tests_revision_unpaired_stdout_stderr.rs b/src/tools/tidy/src/tests_revision_unpaired_stdout_stderr.rs
index 02412b6f190..1738088a3a0 100644
--- a/src/tools/tidy/src/tests_revision_unpaired_stdout_stderr.rs
+++ b/src/tools/tidy/src/tests_revision_unpaired_stdout_stderr.rs
@@ -4,6 +4,7 @@ use std::collections::{BTreeMap, BTreeSet};
 use std::ffi::OsStr;
 use std::path::Path;
 
+use crate::diagnostics::{CheckId, DiagCtx};
 use crate::iter_header::*;
 use crate::walk::*;
 
@@ -21,7 +22,10 @@ const IGNORES: &[&str] = &[
 const EXTENSIONS: &[&str] = &["stdout", "stderr"];
 const SPECIAL_TEST: &str = "tests/ui/command/need-crate-arg-ignore-tidy.x.rs";
 
-pub fn check(tests_path: impl AsRef<Path>, bad: &mut bool) {
+pub fn check(tests_path: &Path, diag_ctx: DiagCtx) {
+    let mut check = diag_ctx
+        .start_check(CheckId::new("tests_revision_unpaired_stdout_stderr").path(tests_path));
+
     // Recurse over subdirectories under `tests/`
     walk_dir(tests_path.as_ref(), filter, &mut |entry| {
         // We are inspecting a folder. Collect the paths to interesting files `.rs`, `.stderr`,
@@ -122,12 +126,11 @@ pub fn check(tests_path: impl AsRef<Path>, bad: &mut bool) {
                 [] | [_] => return,
                 [_, _] if !expected_revisions.is_empty() => {
                     // Found unrevisioned output files for a revisioned test.
-                    tidy_error!(
-                        bad,
+                    check.error(format!(
                         "found unrevisioned output file `{}` for a revisioned test `{}`",
                         sibling.display(),
                         test_path.display(),
-                    );
+                    ));
                 }
                 [_, _] => return,
                 [_, found_revision, .., extension] => {
@@ -138,13 +141,12 @@ pub fn check(tests_path: impl AsRef<Path>, bad: &mut bool) {
                     {
                         // Found some unexpected revision-esque component that is not a known
                         // compare-mode or expected revision.
-                        tidy_error!(
-                            bad,
+                        check.error(format!(
                             "found output file `{}` for unexpected revision `{}` of test `{}`",
                             sibling.display(),
                             found_revision,
                             test_path.display()
-                        );
+                        ));
                     }
                 }
             }
diff --git a/src/tools/tidy/src/triagebot.rs b/src/tools/tidy/src/triagebot.rs
index 305a0b4d264..41d61dcd141 100644
--- a/src/tools/tidy/src/triagebot.rs
+++ b/src/tools/tidy/src/triagebot.rs
@@ -4,7 +4,10 @@ use std::path::Path;
 
 use toml::Value;
 
-pub fn check(path: &Path, bad: &mut bool) {
+use crate::diagnostics::DiagCtx;
+
+pub fn check(path: &Path, diag_ctx: DiagCtx) {
+    let mut check = diag_ctx.start_check("triagebot");
     let triagebot_path = path.join("triagebot.toml");
 
     // This check is mostly to catch broken path filters *within* `triagebot.toml`, and not enforce
@@ -19,23 +22,25 @@ pub fn check(path: &Path, bad: &mut bool) {
 
     // Check [mentions."*"] sections, i.e. [mentions."compiler/rustc_const_eval/src/"]
     if let Some(Value::Table(mentions)) = config.get("mentions") {
-        for path_str in mentions.keys() {
+        for (entry_key, entry_val) in mentions.iter() {
+            // If the type is set to something other than "filename", then this is not a path.
+            if entry_val.get("type").is_some_and(|t| t.as_str().unwrap_or_default() != "filename") {
+                continue;
+            }
+            let path_str = entry_key;
             // Remove quotes from the path
             let clean_path = path_str.trim_matches('"');
             let full_path = path.join(clean_path);
 
             if !full_path.exists() {
-                tidy_error!(
-                    bad,
-                    "triagebot.toml [mentions.*] contains path '{}' which doesn't exist",
-                    clean_path
-                );
+                check.error(format!(
+                    "triagebot.toml [mentions.*] contains path '{clean_path}' which doesn't exist"
+                ));
             }
         }
     } else {
-        tidy_error!(
-            bad,
-            "triagebot.toml missing [mentions.*] section, this wrong for rust-lang/rust repo."
+        check.error(
+            "triagebot.toml missing [mentions.*] section, this wrong for rust-lang/rust repo.",
         );
     }
 
@@ -50,16 +55,13 @@ pub fn check(path: &Path, bad: &mut bool) {
                 let full_path = path.join(clean_path);
 
                 if !full_path.exists() {
-                    tidy_error!(
-                        bad,
-                        "triagebot.toml [assign.owners] contains path '{}' which doesn't exist",
-                        clean_path
-                    );
+                    check.error(format!(
+                        "triagebot.toml [assign.owners] contains path '{clean_path}' which doesn't exist"
+                    ));
                 }
             }
         } else {
-            tidy_error!(
-                bad,
+            check.error(
                 "triagebot.toml missing [assign.owners] section, this wrong for rust-lang/rust repo."
             );
         }
@@ -81,12 +83,9 @@ pub fn check(path: &Path, bad: &mut bool) {
 
                         // Handle both file and directory paths
                         if !full_path.exists() {
-                            tidy_error!(
-                                bad,
-                                "triagebot.toml [autolabel.{}] contains trigger_files path '{}' which doesn't exist",
-                                label,
-                                file_str
-                            );
+                            check.error(format!(
+                                "triagebot.toml [autolabel.{label}] contains trigger_files path '{file_str}' which doesn't exist",
+                            ));
                         }
                     }
                 }
diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs
index 5bf966b658c..12eca47c171 100644
--- a/src/tools/tidy/src/ui_tests.rs
+++ b/src/tools/tidy/src/ui_tests.rs
@@ -7,13 +7,16 @@ use std::fs;
 use std::io::Write;
 use std::path::{Path, PathBuf};
 
+use crate::diagnostics::{CheckId, DiagCtx, RunningCheck};
+
 const ISSUES_TXT_HEADER: &str = r#"============================================================
     ⚠️⚠️⚠️NOTHING SHOULD EVER BE ADDED TO THIS LIST⚠️⚠️⚠️
 ============================================================
 "#;
 
-pub fn check(root_path: &Path, bless: bool, bad: &mut bool) {
+pub fn check(root_path: &Path, bless: bool, diag_ctx: DiagCtx) {
     let path = &root_path.join("tests");
+    let mut check = diag_ctx.start_check(CheckId::new("ui_tests").path(path));
 
     // the list of files in ui tests that are allowed to start with `issue-XXXX`
     // BTreeSet because we would like a stable ordering so --bless works
@@ -33,16 +36,15 @@ pub fn check(root_path: &Path, bless: bool, bad: &mut bool) {
         .collect();
 
     if !is_sorted && !bless {
-        tidy_error!(
-            bad,
+        check.error(
             "`src/tools/tidy/src/issues.txt` is not in order, mostly because you modified it manually,
             please only update it with command `x test tidy --bless`"
         );
     }
 
-    deny_new_top_level_ui_tests(bad, &path.join("ui"));
+    deny_new_top_level_ui_tests(&mut check, &path.join("ui"));
 
-    let remaining_issue_names = recursively_check_ui_tests(bad, path, &allowed_issue_names);
+    let remaining_issue_names = recursively_check_ui_tests(&mut check, path, &allowed_issue_names);
 
     // if there are any file names remaining, they were moved on the fs.
     // our data must remain up to date, so it must be removed from issues.txt
@@ -64,16 +66,15 @@ pub fn check(root_path: &Path, bless: bool, bad: &mut bool) {
         for file_name in remaining_issue_names {
             let mut p = PathBuf::from(path);
             p.push(file_name);
-            tidy_error!(
-                bad,
+            check.error(format!(
                 "file `{}` no longer exists and should be removed from the exclusions in `src/tools/tidy/src/issues.txt`",
                 p.display()
-            );
+            ));
         }
     }
 }
 
-fn deny_new_top_level_ui_tests(bad: &mut bool, tests_path: &Path) {
+fn deny_new_top_level_ui_tests(check: &mut RunningCheck, tests_path: &Path) {
     // See <https://github.com/rust-lang/compiler-team/issues/902> where we propose banning adding
     // new ui tests *directly* under `tests/ui/`. For more context, see:
     //
@@ -93,16 +94,15 @@ fn deny_new_top_level_ui_tests(bad: &mut bool, tests_path: &Path) {
         })
         .filter(|e| !e.file_type().is_dir());
     for entry in top_level_ui_tests {
-        tidy_error!(
-            bad,
+        check.error(format!(
             "ui tests should be added under meaningful subdirectories: `{}`",
             entry.path().display()
-        )
+        ));
     }
 }
 
 fn recursively_check_ui_tests<'issues>(
-    bad: &mut bool,
+    check: &mut RunningCheck,
     path: &Path,
     allowed_issue_names: &'issues BTreeSet<&'issues str>,
 ) -> BTreeSet<&'issues str> {
@@ -113,19 +113,19 @@ fn recursively_check_ui_tests<'issues>(
     crate::walk::walk_no_read(&paths, |_, _| false, &mut |entry| {
         let file_path = entry.path();
         if let Some(ext) = file_path.extension().and_then(OsStr::to_str) {
-            check_unexpected_extension(bad, file_path, ext);
+            check_unexpected_extension(check, file_path, ext);
 
             // NB: We do not use file_stem() as some file names have multiple `.`s and we
             // must strip all of them.
             let testname =
                 file_path.file_name().unwrap().to_str().unwrap().split_once('.').unwrap().0;
             if ext == "stderr" || ext == "stdout" || ext == "fixed" {
-                check_stray_output_snapshot(bad, file_path, testname);
-                check_empty_output_snapshot(bad, file_path);
+                check_stray_output_snapshot(check, file_path, testname);
+                check_empty_output_snapshot(check, file_path);
             }
 
             deny_new_nondescriptive_test_names(
-                bad,
+                check,
                 path,
                 &mut remaining_issue_names,
                 file_path,
@@ -137,7 +137,7 @@ fn recursively_check_ui_tests<'issues>(
     remaining_issue_names
 }
 
-fn check_unexpected_extension(bad: &mut bool, file_path: &Path, ext: &str) {
+fn check_unexpected_extension(check: &mut RunningCheck, file_path: &Path, ext: &str) {
     const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[
         "rs",     // test source files
         "stderr", // expected stderr file, corresponds to a rs file
@@ -178,11 +178,11 @@ fn check_unexpected_extension(bad: &mut bool, file_path: &Path, ext: &str) {
     if !(EXPECTED_TEST_FILE_EXTENSIONS.contains(&ext)
         || EXTENSION_EXCEPTION_PATHS.iter().any(|path| file_path.ends_with(path)))
     {
-        tidy_error!(bad, "file {} has unexpected extension {}", file_path.display(), ext);
+        check.error(format!("file {} has unexpected extension {}", file_path.display(), ext));
     }
 }
 
-fn check_stray_output_snapshot(bad: &mut bool, file_path: &Path, testname: &str) {
+fn check_stray_output_snapshot(check: &mut RunningCheck, file_path: &Path, testname: &str) {
     // Test output filenames have one of the formats:
     // ```
     // $testname.stderr
@@ -197,20 +197,20 @@ fn check_stray_output_snapshot(bad: &mut bool, file_path: &Path, testname: &str)
     if !file_path.with_file_name(testname).with_extension("rs").exists()
         && !testname.contains("ignore-tidy")
     {
-        tidy_error!(bad, "Stray file with UI testing output: {:?}", file_path);
+        check.error(format!("Stray file with UI testing output: {:?}", file_path));
     }
 }
 
-fn check_empty_output_snapshot(bad: &mut bool, file_path: &Path) {
+fn check_empty_output_snapshot(check: &mut RunningCheck, file_path: &Path) {
     if let Ok(metadata) = fs::metadata(file_path)
         && metadata.len() == 0
     {
-        tidy_error!(bad, "Empty file with UI testing output: {:?}", file_path);
+        check.error(format!("Empty file with UI testing output: {:?}", file_path));
     }
 }
 
 fn deny_new_nondescriptive_test_names(
-    bad: &mut bool,
+    check: &mut RunningCheck,
     path: &Path,
     remaining_issue_names: &mut BTreeSet<&str>,
     file_path: &Path,
@@ -231,11 +231,10 @@ fn deny_new_nondescriptive_test_names(
         if !remaining_issue_names.remove(stripped_path.as_str())
             && !stripped_path.starts_with("ui/issues/")
         {
-            tidy_error!(
-                bad,
+            check.error(format!(
                 "file `tests/{stripped_path}` must begin with a descriptive name, consider `{{reason}}-issue-{issue_n}.rs`",
                 issue_n = &test_name[1],
-            );
+            ));
         }
     }
 }
diff --git a/src/tools/tidy/src/unit_tests.rs b/src/tools/tidy/src/unit_tests.rs
index 3d14a467319..cab445ac63a 100644
--- a/src/tools/tidy/src/unit_tests.rs
+++ b/src/tools/tidy/src/unit_tests.rs
@@ -11,9 +11,12 @@
 
 use std::path::Path;
 
+use crate::diagnostics::{CheckId, DiagCtx};
 use crate::walk::{filter_dirs, walk};
 
-pub fn check(root_path: &Path, stdlib: bool, bad: &mut bool) {
+pub fn check(root_path: &Path, stdlib: bool, diag_ctx: DiagCtx) {
+    let mut check = diag_ctx.start_check(CheckId::new("unit_tests").path(root_path));
+
     let skip = move |path: &Path, is_dir| {
         let file_name = path.file_name().unwrap_or_default();
 
@@ -61,6 +64,7 @@ pub fn check(root_path: &Path, stdlib: bool, bad: &mut bool) {
                 || path.ends_with("library/alloc/src/collections/linked_list/tests.rs")
                 || path.ends_with("library/alloc/src/collections/vec_deque/tests.rs")
                 || path.ends_with("library/alloc/src/raw_vec/tests.rs")
+                || path.ends_with("library/alloc/src/wtf8/tests.rs")
         }
     };
 
@@ -91,14 +95,11 @@ pub fn check(root_path: &Path, stdlib: bool, bad: &mut bool) {
                         .to_owned()
                 };
                 let name = if is_test() { "test" } else { "bench" };
-                tidy_error!(
-                    bad,
-                    "`{}:{}` contains `#[{}]`; {}",
+                check.error(format!(
+                    "`{}:{}` contains `#[{name}]`; {explanation}",
                     path.display(),
                     i + 1,
-                    name,
-                    explanation,
-                );
+                ));
                 return;
             }
         }
diff --git a/src/tools/tidy/src/unknown_revision.rs b/src/tools/tidy/src/unknown_revision.rs
index 0ba05c80a79..776d45e25de 100644
--- a/src/tools/tidy/src/unknown_revision.rs
+++ b/src/tools/tidy/src/unknown_revision.rs
@@ -12,12 +12,14 @@ use std::sync::OnceLock;
 use ignore::DirEntry;
 use regex::Regex;
 
+use crate::diagnostics::{CheckId, DiagCtx, RunningCheck};
 use crate::iter_header::{HeaderLine, iter_header};
 use crate::walk::{filter_dirs, filter_not_rust, walk};
 
-pub fn check(tests_path: impl AsRef<Path>, bad: &mut bool) {
+pub fn check(tests_path: &Path, diag_ctx: DiagCtx) {
+    let mut check = diag_ctx.start_check(CheckId::new("unknown_revision").path(tests_path));
     walk(
-        tests_path.as_ref(),
+        tests_path,
         |path, is_dir| {
             filter_dirs(path) || filter_not_rust(path) || {
                 // Auxiliary source files for incremental tests can refer to revisions
@@ -25,11 +27,11 @@ pub fn check(tests_path: impl AsRef<Path>, bad: &mut bool) {
                 is_dir && path.file_name().is_some_and(|name| name == "auxiliary")
             }
         },
-        &mut |entry, contents| visit_test_file(entry, contents, bad),
+        &mut |entry, contents| visit_test_file(entry, contents, &mut check),
     );
 }
 
-fn visit_test_file(entry: &DirEntry, contents: &str, bad: &mut bool) {
+fn visit_test_file(entry: &DirEntry, contents: &str, check: &mut RunningCheck) {
     let mut revisions = HashSet::new();
     let mut unused_revision_names = HashSet::new();
 
@@ -68,10 +70,9 @@ fn visit_test_file(entry: &DirEntry, contents: &str, bad: &mut bool) {
 
     // Fail if any revision names appear in both places, since that's probably a mistake.
     for rev in revisions.intersection(&unused_revision_names).copied().collect::<BTreeSet<_>>() {
-        tidy_error!(
-            bad,
+        check.error(format!(
             "revision name [{rev}] appears in both `revisions` and `unused-revision-names` in {path}"
-        );
+        ));
     }
 
     // Compute the set of revisions that were mentioned but not declared,
@@ -84,7 +85,7 @@ fn visit_test_file(entry: &DirEntry, contents: &str, bad: &mut bool) {
     bad_revisions.sort();
 
     for (line_number, rev) in bad_revisions {
-        tidy_error!(bad, "unknown revision [{rev}] at {path}:{line_number}");
+        check.error(format!("unknown revision [{rev}] at {path}:{line_number}"));
     }
 }
 
diff --git a/src/tools/tidy/src/unstable_book.rs b/src/tools/tidy/src/unstable_book.rs
index 0ed954d48de..bab294abee0 100644
--- a/src/tools/tidy/src/unstable_book.rs
+++ b/src/tools/tidy/src/unstable_book.rs
@@ -2,6 +2,7 @@ use std::collections::BTreeSet;
 use std::fs;
 use std::path::{Path, PathBuf};
 
+use crate::diagnostics::{DiagCtx, RunningCheck};
 use crate::features::{CollectedFeatures, Features, Status};
 
 pub const PATH_STR: &str = "doc/unstable-book";
@@ -75,19 +76,18 @@ fn collect_unstable_book_lib_features_section_file_names(base_src_path: &Path) -
 }
 
 /// Would switching underscores for dashes work?
-fn maybe_suggest_dashes(names: &BTreeSet<String>, feature_name: &str, bad: &mut bool) {
+fn maybe_suggest_dashes(names: &BTreeSet<String>, feature_name: &str, check: &mut RunningCheck) {
     let with_dashes = feature_name.replace('_', "-");
     if names.contains(&with_dashes) {
-        tidy_error!(
-            bad,
-            "the file `{}.md` contains underscores; use dashes instead: `{}.md`",
-            feature_name,
-            with_dashes,
-        );
+        check.error(format!(
+            "the file `{feature_name}.md` contains underscores; use dashes instead: `{with_dashes}.md`",
+        ));
     }
 }
 
-pub fn check(path: &Path, features: CollectedFeatures, bad: &mut bool) {
+pub fn check(path: &Path, features: CollectedFeatures, diag_ctx: DiagCtx) {
+    let mut check = diag_ctx.start_check("unstable_book");
+
     let lang_features = features.lang;
     let lib_features = features
         .lib
@@ -108,26 +108,22 @@ pub fn check(path: &Path, features: CollectedFeatures, bad: &mut bool) {
     // Check for Unstable Book sections that don't have a corresponding unstable feature
     for feature_name in &unstable_book_lib_features_section_file_names - &unstable_lib_feature_names
     {
-        tidy_error!(
-            bad,
-            "The Unstable Book has a 'library feature' section '{}' which doesn't \
-                         correspond to an unstable library feature",
-            feature_name
-        );
-        maybe_suggest_dashes(&unstable_lib_feature_names, &feature_name, bad);
+        check.error(format!(
+            "The Unstable Book has a 'library feature' section '{feature_name}' which doesn't \
+                         correspond to an unstable library feature"
+        ));
+        maybe_suggest_dashes(&unstable_lib_feature_names, &feature_name, &mut check);
     }
 
     // Check for Unstable Book sections that don't have a corresponding unstable feature.
     for feature_name in
         &unstable_book_lang_features_section_file_names - &unstable_lang_feature_names
     {
-        tidy_error!(
-            bad,
-            "The Unstable Book has a 'language feature' section '{}' which doesn't \
-                     correspond to an unstable language feature",
-            feature_name
-        );
-        maybe_suggest_dashes(&unstable_lang_feature_names, &feature_name, bad);
+        check.error(format!(
+            "The Unstable Book has a 'language feature' section '{feature_name}' which doesn't \
+                     correspond to an unstable language feature"
+        ));
+        maybe_suggest_dashes(&unstable_lang_feature_names, &feature_name, &mut check);
     }
 
     // List unstable features that don't have Unstable Book sections.
diff --git a/src/tools/tidy/src/x_version.rs b/src/tools/tidy/src/x_version.rs
index 9f7f43c4000..b3e322d9403 100644
--- a/src/tools/tidy/src/x_version.rs
+++ b/src/tools/tidy/src/x_version.rs
@@ -3,12 +3,18 @@ use std::process::{Command, Stdio};
 
 use semver::Version;
 
-pub fn check(root: &Path, cargo: &Path, bad: &mut bool) {
+use crate::diagnostics::{CheckId, DiagCtx};
+
+pub fn check(root: &Path, cargo: &Path, diag_ctx: DiagCtx) {
+    let mut check = diag_ctx.start_check(CheckId::new("x_version").path(root));
     let cargo_list = Command::new(cargo).args(["install", "--list"]).stdout(Stdio::piped()).spawn();
 
     let child = match cargo_list {
         Ok(child) => child,
-        Err(e) => return tidy_error!(bad, "failed to run `cargo`: {}", e),
+        Err(e) => {
+            check.error(format!("failed to run `cargo`: {e}"));
+            return;
+        }
     };
 
     let cargo_list = child.wait_with_output().unwrap();
@@ -47,13 +53,10 @@ pub fn check(root: &Path, cargo: &Path, bad: &mut bool) {
                 )
             }
         } else {
-            tidy_error!(
-                bad,
-                "Unable to parse the latest version of `x` at `src/tools/x/Cargo.toml`"
-            )
+            check.error("Unable to parse the latest version of `x` at `src/tools/x/Cargo.toml`")
         }
     } else {
-        tidy_error!(bad, "failed to check version of `x`: {}", cargo_list.status)
+        check.error(format!("failed to check version of `x`: {}", cargo_list.status))
     }
 }
 
diff --git a/src/tools/unicode-table-generator/src/cascading_map.rs b/src/tools/unicode-table-generator/src/cascading_map.rs
index 78a7bba3208..56e6401908d 100644
--- a/src/tools/unicode-table-generator/src/cascading_map.rs
+++ b/src/tools/unicode-table-generator/src/cascading_map.rs
@@ -64,6 +64,7 @@ impl RawEmitter {
 
         writeln!(&mut self.file, "#[inline]").unwrap();
         writeln!(&mut self.file, "pub const fn lookup(c: char) -> bool {{").unwrap();
+        writeln!(&mut self.file, "    debug_assert!(!c.is_ascii());").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/case_mapping.rs b/src/tools/unicode-table-generator/src/case_mapping.rs
index 9c6454492e7..49aef3ec33e 100644
--- a/src/tools/unicode-table-generator/src/case_mapping.rs
+++ b/src/tools/unicode-table-generator/src/case_mapping.rs
@@ -6,24 +6,26 @@ use crate::{UnicodeData, fmt_list};
 
 const INDEX_MASK: u32 = 1 << 22;
 
-pub(crate) fn generate_case_mapping(data: &UnicodeData) -> String {
+pub(crate) fn generate_case_mapping(data: &UnicodeData) -> (String, [usize; 2]) {
     let mut file = String::new();
 
     write!(file, "const INDEX_MASK: u32 = 0x{INDEX_MASK:x};").unwrap();
     file.push_str("\n\n");
     file.push_str(HEADER.trim_start());
     file.push('\n');
-    file.push_str(&generate_tables("LOWER", &data.to_lower));
+    let (lower_tables, lower_size) = generate_tables("LOWER", &data.to_lower);
+    file.push_str(&lower_tables);
     file.push_str("\n\n");
-    file.push_str(&generate_tables("UPPER", &data.to_upper));
-    file
+    let (upper_tables, upper_size) = generate_tables("UPPER", &data.to_upper);
+    file.push_str(&upper_tables);
+    (file, [lower_size, upper_size])
 }
 
-fn generate_tables(case: &str, data: &BTreeMap<u32, (u32, u32, u32)>) -> String {
+fn generate_tables(case: &str, data: &BTreeMap<u32, [u32; 3]>) -> (String, usize) {
     let mut mappings = Vec::with_capacity(data.len());
     let mut multis = Vec::new();
 
-    for (&key, &(a, b, c)) in data.iter() {
+    for (&key, &[a, b, c]) in data.iter() {
         let key = char::from_u32(key).unwrap();
 
         if key.is_ascii() {
@@ -46,16 +48,31 @@ fn generate_tables(case: &str, data: &BTreeMap<u32, (u32, u32, u32)>) -> String
     }
 
     let mut tables = String::new();
-
-    write!(tables, "static {}CASE_TABLE: &[(char, u32)] = &[{}];", case, fmt_list(mappings))
-        .unwrap();
+    let mut size = 0;
+
+    size += size_of_val(mappings.as_slice());
+    write!(
+        tables,
+        "static {}CASE_TABLE: &[(char, u32); {}] = &[{}];",
+        case,
+        mappings.len(),
+        fmt_list(mappings),
+    )
+    .unwrap();
 
     tables.push_str("\n\n");
 
-    write!(tables, "static {}CASE_TABLE_MULTI: &[[char; 3]] = &[{}];", case, fmt_list(multis))
-        .unwrap();
-
-    tables
+    size += size_of_val(multis.as_slice());
+    write!(
+        tables,
+        "static {}CASE_TABLE_MULTI: &[[char; 3]; {}] = &[{}];",
+        case,
+        multis.len(),
+        fmt_list(multis),
+    )
+    .unwrap();
+
+    (tables, size)
 }
 
 struct CharEscape(char);
diff --git a/src/tools/unicode-table-generator/src/main.rs b/src/tools/unicode-table-generator/src/main.rs
index 6cdb82a87bd..ded9205ffc4 100644
--- a/src/tools/unicode-table-generator/src/main.rs
+++ b/src/tools/unicode-table-generator/src/main.rs
@@ -72,6 +72,8 @@
 //! or not.
 
 use std::collections::{BTreeMap, HashMap};
+use std::fmt;
+use std::fmt::Write;
 use std::ops::Range;
 
 use ucd_parse::Codepoints;
@@ -92,17 +94,16 @@ static PROPERTIES: &[&str] = &[
     "Case_Ignorable",
     "Grapheme_Extend",
     "White_Space",
-    "Cc",
     "N",
 ];
 
 struct UnicodeData {
     ranges: Vec<(&'static str, Vec<Range<u32>>)>,
-    to_upper: BTreeMap<u32, (u32, u32, u32)>,
-    to_lower: BTreeMap<u32, (u32, u32, u32)>,
+    to_upper: BTreeMap<u32, [u32; 3]>,
+    to_lower: BTreeMap<u32, [u32; 3]>,
 }
 
-fn to_mapping(origin: u32, codepoints: Vec<ucd_parse::Codepoint>) -> Option<(u32, u32, u32)> {
+fn to_mapping(origin: u32, codepoints: Vec<ucd_parse::Codepoint>) -> Option<[u32; 3]> {
     let mut a = None;
     let mut b = None;
     let mut c = None;
@@ -123,7 +124,7 @@ fn to_mapping(origin: u32, codepoints: Vec<ucd_parse::Codepoint>) -> Option<(u32
         }
     }
 
-    Some((a.unwrap(), b.unwrap_or(0), c.unwrap_or(0)))
+    Some([a.unwrap(), b.unwrap_or(0), c.unwrap_or(0)])
 }
 
 static UNICODE_DIRECTORY: &str = "unicode-downloads";
@@ -163,12 +164,12 @@ fn load_data() -> UnicodeData {
         if let Some(mapped) = row.simple_lowercase_mapping
             && mapped != row.codepoint
         {
-            to_lower.insert(row.codepoint.value(), (mapped.value(), 0, 0));
+            to_lower.insert(row.codepoint.value(), [mapped.value(), 0, 0]);
         }
         if let Some(mapped) = row.simple_uppercase_mapping
             && mapped != row.codepoint
         {
-            to_upper.insert(row.codepoint.value(), (mapped.value(), 0, 0));
+            to_upper.insert(row.codepoint.value(), [mapped.value(), 0, 0]);
         }
     }
 
@@ -187,33 +188,20 @@ fn load_data() -> UnicodeData {
         }
     }
 
-    let mut properties: HashMap<&'static str, Vec<Range<u32>>> = properties
+    let mut properties: Vec<(&'static str, Vec<Range<u32>>)> = properties
         .into_iter()
-        .map(|(k, v)| {
-            (
-                k,
-                v.into_iter()
-                    .flat_map(|codepoints| match codepoints {
-                        Codepoints::Single(c) => c
-                            .scalar()
-                            .map(|ch| ch as u32..ch as u32 + 1)
-                            .into_iter()
-                            .collect::<Vec<_>>(),
-                        Codepoints::Range(c) => c
-                            .into_iter()
-                            .flat_map(|c| c.scalar().map(|ch| ch as u32..ch as u32 + 1))
-                            .collect::<Vec<_>>(),
-                    })
-                    .collect::<Vec<Range<u32>>>(),
-            )
+        .map(|(prop, codepoints)| {
+            let codepoints = codepoints
+                .into_iter()
+                .flatten()
+                .flat_map(|cp| cp.scalar())
+                .filter(|c| !c.is_ascii())
+                .map(u32::from)
+                .collect::<Vec<_>>();
+            (prop, ranges_from_set(&codepoints))
         })
         .collect();
 
-    for ranges in properties.values_mut() {
-        merge_ranges(ranges);
-    }
-
-    let mut properties = properties.into_iter().collect::<Vec<_>>();
     properties.sort_by_key(|p| p.0);
     UnicodeData { ranges: properties, to_lower, to_upper }
 }
@@ -236,9 +224,14 @@ fn main() {
     let ranges_by_property = &unicode_data.ranges;
 
     if let Some(path) = test_path {
-        std::fs::write(&path, generate_tests(&write_location, ranges_by_property)).unwrap();
+        std::fs::write(&path, generate_tests(&unicode_data).unwrap()).unwrap();
     }
 
+    let mut table_file = String::new();
+    table_file.push_str(
+        "//! This file is generated by `./x run src/tools/unicode-table-generator`; do not edit manually!\n",
+    );
+
     let mut total_bytes = 0;
     let mut modules = Vec::new();
     for (property, ranges) in ranges_by_property {
@@ -252,8 +245,8 @@ fn main() {
         }
 
         modules.push((property.to_lowercase().to_string(), emitter.file));
-        println!(
-            "{:15}: {} bytes, {} codepoints in {} ranges ({} - {}) using {}",
+        table_file.push_str(&format!(
+            "// {:16}: {:5} bytes, {:6} codepoints in {:3} ranges (U+{:06X} - U+{:06X}) using {}\n",
             property,
             emitter.bytes_used,
             datapoints,
@@ -261,15 +254,15 @@ fn main() {
             ranges.first().unwrap().start,
             ranges.last().unwrap().end,
             emitter.desc,
-        );
+        ));
         total_bytes += emitter.bytes_used;
     }
-
-    let mut table_file = String::new();
-
-    table_file.push_str(
-        "///! This file is generated by `./x run src/tools/unicode-table-generator`; do not edit manually!\n",
-    );
+    let (conversions, sizes) = case_mapping::generate_case_mapping(&unicode_data);
+    for (name, size) in ["to_lower", "to_upper"].iter().zip(sizes) {
+        table_file.push_str(&format!("// {:16}: {:5} bytes\n", name, size));
+        total_bytes += size;
+    }
+    table_file.push_str(&format!("// {:16}: {:5} bytes\n", "Total", total_bytes));
 
     // Include the range search function
     table_file.push('\n');
@@ -280,7 +273,7 @@ fn main() {
 
     table_file.push('\n');
 
-    modules.push((String::from("conversions"), case_mapping::generate_case_mapping(&unicode_data)));
+    modules.push((String::from("conversions"), conversions));
 
     for (name, contents) in modules {
         table_file.push_str("#[rustfmt::skip]\n");
@@ -296,8 +289,6 @@ fn main() {
     }
 
     std::fs::write(&write_location, format!("{}\n", table_file.trim_end())).unwrap();
-
-    println!("Total table sizes: {total_bytes} bytes");
 }
 
 fn version() -> String {
@@ -337,110 +328,96 @@ fn fmt_list<V: std::fmt::Debug>(values: impl IntoIterator<Item = V>) -> String {
     out
 }
 
-fn generate_tests(data_path: &str, ranges: &[(&str, Vec<Range<u32>>)]) -> String {
+fn generate_tests(data: &UnicodeData) -> Result<String, fmt::Error> {
     let mut s = String::new();
-    s.push_str("#![allow(incomplete_features, unused)]\n");
-    s.push_str("#![feature(const_generics)]\n\n");
-    s.push_str("\n#[allow(unused)]\nuse std::hint;\n");
-    s.push_str(&format!("#[path = \"{data_path}\"]\n"));
-    s.push_str("mod unicode_data;\n\n");
-
-    s.push_str("\nfn main() {\n");
-
-    for (property, ranges) in ranges {
-        s.push_str(&format!(r#"    println!("Testing {property}");"#));
-        s.push('\n');
-        s.push_str(&format!("    {}_true();\n", property.to_lowercase()));
-        s.push_str(&format!("    {}_false();\n", property.to_lowercase()));
-        let mut is_true = Vec::new();
-        let mut is_false = Vec::new();
-        for ch_num in 0..(std::char::MAX as u32) {
-            if std::char::from_u32(ch_num).is_none() {
-                continue;
-            }
-            if ranges.iter().any(|r| r.contains(&ch_num)) {
-                is_true.push(ch_num);
-            } else {
-                is_false.push(ch_num);
-            }
-        }
-
-        s.push_str(&format!("    fn {}_true() {{\n", property.to_lowercase()));
-        generate_asserts(&mut s, property, &is_true, true);
-        s.push_str("    }\n\n");
-        s.push_str(&format!("    fn {}_false() {{\n", property.to_lowercase()));
-        generate_asserts(&mut s, property, &is_false, false);
-        s.push_str("    }\n\n");
+    writeln!(s, "#![feature(core_intrinsics)]")?;
+    writeln!(s, "#![allow(internal_features, dead_code)]")?;
+    writeln!(s, "// ignore-tidy-filelength")?;
+    writeln!(s, "use std::intrinsics;")?;
+    writeln!(s, "mod unicode_data;")?;
+    writeln!(s, "fn main() {{")?;
+    for (property, ranges) in &data.ranges {
+        let prop = property.to_lowercase();
+        writeln!(s, r#"    println!("Testing {prop}");"#)?;
+        writeln!(s, "    {prop}_true();")?;
+        writeln!(s, "    {prop}_false();")?;
+        let (is_true, is_false): (Vec<_>, Vec<_>) = (char::MIN..=char::MAX)
+            .filter(|c| !c.is_ascii())
+            .map(u32::from)
+            .partition(|c| ranges.iter().any(|r| r.contains(c)));
+
+        writeln!(s, "    fn {prop}_true() {{")?;
+        generate_asserts(&mut s, &prop, &is_true, true)?;
+        writeln!(s, "    }}")?;
+
+        writeln!(s, "    fn {prop}_false() {{")?;
+        generate_asserts(&mut s, &prop, &is_false, false)?;
+        writeln!(s, "    }}")?;
     }
 
-    s.push('}');
-    s
-}
-
-fn generate_asserts(s: &mut String, property: &str, points: &[u32], truthy: bool) {
-    for range in ranges_from_set(points) {
-        if range.end == range.start + 1 {
-            s.push_str(&format!(
-                "        assert!({}unicode_data::{}::lookup({:?}), \"{}\");\n",
-                if truthy { "" } else { "!" },
-                property.to_lowercase(),
-                std::char::from_u32(range.start).unwrap(),
-                range.start,
-            ));
-        } else {
-            s.push_str(&format!("        for chn in {range:?}u32 {{\n"));
-            s.push_str(&format!(
-                "            assert!({}unicode_data::{}::lookup(std::char::from_u32(chn).unwrap()), \"{{:?}}\", chn);\n",
-                if truthy { "" } else { "!" },
-                property.to_lowercase(),
-            ));
-            s.push_str("        }\n");
+    for (name, conversion) in ["to_lower", "to_upper"].iter().zip([&data.to_lower, &data.to_upper])
+    {
+        writeln!(s, r#"    println!("Testing {name}");"#)?;
+        for (c, mapping) in conversion {
+            let c = char::from_u32(*c).unwrap();
+            let mapping = mapping.map(|c| char::from_u32(c).unwrap());
+            writeln!(
+                s,
+                r#"    assert_eq!(unicode_data::conversions::{name}({c:?}), {mapping:?});"#
+            )?;
+        }
+        let unmapped: Vec<_> = (char::MIN..=char::MAX)
+            .filter(|c| !c.is_ascii())
+            .map(u32::from)
+            .filter(|c| !conversion.contains_key(c))
+            .collect();
+        let unmapped_ranges = ranges_from_set(&unmapped);
+        for range in unmapped_ranges {
+            let start = char::from_u32(range.start).unwrap();
+            let end = char::from_u32(range.end - 1).unwrap();
+            writeln!(s, "    for c in {start:?}..={end:?} {{")?;
+            writeln!(
+                s,
+                r#"        assert_eq!(unicode_data::conversions::{name}(c), [c, '\0', '\0']);"#
+            )?;
+
+            writeln!(s, "    }}")?;
         }
     }
-}
 
-fn ranges_from_set(set: &[u32]) -> Vec<Range<u32>> {
-    let mut ranges = set.iter().map(|e| (*e)..(*e + 1)).collect::<Vec<Range<u32>>>();
-    merge_ranges(&mut ranges);
-    ranges
+    writeln!(s, "}}")?;
+    Ok(s)
 }
 
-fn merge_ranges(ranges: &mut Vec<Range<u32>>) {
-    loop {
-        let mut new_ranges = Vec::new();
-        let mut idx_iter = 0..(ranges.len() - 1);
-        let mut should_insert_last = true;
-        while let Some(idx) = idx_iter.next() {
-            let cur = ranges[idx].clone();
-            let next = ranges[idx + 1].clone();
-            if cur.end == next.start {
-                if idx_iter.next().is_none() {
-                    // We're merging the last element
-                    should_insert_last = false;
-                }
-                new_ranges.push(cur.start..next.end);
-            } else {
-                // We're *not* merging the last element
-                should_insert_last = true;
-                new_ranges.push(cur);
+fn generate_asserts(
+    s: &mut String,
+    prop: &str,
+    points: &[u32],
+    truthy: bool,
+) -> Result<(), fmt::Error> {
+    let truthy = if truthy { "" } else { "!" };
+    for range in ranges_from_set(points) {
+        let start = char::from_u32(range.start).unwrap();
+        let end = char::from_u32(range.end - 1).unwrap();
+        match range.len() {
+            1 => writeln!(s, "        assert!({truthy}unicode_data::{prop}::lookup({start:?}));")?,
+            _ => {
+                writeln!(s, "        for c in {start:?}..={end:?} {{")?;
+                writeln!(s, "            assert!({truthy}unicode_data::{prop}::lookup(c));")?;
+                writeln!(s, "        }}")?;
             }
         }
-        if should_insert_last {
-            new_ranges.push(ranges.last().unwrap().clone());
-        }
-        if new_ranges.len() == ranges.len() {
-            *ranges = new_ranges;
-            break;
-        } else {
-            *ranges = new_ranges;
-        }
     }
+    Ok(())
+}
 
-    let mut last_end = None;
-    for range in ranges {
-        if let Some(last) = last_end {
-            assert!(range.start > last, "{range:?}");
-        }
-        last_end = Some(range.end);
-    }
+/// Group the elements of `set` into contigous ranges
+fn ranges_from_set(set: &[u32]) -> Vec<Range<u32>> {
+    set.chunk_by(|a, b| a + 1 == *b)
+        .map(|chunk| {
+            let start = *chunk.first().unwrap();
+            let end = *chunk.last().unwrap();
+            start..(end + 1)
+        })
+        .collect()
 }
diff --git a/src/tools/unicode-table-generator/src/raw_emitter.rs b/src/tools/unicode-table-generator/src/raw_emitter.rs
index e9e0efc4594..297965615c1 100644
--- a/src/tools/unicode-table-generator/src/raw_emitter.rs
+++ b/src/tools/unicode-table-generator/src/raw_emitter.rs
@@ -98,6 +98,7 @@ impl RawEmitter {
         self.blank_line();
 
         writeln!(&mut self.file, "pub const fn lookup(c: char) -> bool {{").unwrap();
+        writeln!(&mut self.file, "    debug_assert!(!c.is_ascii());").unwrap();
         if first_code_point > 0x7f {
             writeln!(&mut self.file, "    (c as u32) >= {first_code_point:#04x} &&").unwrap();
         }
@@ -341,7 +342,7 @@ impl Canonicalized {
         for &w in unique_words {
             unique_mapping.entry(w).or_insert_with(|| {
                 canonical_words.push(w);
-                UniqueMapping::Canonical(canonical_words.len())
+                UniqueMapping::Canonical(canonical_words.len() - 1)
             });
         }
         assert_eq!(canonicalized_words.len() + canonical_words.len(), unique_words.len());
diff --git a/src/tools/unicode-table-generator/src/skiplist.rs b/src/tools/unicode-table-generator/src/skiplist.rs
index 34c9802e122..660a8f342f7 100644
--- a/src/tools/unicode-table-generator/src/skiplist.rs
+++ b/src/tools/unicode-table-generator/src/skiplist.rs
@@ -99,6 +99,7 @@ impl RawEmitter {
         if first_code_point > 0x7f {
             writeln!(&mut self.file, "#[inline]").unwrap();
             writeln!(&mut self.file, "pub fn lookup(c: char) -> bool {{").unwrap();
+            writeln!(&mut self.file, "    debug_assert!(!c.is_ascii());").unwrap();
             writeln!(&mut self.file, "    (c as u32) >= {first_code_point:#04x} && lookup_slow(c)")
                 .unwrap();
             writeln!(&mut self.file, "}}").unwrap();
@@ -107,6 +108,7 @@ impl RawEmitter {
             writeln!(&mut self.file, "fn lookup_slow(c: char) -> bool {{").unwrap();
         } else {
             writeln!(&mut self.file, "pub fn lookup(c: char) -> bool {{").unwrap();
+            writeln!(&mut self.file, "    debug_assert!(!c.is_ascii());").unwrap();
         }
         writeln!(&mut self.file, "    const {{").unwrap();
         writeln!(
diff --git a/src/tools/unstable-book-gen/src/main.rs b/src/tools/unstable-book-gen/src/main.rs
index a7c6173d88c..16550f83003 100644
--- a/src/tools/unstable-book-gen/src/main.rs
+++ b/src/tools/unstable-book-gen/src/main.rs
@@ -5,6 +5,7 @@ use std::env;
 use std::fs::{self, write};
 use std::path::Path;
 
+use tidy::diagnostics::RunningCheck;
 use tidy::features::{Features, collect_env_vars, collect_lang_features, collect_lib_features};
 use tidy::t;
 use tidy::unstable_book::{
@@ -122,7 +123,7 @@ fn main() {
     let src_path = Path::new(&src_path_str);
     let dest_path = Path::new(&dest_path_str);
 
-    let lang_features = collect_lang_features(compiler_path, &mut false);
+    let lang_features = collect_lang_features(compiler_path, &mut RunningCheck::new_noop());
     let lib_features = collect_lib_features(library_path)
         .into_iter()
         .filter(|&(ref name, _)| !lang_features.contains_key(name))
diff --git a/src/tools/wasm-component-ld/Cargo.toml b/src/tools/wasm-component-ld/Cargo.toml
index ce718902b29..3def2391a13 100644
--- a/src/tools/wasm-component-ld/Cargo.toml
+++ b/src/tools/wasm-component-ld/Cargo.toml
@@ -10,4 +10,4 @@ name = "wasm-component-ld"
 path = "src/main.rs"
 
 [dependencies]
-wasm-component-ld = "0.5.14"
+wasm-component-ld = "0.5.17"