about summary refs log tree commit diff
path: root/src/tools
diff options
context:
space:
mode:
authorLaurențiu Nicola <lnicola@dend.ro>2024-12-11 11:49:08 +0200
committerLaurențiu Nicola <lnicola@dend.ro>2024-12-11 11:49:08 +0200
commit5db2aa865cdc4bf0634fdcbdd8d201253938f02a (patch)
tree6ba3b87d4ac3b10b67159211a493f785c76ee0fa /src/tools
parent1649eb6dd74507bbd9513464609fcd5a9bcc655b (diff)
parent5a6036a1802262f8cf02192b02026688d396f1d7 (diff)
downloadrust-5db2aa865cdc4bf0634fdcbdd8d201253938f02a.tar.gz
rust-5db2aa865cdc4bf0634fdcbdd8d201253938f02a.zip
Merge from rust-lang/rust
Diffstat (limited to 'src/tools')
m---------src/tools/cargo0
-rw-r--r--src/tools/clippy/CHANGELOG.md50
-rw-r--r--src/tools/clippy/Cargo.toml6
-rw-r--r--src/tools/clippy/book/src/SUMMARY.md1
-rw-r--r--src/tools/clippy/book/src/attribs.md53
-rw-r--r--src/tools/clippy/book/src/development/adding_lints.md4
-rw-r--r--src/tools/clippy/book/src/lint_configuration.md2
-rw-r--r--src/tools/clippy/clippy_config/Cargo.toml6
-rw-r--r--src/tools/clippy/clippy_config/src/conf.rs6
-rw-r--r--src/tools/clippy/clippy_config/src/lib.rs8
-rw-r--r--src/tools/clippy/clippy_config/src/types.rs15
-rw-r--r--src/tools/clippy/clippy_dev/Cargo.toml1
-rw-r--r--src/tools/clippy/clippy_dev/src/dogfood.rs2
-rw-r--r--src/tools/clippy/clippy_dev/src/fmt.rs2
-rw-r--r--src/tools/clippy/clippy_dev/src/lib.rs62
-rw-r--r--src/tools/clippy/clippy_dev/src/lint.rs2
-rw-r--r--src/tools/clippy/clippy_dev/src/main.rs44
-rw-r--r--src/tools/clippy/clippy_dev/src/new_lint.rs25
-rw-r--r--src/tools/clippy/clippy_dev/src/release.rs27
-rw-r--r--src/tools/clippy/clippy_dev/src/sync.rs33
-rw-r--r--src/tools/clippy/clippy_dev/src/update_lints.rs71
-rw-r--r--src/tools/clippy/clippy_dev/src/utils.rs142
-rw-r--r--src/tools/clippy/clippy_lints/Cargo.toml4
-rw-r--r--src/tools/clippy/clippy_lints/src/almost_complete_range.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/approx_const.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/assigning_clones.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs/deprecated_cfg_attr.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs/mod.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs/should_panic_without_expect.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/await_holding_invalid.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/booleans.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/cargo/common_metadata.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_slice_from_raw_parts.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/mod.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/ptr_cast_constness.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/checked_conversions.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/copies.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/declared_lints.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/default.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/deprecated_lints.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/dereference.rs41
-rw-r--r--src/tools/clippy/clippy_lints/src/derivable_impls.rs50
-rw-r--r--src/tools/clippy/clippy_lints/src/derive.rs20
-rw-r--r--src/tools/clippy/clippy_lints/src/disallowed_macros.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/disallowed_methods.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/doc/include_in_doc_without_cfg.rs45
-rw-r--r--src/tools/clippy/clippy_lints/src/doc/mod.rs35
-rw-r--r--src/tools/clippy/clippy_lints/src/eta_reduction.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/excessive_bools.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/format_args.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/from_over_into.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/future_not_send.rs67
-rw-r--r--src/tools/clippy/clippy_lints/src/if_let_mutex.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/incompatible_msrv.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/index_refutable_slice.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/init_numbered_fields.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/instant_subtraction.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/large_const_arrays.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/large_include_file.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/lifetimes.rs55
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/mod.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/never_loop.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_bits.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_clamp.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_div_ceil.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_float_methods.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_hash_one.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_let_else.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_main_separator_str.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_retain.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_strip.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/manual_utils.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/mod.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/mem_replace.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/err_expect.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/filter_map_next.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/manual_is_variant_and.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/map_clone.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/mod.rs24
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/needless_collect.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/path_ends_with_ext.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/str_splitn.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/string_lit_chars_any.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs22
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/misc.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_const_for_thread_local.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_update.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/neg_multiply.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/no_effect.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/non_copy_const.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/bit_mask.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/ptr.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/question_mark.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/ranges.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_async_block.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_field_names.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_slicing.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/single_call_fn.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/std_instead_of_core.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/string_patterns.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/trailing_empty_array.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/trait_bounds.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/mod.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/transmute_float_to_int.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_float.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/transmute_num_to_bytes.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/unnamed_address.rs60
-rw-r--r--src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_trait_names.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/use_self.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/author.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/vec.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/zombie_processes.rs2
-rw-r--r--src/tools/clippy/clippy_utils/Cargo.toml13
-rw-r--r--src/tools/clippy/clippy_utils/README.md40
-rw-r--r--src/tools/clippy/clippy_utils/src/ast_utils.rs4
-rw-r--r--src/tools/clippy/clippy_utils/src/attrs.rs3
-rw-r--r--src/tools/clippy/clippy_utils/src/consts.rs4
-rw-r--r--src/tools/clippy/clippy_utils/src/higher.rs4
-rw-r--r--src/tools/clippy/clippy_utils/src/hir_utils.rs29
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs44
-rw-r--r--src/tools/clippy/clippy_utils/src/macros.rs9
-rw-r--r--src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs10
-rw-r--r--src/tools/clippy/clippy_utils/src/mir/possible_origin.rs4
-rw-r--r--src/tools/clippy/clippy_utils/src/mir/transitive_relation.rs6
-rw-r--r--src/tools/clippy/clippy_utils/src/msrvs.rs (renamed from src/tools/clippy/clippy_config/src/msrvs.rs)2
-rw-r--r--src/tools/clippy/clippy_utils/src/paths.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs5
-rw-r--r--src/tools/clippy/clippy_utils/src/ty.rs19
-rw-r--r--src/tools/clippy/clippy_utils/src/usage.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/visitors.rs4
-rw-r--r--src/tools/clippy/rust-toolchain4
-rw-r--r--src/tools/clippy/src/driver.rs8
-rw-r--r--src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.fixed2
-rw-r--r--src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.rs2
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-10645.rs7
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-10645.stderr17
-rw-r--r--src/tools/clippy/tests/ui/derivable_impls.fixed295
-rw-r--r--src/tools/clippy/tests/ui/derivable_impls.rs2
-rw-r--r--src/tools/clippy/tests/ui/derivable_impls.stderr60
-rw-r--r--src/tools/clippy/tests/ui/doc/doc_include_without_cfg.fixed40
-rw-r--r--src/tools/clippy/tests/ui/doc/doc_include_without_cfg.rs40
-rw-r--r--src/tools/clippy/tests/ui/doc/doc_include_without_cfg.stderr17
-rw-r--r--src/tools/clippy/tests/ui/extra_unused_lifetimes.rs7
-rw-r--r--src/tools/clippy/tests/ui/fn_address_comparisons.rs23
-rw-r--r--src/tools/clippy/tests/ui/fn_address_comparisons.stderr17
-rw-r--r--src/tools/clippy/tests/ui/format_args_unfixable.rs29
-rw-r--r--src/tools/clippy/tests/ui/format_args_unfixable.stderr65
-rw-r--r--src/tools/clippy/tests/ui/future_not_send.rs18
-rw-r--r--src/tools/clippy/tests/ui/future_not_send.stderr51
-rw-r--r--src/tools/clippy/tests/ui/if_let_mutex.edition2021.stderr (renamed from src/tools/clippy/tests/ui/if_let_mutex.stderr)8
-rw-r--r--src/tools/clippy/tests/ui/if_let_mutex.rs11
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed6
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs6
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr24
-rw-r--r--src/tools/clippy/tests/ui/missing_doc_crate.rs1
-rw-r--r--src/tools/clippy/tests/ui/redundant_guards.fixed15
-rw-r--r--src/tools/clippy/tests/ui/redundant_guards.rs11
-rw-r--r--src/tools/clippy/tests/ui/redundant_guards.stderr86
-rw-r--r--src/tools/clippy/tests/ui/rename.fixed2
-rw-r--r--src/tools/clippy/tests/ui/rename.rs2
-rw-r--r--src/tools/clippy/tests/ui/rename.stderr140
-rw-r--r--src/tools/clippy/tests/ui/trait_duplication_in_bounds.fixed18
-rw-r--r--src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs18
-rw-r--r--src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr8
-rw-r--r--src/tools/clippy/tests/ui/uninlined_format_args.fixed21
-rw-r--r--src/tools/clippy/tests/ui/uninlined_format_args.rs21
-rw-r--r--src/tools/clippy/tests/ui/uninlined_format_args.stderr50
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_map_or.fixed22
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_map_or.rs20
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_map_or.stderr60
-rw-r--r--src/tools/clippy/tests/ui/unused_format_specs.1.fixed35
-rw-r--r--src/tools/clippy/tests/ui/unused_format_specs.2.fixed35
-rw-r--r--src/tools/clippy/tests/ui/unused_format_specs.rs35
-rw-r--r--src/tools/clippy/tests/ui/unused_format_specs.stderr60
-rw-r--r--src/tools/compiletest/src/common.rs22
-rw-r--r--src/tools/compiletest/src/compute_diff.rs2
-rw-r--r--src/tools/compiletest/src/debuggers.rs6
-rw-r--r--src/tools/compiletest/src/directive-list.rs3
-rw-r--r--src/tools/compiletest/src/header.rs13
-rw-r--r--src/tools/compiletest/src/header/auxiliary.rs11
-rw-r--r--src/tools/compiletest/src/header/needs.rs51
-rw-r--r--src/tools/compiletest/src/header/tests.rs37
-rw-r--r--src/tools/compiletest/src/lib.rs27
-rw-r--r--src/tools/compiletest/src/runtest.rs284
-rw-r--r--src/tools/compiletest/src/runtest/codegen_units.rs22
-rw-r--r--src/tools/compiletest/src/runtest/coverage.rs16
-rw-r--r--src/tools/compiletest/src/runtest/debuginfo.rs10
-rw-r--r--src/tools/compiletest/src/runtest/incremental.rs2
-rw-r--r--src/tools/compiletest/src/runtest/mir_opt.rs2
-rw-r--r--src/tools/compiletest/src/runtest/rustdoc_json.rs2
-rw-r--r--src/tools/compiletest/src/runtest/ui.rs6
-rw-r--r--src/tools/compiletest/src/util.rs2
-rw-r--r--src/tools/jsondocck/src/cache.rs5
-rw-r--r--src/tools/jsondocck/src/error.rs28
-rw-r--r--src/tools/jsondocck/src/main.rs398
-rw-r--r--src/tools/miri/Cargo.lock53
-rw-r--r--src/tools/miri/Cargo.toml5
-rw-r--r--src/tools/miri/README.md7
-rw-r--r--src/tools/miri/bench-cargo-miri/string-replace/Cargo.lock7
-rw-r--r--src/tools/miri/bench-cargo-miri/string-replace/Cargo.toml8
-rw-r--r--src/tools/miri/bench-cargo-miri/string-replace/data.json1
-rw-r--r--src/tools/miri/bench-cargo-miri/string-replace/src/main.rs7
-rw-r--r--src/tools/miri/cargo-miri/src/phases.rs13
-rwxr-xr-xsrc/tools/miri/ci/ci.sh6
-rw-r--r--src/tools/miri/rust-version2
-rw-r--r--src/tools/miri/src/alloc_addresses/mod.rs10
-rw-r--r--src/tools/miri/src/bin/miri.rs6
-rw-r--r--src/tools/miri/src/borrow_tracker/mod.rs4
-rw-r--r--src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs4
-rw-r--r--src/tools/miri/src/borrow_tracker/tree_borrows/foreign_access_skipping.rs108
-rw-r--r--src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs14
-rw-r--r--src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs61
-rw-r--r--src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs190
-rw-r--r--src/tools/miri/src/borrow_tracker/tree_borrows/tree/tests.rs27
-rw-r--r--src/tools/miri/src/concurrency/data_race.rs238
-rw-r--r--src/tools/miri/src/concurrency/weak_memory.rs25
-rw-r--r--src/tools/miri/src/intrinsics/simd.rs17
-rw-r--r--src/tools/miri/src/machine.rs10
-rw-r--r--src/tools/miri/src/shims/backtrace.rs10
-rw-r--r--src/tools/miri/src/shims/files.rs411
-rw-r--r--src/tools/miri/src/shims/io_error.rs69
-rw-r--r--src/tools/miri/src/shims/mod.rs4
-rw-r--r--src/tools/miri/src/shims/native_lib.rs46
-rw-r--r--src/tools/miri/src/shims/panic.rs5
-rw-r--r--src/tools/miri/src/shims/unix/android/foreign_items.rs29
-rw-r--r--src/tools/miri/src/shims/unix/fd.rs406
-rw-r--r--src/tools/miri/src/shims/unix/foreign_items.rs117
-rw-r--r--src/tools/miri/src/shims/unix/freebsd/foreign_items.rs6
-rw-r--r--src/tools/miri/src/shims/unix/fs.rs173
-rw-r--r--src/tools/miri/src/shims/unix/linux/foreign_items.rs13
-rw-r--r--src/tools/miri/src/shims/unix/linux/mod.rs4
-rw-r--r--src/tools/miri/src/shims/unix/linux_like/epoll.rs (renamed from src/tools/miri/src/shims/unix/linux/epoll.rs)14
-rw-r--r--src/tools/miri/src/shims/unix/linux_like/eventfd.rs (renamed from src/tools/miri/src/shims/unix/linux/eventfd.rs)55
-rw-r--r--src/tools/miri/src/shims/unix/linux_like/mod.rs4
-rw-r--r--src/tools/miri/src/shims/unix/linux_like/sync.rs (renamed from src/tools/miri/src/shims/unix/linux/sync.rs)0
-rw-r--r--src/tools/miri/src/shims/unix/linux_like/syscall.rs (renamed from src/tools/miri/src/shims/unix/linux/syscall.rs)4
-rw-r--r--src/tools/miri/src/shims/unix/macos/foreign_items.rs6
-rw-r--r--src/tools/miri/src/shims/unix/mod.rs7
-rw-r--r--src/tools/miri/src/shims/unix/solarish/foreign_items.rs28
-rw-r--r--src/tools/miri/src/shims/unix/unnamed_socket.rs236
-rw-r--r--src/tools/miri/src/shims/windows/foreign_items.rs3
-rw-r--r--src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.rs9
-rw-r--r--src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.rs9
-rw-r--r--src/tools/miri/tests/fail-dep/libc/libc-epoll-data-race.rs2
-rw-r--r--src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs32
-rw-r--r--src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.stderr14
-rw-r--r--src/tools/miri/tests/fail/breakpoint.rs4
-rw-r--r--src/tools/miri/tests/fail/breakpoint.stderr4
-rw-r--r--src/tools/miri/tests/fail/enum-untagged-variant-invalid-encoding.rs27
-rw-r--r--src/tools/miri/tests/fail/enum-untagged-variant-invalid-encoding.stderr15
-rw-r--r--src/tools/miri/tests/fail/should-pass/cpp20_rwc_syncs.rs87
-rw-r--r--src/tools/miri/tests/fail/should-pass/cpp20_rwc_syncs.stderr20
-rw-r--r--src/tools/miri/tests/native-lib/pass/ptr_read_access.rs27
-rw-r--r--src/tools/miri/tests/native-lib/pass/ptr_write_access.rs202
-rw-r--r--src/tools/miri/tests/native-lib/ptr_read_access.c8
-rw-r--r--src/tools/miri/tests/native-lib/ptr_write_access.c90
-rw-r--r--src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs2
-rw-r--r--src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs2
-rw-r--r--src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs9
-rw-r--r--src/tools/miri/tests/pass-dep/libc/libc-fs-symlink.rs7
-rw-r--r--src/tools/miri/tests/pass-dep/libc/libc-fs-with-isolation.rs4
-rw-r--r--src/tools/miri/tests/pass-dep/libc/libc-mem.rs10
-rw-r--r--src/tools/miri/tests/pass-dep/libc/libc-strerror_r.rs16
-rw-r--r--src/tools/miri/tests/pass-dep/libc/libc-sysconf.rs19
-rw-r--r--src/tools/miri/tests/pass/0weak_memory_consistency.rs177
-rw-r--r--src/tools/miri/tests/pass/async-closure-captures.rs2
-rw-r--r--src/tools/miri/tests/pass/async-closure-drop.rs2
-rw-r--r--src/tools/miri/tests/pass/async-closure.rs2
-rw-r--r--src/tools/miri/tests/pass/async-drop.rs2
-rw-r--r--src/tools/miri/tests/pass/async-fn.rs1
-rw-r--r--src/tools/miri/tests/pass/backtrace/backtrace-api-v0.rs69
-rw-r--r--src/tools/miri/tests/pass/backtrace/backtrace-api-v0.stderr18
-rw-r--r--src/tools/miri/tests/pass/backtrace/backtrace-api-v0.stdout5
-rw-r--r--src/tools/miri/tests/pass/dyn-star.rs1
-rw-r--r--src/tools/miri/tests/pass/function_pointers.rs2
-rw-r--r--src/tools/miri/tests/pass/future-self-referential.rs1
-rw-r--r--src/tools/miri/tests/pass/intrinsics/fmuladd_nondeterministic.rs91
-rw-r--r--src/tools/miri/tests/pass/intrinsics/portable-simd.rs22
-rw-r--r--src/tools/miri/tests/pass/issues/issue-91636.rs2
-rw-r--r--src/tools/miri/tests/pass/issues/issue-miri-2068.rs2
-rw-r--r--src/tools/miri/tests/pass/move-data-across-await-point.rs1
-rw-r--r--src/tools/miri/tests/pass/shims/fs.rs7
-rw-r--r--src/tools/miri/tests/ui.rs1
-rw-r--r--src/tools/nix-dev-shell/envrc-flake2
-rw-r--r--src/tools/nix-dev-shell/envrc-shell2
-rw-r--r--src/tools/nix-dev-shell/flake.nix5
-rw-r--r--src/tools/nix-dev-shell/shell.nix5
-rwxr-xr-xsrc/tools/publish_toolstate.py226
-rw-r--r--src/tools/run-make-support/Cargo.toml2
-rw-r--r--src/tools/run-make-support/src/command.rs2
-rw-r--r--src/tools/run-make-support/src/external_deps/rustc.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/inert_attr_macro.rs1
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs13
-rw-r--r--src/tools/rust-analyzer/crates/profile/src/memory_usage.rs4
-rw-r--r--src/tools/rustbook/Cargo.lock486
-rw-r--r--src/tools/rustbook/Cargo.toml3
-rw-r--r--src/tools/rustbook/src/main.rs11
-rw-r--r--src/tools/rustfmt/src/parse/session.rs29
-rw-r--r--src/tools/rustfmt/src/patterns.rs6
-rw-r--r--src/tools/rustfmt/tests/target/guard_patterns.rs12
-rw-r--r--src/tools/tidy/config/black.toml18
-rw-r--r--src/tools/tidy/config/requirements.in1
-rw-r--r--src/tools/tidy/config/requirements.txt52
-rw-r--r--src/tools/tidy/config/ruff.toml6
-rw-r--r--src/tools/tidy/src/deps.rs8
-rw-r--r--src/tools/tidy/src/ext_tool_checks.rs30
-rw-r--r--src/tools/tidy/src/fluent_period.rs1
-rw-r--r--src/tools/tidy/src/issues.txt14
-rw-r--r--src/tools/tidy/src/ui_tests.rs2
-rw-r--r--src/tools/unicode-table-generator/src/range_search.rs1
-rw-r--r--src/tools/unicode-table-generator/src/raw_emitter.rs4
-rw-r--r--src/tools/wasm-component-ld/Cargo.toml2
364 files changed, 5424 insertions, 3001 deletions
diff --git a/src/tools/cargo b/src/tools/cargo
-Subproject 4c39aaff66862cc0da52fe529aa1990bb8bb9a2
+Subproject 20a443231846b81c7b909691ec3f15eb173f2b1
diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md
index dd3124ee9a3..61efaa3bf3e 100644
--- a/src/tools/clippy/CHANGELOG.md
+++ b/src/tools/clippy/CHANGELOG.md
@@ -6,11 +6,56 @@ document.
 
 ## Unreleased / Beta / In Rust Nightly
 
-[0f8eabd6...master](https://github.com/rust-lang/rust-clippy/compare/0f8eabd6...master)
+[aa0d5513...master](https://github.com/rust-lang/rust-clippy/compare/aa0d5513...master)
+
+## Rust 1.83
+
+Current stable, released 2024-11-28
+
+[View all 64 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-08-25T09%3A59%3A01Z..2024-10-03T13%3A42%3A56Z+base%3Amaster)
+
+### Important Change
+
+* Removed the implicit `cargo-clippy` feature set by Clippy as announced here:
+  <https://blog.rust-lang.org/2024/02/28/Clippy-deprecating-feature-cargo-clippy.html>
+  [#13246](https://github.com/rust-lang/rust-clippy/pull/13246)
+
+### New Lints
+
+* Added [`unused_trait_names`] to `restriction`
+  [#13322](https://github.com/rust-lang/rust-clippy/pull/13322)
+* Added [`unnecessary_first_then_check`] to `complexity`
+  [#13421](https://github.com/rust-lang/rust-clippy/pull/13421)
+* Added [`non_zero_suggestions`] to `restriction`
+  [#13167](https://github.com/rust-lang/rust-clippy/pull/13167)
+* Added [`manual_is_power_of_two`] to `pedantic`
+  [#13327](https://github.com/rust-lang/rust-clippy/pull/13327)
+* Added [`manual_div_ceil`] to `complexity`
+  [#12987](https://github.com/rust-lang/rust-clippy/pull/12987)
+* Added [`zombie_processes`] to `suspicious`
+  [#11476](https://github.com/rust-lang/rust-clippy/pull/11476)
+* Added [`used_underscore_items`] to `pedantic`
+  [#13294](https://github.com/rust-lang/rust-clippy/pull/13294)
+
+### Moves and Deprecations
+
+* Moved [`ref_option`] to `pedantic` (From `nursery`)
+  [#13469](https://github.com/rust-lang/rust-clippy/pull/13469)
+* Moved [`manual_c_str_literals`] to `complexity` (From `pedantic`, now warn-by-default)
+  [#13263](https://github.com/rust-lang/rust-clippy/pull/13263)
+* Moved [`empty_line_after_doc_comments`] to `suspicious` (From `nursery`, now warn-by-default)
+  [#13091](https://github.com/rust-lang/rust-clippy/pull/13091)
+* Moved [`empty_line_after_outer_attr`] to `suspicious` (From `nursery`, now warn-by-default)
+  [#13091](https://github.com/rust-lang/rust-clippy/pull/13091)
+
+### Enhancements
+
+* [`missing_panics_doc`]: No longer lints in const environments
+  [#13382](https://github.com/rust-lang/rust-clippy/pull/13382)
 
 ## Rust 1.82
 
-Current stable, released 2024-10-17
+Released 2024-10-17
 
 [View all 108 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2024-07-11T20%3A12%3A07Z..2024-08-24T20%3A55%3A35Z+base%3Amaster)
 
@@ -5441,6 +5486,7 @@ Released 2018-09-13
 [`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type
 [`disallowed_types`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types
 [`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression
+[`doc_include_without_cfg`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_include_without_cfg
 [`doc_lazy_continuation`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_lazy_continuation
 [`doc_link_with_quotes`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_link_with_quotes
 [`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml
index 50a2afbfc07..77cb0006ff8 100644
--- a/src/tools/clippy/Cargo.toml
+++ b/src/tools/clippy/Cargo.toml
@@ -1,6 +1,8 @@
 [package]
 name = "clippy"
-version = "0.1.84"
+# begin autogenerated version
+version = "0.1.85"
+# end autogenerated version
 description = "A bunch of helpful lints to avoid common pitfalls in Rust"
 repository = "https://github.com/rust-lang/rust-clippy"
 readme = "README.md"
@@ -27,7 +29,7 @@ rustc_tools_util = "0.4.0"
 tempfile = { version = "3.3", optional = true }
 termize = "0.1"
 color-print = "0.3.4"
-anstream = "0.6.0"
+anstream = "0.6.18"
 
 [dev-dependencies]
 cargo_metadata = "0.18.1"
diff --git a/src/tools/clippy/book/src/SUMMARY.md b/src/tools/clippy/book/src/SUMMARY.md
index be13fcbe260..19328fdd3cd 100644
--- a/src/tools/clippy/book/src/SUMMARY.md
+++ b/src/tools/clippy/book/src/SUMMARY.md
@@ -7,6 +7,7 @@
 - [Configuration](configuration.md)
     - [Lint Configuration](lint_configuration.md)
 - [Clippy's Lints](lints.md)
+- [Attributes for Crate Authors](attribs.md)
 - [Continuous Integration](continuous_integration/README.md)
     - [GitHub Actions](continuous_integration/github_actions.md)
     - [GitLab CI](continuous_integration/gitlab.md)
diff --git a/src/tools/clippy/book/src/attribs.md b/src/tools/clippy/book/src/attribs.md
new file mode 100644
index 00000000000..cf99497bc0f
--- /dev/null
+++ b/src/tools/clippy/book/src/attribs.md
@@ -0,0 +1,53 @@
+# Attributes for Crate Authors
+
+In some cases it is possible to extend Clippy coverage to 3rd party libraries.
+To do this, Clippy provides attributes that can be applied to items in the 3rd party crate.
+
+## `#[clippy::format_args]`
+
+_Available since Clippy v1.84_
+
+This attribute can be added to a macro that supports `format!`, `println!`, or similar syntax.
+It tells Clippy that the macro is a formatting macro, and that the arguments to the macro
+should be linted as if they were arguments to `format!`. Any lint that would apply to a
+`format!` call will also apply to the macro call. The macro may have additional arguments
+before the format string, and these will be ignored.
+
+### Example
+
+```rust
+/// A macro that prints a message if a condition is true.
+#[macro_export]
+#[clippy::format_args]
+macro_rules! print_if {
+    ($condition:expr, $($args:tt)+) => {{
+        if $condition {
+            println!($($args)+)
+        }
+    }};
+}
+```
+
+## `#[clippy::has_significant_drop]`
+
+_Available since Clippy v1.60_
+
+The `clippy::has_significant_drop` attribute can be added to types whose Drop impls have an important side effect,
+such as unlocking a mutex, making it important for users to be able to accurately understand their lifetimes.
+When a temporary is returned in a function call in a match scrutinee, its lifetime lasts until the end of the match
+block, which may be surprising.
+
+### Example
+
+```rust
+#[clippy::has_significant_drop]
+struct CounterWrapper<'a> {
+    counter: &'a Counter,
+}
+
+impl<'a> Drop for CounterWrapper<'a> {
+    fn drop(&mut self) {
+        self.counter.i.fetch_sub(1, Ordering::Relaxed);
+    }
+}
+```
diff --git a/src/tools/clippy/book/src/development/adding_lints.md b/src/tools/clippy/book/src/development/adding_lints.md
index 963e02e5c16..c07568697d0 100644
--- a/src/tools/clippy/book/src/development/adding_lints.md
+++ b/src/tools/clippy/book/src/development/adding_lints.md
@@ -438,7 +438,7 @@ need to ensure that the MSRV configured for the project is >= the MSRV of the
 required Rust feature. If multiple features are required, just use the one with
 a lower MSRV.
 
-First, add an MSRV alias for the required feature in [`clippy_config::msrvs`].
+First, add an MSRV alias for the required feature in [`clippy_utils::msrvs`].
 This can be accessed later as `msrvs::STR_STRIP_PREFIX`, for example.
 
 ```rust
@@ -517,7 +517,7 @@ define_Conf! {
 }
 ```
 
-[`clippy_config::msrvs`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_config/msrvs/index.html
+[`clippy_utils::msrvs`]: https://doc.rust-lang.org/nightly/nightly-rustc/clippy_config/msrvs/index.html
 
 Afterwards update the documentation for the book as described in [Adding configuration to a lint](#adding-configuration-to-a-lint).
 
diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md
index 670b5cbef82..275d125096e 100644
--- a/src/tools/clippy/book/src/lint_configuration.md
+++ b/src/tools/clippy/book/src/lint_configuration.md
@@ -895,7 +895,7 @@ The order of associated items in traits.
 
 ## `trivial-copy-size-limit`
 The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by
-reference. By default there is no limit
+reference.
 
 **Default Value:** `target_pointer_width * 2`
 
diff --git a/src/tools/clippy/clippy_config/Cargo.toml b/src/tools/clippy/clippy_config/Cargo.toml
index d21df202dca..3f18a0bc7d2 100644
--- a/src/tools/clippy/clippy_config/Cargo.toml
+++ b/src/tools/clippy/clippy_config/Cargo.toml
@@ -1,11 +1,15 @@
 [package]
 name = "clippy_config"
-version = "0.1.84"
+# begin autogenerated version
+version = "0.1.85"
+# end autogenerated version
 edition = "2021"
+publish = false
 
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
+clippy_utils = { path = "../clippy_utils" }
 itertools = "0.12"
 serde = { version = "1.0", features = ["derive"] }
 toml = "0.7.3"
diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs
index 600d5b6e2c8..41b56b45d9a 100644
--- a/src/tools/clippy/clippy_config/src/conf.rs
+++ b/src/tools/clippy/clippy_config/src/conf.rs
@@ -1,10 +1,10 @@
 use crate::ClippyConfiguration;
-use crate::msrvs::Msrv;
 use crate::types::{
     DisallowedPath, MacroMatcher, MatchLintBehaviour, PubUnderscoreFieldsBehaviour, Rename, SourceItemOrdering,
     SourceItemOrderingCategory, SourceItemOrderingModuleItemGroupings, SourceItemOrderingModuleItemKind,
     SourceItemOrderingTraitAssocItemKind, SourceItemOrderingTraitAssocItemKinds,
 };
+use clippy_utils::msrvs::Msrv;
 use rustc_errors::Applicability;
 use rustc_session::Session;
 use rustc_span::edit_distance::edit_distance;
@@ -181,7 +181,7 @@ macro_rules! define_Conf {
     )*) => {
         /// Clippy lint configuration
         pub struct Conf {
-            $($(#[doc = $doc])+ pub $name: $ty,)*
+            $($(#[cfg_attr(doc, doc = $doc)])+ pub $name: $ty,)*
         }
 
         mod defaults {
@@ -678,7 +678,7 @@ define_Conf! {
     #[lints(arbitrary_source_item_ordering)]
     trait_assoc_item_kinds_order: SourceItemOrderingTraitAssocItemKinds = DEFAULT_TRAIT_ASSOC_ITEM_KINDS_ORDER.into(),
     /// The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by
-    /// reference. By default there is no limit
+    /// reference.
     #[default_text = "target_pointer_width * 2"]
     #[lints(trivially_copy_pass_by_ref)]
     trivial_copy_size_limit: Option<u64> = None,
diff --git a/src/tools/clippy/clippy_config/src/lib.rs b/src/tools/clippy/clippy_config/src/lib.rs
index 1c3f32c2514..5d6e8b87516 100644
--- a/src/tools/clippy/clippy_config/src/lib.rs
+++ b/src/tools/clippy/clippy_config/src/lib.rs
@@ -13,18 +13,14 @@
     rustc::untranslatable_diagnostic
 )]
 
-extern crate rustc_ast;
-extern crate rustc_attr;
-#[allow(unused_extern_crates)]
-extern crate rustc_driver;
 extern crate rustc_errors;
+extern crate rustc_hir;
+extern crate rustc_middle;
 extern crate rustc_session;
 extern crate rustc_span;
-extern crate smallvec;
 
 mod conf;
 mod metadata;
-pub mod msrvs;
 pub mod types;
 
 pub use conf::{Conf, get_configuration_metadata, lookup_conf_file, sanitize_explanation};
diff --git a/src/tools/clippy/clippy_config/src/types.rs b/src/tools/clippy/clippy_config/src/types.rs
index fe576424148..c949db9109d 100644
--- a/src/tools/clippy/clippy_config/src/types.rs
+++ b/src/tools/clippy/clippy_config/src/types.rs
@@ -1,3 +1,6 @@
+use clippy_utils::def_path_def_ids;
+use rustc_hir::def_id::DefIdMap;
+use rustc_middle::ty::TyCtxt;
 use serde::de::{self, Deserializer, Visitor};
 use serde::{Deserialize, Serialize, ser};
 use std::collections::HashMap;
@@ -31,6 +34,18 @@ impl DisallowedPath {
     }
 }
 
+/// Creates a map of disallowed items to the reason they were disallowed.
+pub fn create_disallowed_map(
+    tcx: TyCtxt<'_>,
+    disallowed: &'static [DisallowedPath],
+) -> DefIdMap<(&'static str, Option<&'static str>)> {
+    disallowed
+        .iter()
+        .map(|x| (x.path(), x.path().split("::").collect::<Vec<_>>(), x.reason()))
+        .flat_map(|(name, path, reason)| def_path_def_ids(tcx, &path).map(move |id| (id, (name, reason))))
+        .collect()
+}
+
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Deserialize, Serialize)]
 pub enum MatchLintBehaviour {
     AllTypes,
diff --git a/src/tools/clippy/clippy_dev/Cargo.toml b/src/tools/clippy/clippy_dev/Cargo.toml
index 952a8711fb4..d3a103eaf4c 100644
--- a/src/tools/clippy/clippy_dev/Cargo.toml
+++ b/src/tools/clippy/clippy_dev/Cargo.toml
@@ -6,6 +6,7 @@ edition = "2021"
 
 [dependencies]
 aho-corasick = "1.0"
+chrono = { version = "0.4.38", default-features = false, features = ["clock"] }
 clap = { version = "4.4", features = ["derive"] }
 indoc = "1.0"
 itertools = "0.12"
diff --git a/src/tools/clippy/clippy_dev/src/dogfood.rs b/src/tools/clippy/clippy_dev/src/dogfood.rs
index a0d57f5ab48..75a4cbd2f92 100644
--- a/src/tools/clippy/clippy_dev/src/dogfood.rs
+++ b/src/tools/clippy/clippy_dev/src/dogfood.rs
@@ -1,4 +1,4 @@
-use crate::{clippy_project_root, exit_if_err};
+use crate::utils::{clippy_project_root, exit_if_err};
 use std::process::Command;
 
 /// # Panics
diff --git a/src/tools/clippy/clippy_dev/src/fmt.rs b/src/tools/clippy/clippy_dev/src/fmt.rs
index 8c61c35533c..c6673859282 100644
--- a/src/tools/clippy/clippy_dev/src/fmt.rs
+++ b/src/tools/clippy/clippy_dev/src/fmt.rs
@@ -1,4 +1,4 @@
-use crate::clippy_project_root;
+use crate::utils::clippy_project_root;
 use itertools::Itertools;
 use rustc_lexer::{TokenKind, tokenize};
 use shell_escape::escape;
diff --git a/src/tools/clippy/clippy_dev/src/lib.rs b/src/tools/clippy/clippy_dev/src/lib.rs
index ad385d5fbd2..9280369c23b 100644
--- a/src/tools/clippy/clippy_dev/src/lib.rs
+++ b/src/tools/clippy/clippy_dev/src/lib.rs
@@ -14,69 +14,13 @@
 extern crate rustc_driver;
 extern crate rustc_lexer;
 
-use std::io;
-use std::path::PathBuf;
-use std::process::{self, ExitStatus};
-
 pub mod dogfood;
 pub mod fmt;
 pub mod lint;
 pub mod new_lint;
+pub mod release;
 pub mod serve;
 pub mod setup;
+pub mod sync;
 pub mod update_lints;
-
-#[cfg(not(windows))]
-static CARGO_CLIPPY_EXE: &str = "cargo-clippy";
-#[cfg(windows)]
-static CARGO_CLIPPY_EXE: &str = "cargo-clippy.exe";
-
-/// Returns the path to the `cargo-clippy` binary
-///
-/// # Panics
-///
-/// Panics if the path of current executable could not be retrieved.
-#[must_use]
-pub fn cargo_clippy_path() -> PathBuf {
-    let mut path = std::env::current_exe().expect("failed to get current executable name");
-    path.set_file_name(CARGO_CLIPPY_EXE);
-    path
-}
-
-/// Returns the path to the Clippy project directory
-///
-/// # Panics
-///
-/// Panics if the current directory could not be retrieved, there was an error reading any of the
-/// Cargo.toml files or ancestor directory is the clippy root directory
-#[must_use]
-pub fn clippy_project_root() -> PathBuf {
-    let current_dir = std::env::current_dir().unwrap();
-    for path in current_dir.ancestors() {
-        let result = std::fs::read_to_string(path.join("Cargo.toml"));
-        if let Err(err) = &result {
-            if err.kind() == io::ErrorKind::NotFound {
-                continue;
-            }
-        }
-
-        let content = result.unwrap();
-        if content.contains("[package]\nname = \"clippy\"") {
-            return path.to_path_buf();
-        }
-    }
-    panic!("error: Can't determine root of project. Please run inside a Clippy working dir.");
-}
-
-/// # 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);
-        },
-    }
-}
+pub mod utils;
diff --git a/src/tools/clippy/clippy_dev/src/lint.rs b/src/tools/clippy/clippy_dev/src/lint.rs
index f308f5dfdfd..125195397e6 100644
--- a/src/tools/clippy/clippy_dev/src/lint.rs
+++ b/src/tools/clippy/clippy_dev/src/lint.rs
@@ -1,4 +1,4 @@
-use crate::{cargo_clippy_path, exit_if_err};
+use crate::utils::{cargo_clippy_path, exit_if_err};
 use std::process::{self, Command};
 use std::{env, fs};
 
diff --git a/src/tools/clippy/clippy_dev/src/main.rs b/src/tools/clippy/clippy_dev/src/main.rs
index fc15913354c..56ed60256f1 100644
--- a/src/tools/clippy/clippy_dev/src/main.rs
+++ b/src/tools/clippy/clippy_dev/src/main.rs
@@ -3,7 +3,7 @@
 #![warn(rust_2018_idioms, unused_lifetimes)]
 
 use clap::{Args, Parser, Subcommand};
-use clippy_dev::{dogfood, fmt, lint, new_lint, serve, setup, update_lints};
+use clippy_dev::{dogfood, fmt, lint, new_lint, release, serve, setup, sync, update_lints, utils};
 use std::convert::Infallible;
 
 fn main() {
@@ -23,9 +23,9 @@ fn main() {
             if print_only {
                 update_lints::print_lints();
             } else if check {
-                update_lints::update(update_lints::UpdateMode::Check);
+                update_lints::update(utils::UpdateMode::Check);
             } else {
-                update_lints::update(update_lints::UpdateMode::Change);
+                update_lints::update(utils::UpdateMode::Change);
             }
         },
         DevCommand::NewLint {
@@ -35,7 +35,7 @@ fn main() {
             r#type,
             msrv,
         } => match new_lint::create(&pass, &name, &category, r#type.as_deref(), msrv) {
-            Ok(()) => update_lints::update(update_lints::UpdateMode::Change),
+            Ok(()) => update_lints::update(utils::UpdateMode::Change),
             Err(e) => eprintln!("Unable to create lint: {e}"),
         },
         DevCommand::Setup(SetupCommand { subcommand }) => match subcommand {
@@ -75,6 +75,12 @@ fn main() {
             uplift,
         } => update_lints::rename(&old_name, new_name.as_ref().unwrap_or(&old_name), uplift),
         DevCommand::Deprecate { name, reason } => update_lints::deprecate(&name, &reason),
+        DevCommand::Sync(SyncCommand { subcommand }) => match subcommand {
+            SyncSubcommand::UpdateNightly => sync::update_nightly(),
+        },
+        DevCommand::Release(ReleaseCommand { subcommand }) => match subcommand {
+            ReleaseSubcommand::BumpVersion => release::bump_version(),
+        },
     }
 }
 
@@ -225,6 +231,10 @@ enum DevCommand {
         /// The reason for deprecation
         reason: String,
     },
+    /// Sync between the rust repo and the Clippy repo
+    Sync(SyncCommand),
+    /// Manage Clippy releases
+    Release(ReleaseCommand),
 }
 
 #[derive(Args)]
@@ -291,3 +301,29 @@ enum RemoveSubcommand {
     /// Remove the tasks added with 'cargo dev setup vscode-tasks'
     VscodeTasks,
 }
+
+#[derive(Args)]
+struct SyncCommand {
+    #[command(subcommand)]
+    subcommand: SyncSubcommand,
+}
+
+#[derive(Subcommand)]
+enum SyncSubcommand {
+    #[command(name = "update_nightly")]
+    /// Update nightly version in rust-toolchain and `clippy_utils`
+    UpdateNightly,
+}
+
+#[derive(Args)]
+struct ReleaseCommand {
+    #[command(subcommand)]
+    subcommand: ReleaseSubcommand,
+}
+
+#[derive(Subcommand)]
+enum ReleaseSubcommand {
+    #[command(name = "bump_version")]
+    /// Bump the version in the Cargo.toml files
+    BumpVersion,
+}
diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs
index e15ba339717..24d1a53266a 100644
--- a/src/tools/clippy/clippy_dev/src/new_lint.rs
+++ b/src/tools/clippy/clippy_dev/src/new_lint.rs
@@ -1,4 +1,4 @@
-use crate::clippy_project_root;
+use crate::utils::{clippy_project_root, clippy_version};
 use indoc::{formatdoc, writedoc};
 use std::fmt;
 use std::fmt::Write as _;
@@ -186,23 +186,8 @@ fn to_camel_case(name: &str) -> String {
 }
 
 pub(crate) fn get_stabilization_version() -> String {
-    fn parse_manifest(contents: &str) -> Option<String> {
-        let version = contents
-            .lines()
-            .filter_map(|l| l.split_once('='))
-            .find_map(|(k, v)| (k.trim() == "version").then(|| v.trim()))?;
-        let Some(("0", version)) = version.get(1..version.len() - 1)?.split_once('.') else {
-            return None;
-        };
-        let (minor, patch) = version.split_once('.')?;
-        Some(format!(
-            "{}.{}.0",
-            minor.parse::<u32>().ok()?,
-            patch.parse::<u32>().ok()?
-        ))
-    }
-    let contents = fs::read_to_string("Cargo.toml").expect("Unable to read `Cargo.toml`");
-    parse_manifest(&contents).expect("Unable to find package version in `Cargo.toml`")
+    let (minor, patch) = clippy_version();
+    format!("{minor}.{patch}.0")
 }
 
 fn get_test_file_contents(lint_name: &str, msrv: bool) -> String {
@@ -273,7 +258,7 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
     result.push_str(&if enable_msrv {
         formatdoc!(
             r"
-            use clippy_config::msrvs::{{self, Msrv}};
+            use clippy_utils::msrvs::{{self, Msrv}};
             use clippy_config::Conf;
             {pass_import}
             use rustc_lint::{{{context_import}, {pass_type}, LintContext}};
@@ -399,7 +384,7 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R
         let _: fmt::Result = writedoc!(
             lint_file_contents,
             r#"
-                use clippy_config::msrvs::{{self, Msrv}};
+                use clippy_utils::msrvs::{{self, Msrv}};
                 use rustc_lint::{{{context_import}, LintContext}};
 
                 use super::{name_upper};
diff --git a/src/tools/clippy/clippy_dev/src/release.rs b/src/tools/clippy/clippy_dev/src/release.rs
new file mode 100644
index 00000000000..ac755168701
--- /dev/null
+++ b/src/tools/clippy/clippy_dev/src/release.rs
@@ -0,0 +1,27 @@
+use std::fmt::Write;
+use std::path::Path;
+
+use crate::utils::{UpdateMode, clippy_version, replace_region_in_file};
+
+const CARGO_TOML_FILES: [&str; 4] = [
+    "clippy_config/Cargo.toml",
+    "clippy_lints/Cargo.toml",
+    "clippy_utils/Cargo.toml",
+    "Cargo.toml",
+];
+
+pub fn bump_version() {
+    let (minor, mut patch) = clippy_version();
+    patch += 1;
+    for file in &CARGO_TOML_FILES {
+        replace_region_in_file(
+            UpdateMode::Change,
+            Path::new(file),
+            "# begin autogenerated version\n",
+            "# end autogenerated version",
+            |res| {
+                writeln!(res, "version = \"0.{minor}.{patch}\"").unwrap();
+            },
+        );
+    }
+}
diff --git a/src/tools/clippy/clippy_dev/src/sync.rs b/src/tools/clippy/clippy_dev/src/sync.rs
new file mode 100644
index 00000000000..3522d182e90
--- /dev/null
+++ b/src/tools/clippy/clippy_dev/src/sync.rs
@@ -0,0 +1,33 @@
+use std::fmt::Write;
+use std::path::Path;
+
+use chrono::offset::Utc;
+
+use crate::utils::{UpdateMode, replace_region_in_file};
+
+pub fn update_nightly() {
+    // Update rust-toolchain nightly version
+    let date = Utc::now().format("%Y-%m-%d").to_string();
+    replace_region_in_file(
+        UpdateMode::Change,
+        Path::new("rust-toolchain"),
+        "# begin autogenerated nightly\n",
+        "# end autogenerated nightly",
+        |res| {
+            writeln!(res, "channel = \"nightly-{date}\"").unwrap();
+        },
+    );
+
+    // Update clippy_utils nightly version
+    replace_region_in_file(
+        UpdateMode::Change,
+        Path::new("clippy_utils/README.md"),
+        "<!-- begin autogenerated nightly -->\n",
+        "<!-- end autogenerated nightly -->",
+        |res| {
+            writeln!(res, "```").unwrap();
+            writeln!(res, "nightly-{date}").unwrap();
+            writeln!(res, "```").unwrap();
+        },
+    );
+}
diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs
index 795456ad3c5..612d1c0ae13 100644
--- a/src/tools/clippy/clippy_dev/src/update_lints.rs
+++ b/src/tools/clippy/clippy_dev/src/update_lints.rs
@@ -1,4 +1,4 @@
-use crate::clippy_project_root;
+use crate::utils::{UpdateMode, clippy_project_root, exit_with_failure, replace_region_in_file};
 use aho_corasick::AhoCorasickBuilder;
 use itertools::Itertools;
 use rustc_lexer::{LiteralKind, TokenKind, tokenize, unescape};
@@ -17,12 +17,6 @@ const GENERATED_FILE_COMMENT: &str = "// This file was generated by `cargo dev u
 
 const DOCS_LINK: &str = "https://rust-lang.github.io/rust-clippy/master/index.html";
 
-#[derive(Clone, Copy, PartialEq, Eq)]
-pub enum UpdateMode {
-    Check,
-    Change,
-}
-
 /// Runs the `update_lints` command.
 ///
 /// This updates various generated values from the lint source code.
@@ -511,14 +505,6 @@ fn process_file(path: impl AsRef<Path>, update_mode: UpdateMode, content: &str)
     }
 }
 
-fn exit_with_failure() {
-    println!(
-        "Not all lints defined properly. \
-                 Please run `cargo dev update_lints` to make sure all lints are defined properly."
-    );
-    std::process::exit(1);
-}
-
 /// Lint data parsed from the Clippy source code.
 #[derive(Clone, PartialEq, Eq, Debug)]
 struct Lint {
@@ -851,61 +837,6 @@ fn remove_line_splices(s: &str) -> String {
     });
     res
 }
-
-/// Replaces a region in a file delimited by two lines matching regexes.
-///
-/// `path` is the relative path to the file on which you want to perform the replacement.
-///
-/// See `replace_region_in_text` for documentation of the other options.
-///
-/// # Panics
-///
-/// Panics if the path could not read or then written
-fn replace_region_in_file(
-    update_mode: UpdateMode,
-    path: &Path,
-    start: &str,
-    end: &str,
-    write_replacement: impl FnMut(&mut String),
-) {
-    let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {e}", path.display()));
-    let new_contents = match replace_region_in_text(&contents, start, end, write_replacement) {
-        Ok(x) => x,
-        Err(delim) => panic!("Couldn't find `{delim}` in file `{}`", path.display()),
-    };
-
-    match update_mode {
-        UpdateMode::Check if contents != new_contents => exit_with_failure(),
-        UpdateMode::Check => (),
-        UpdateMode::Change => {
-            if let Err(e) = fs::write(path, new_contents.as_bytes()) {
-                panic!("Cannot write to `{}`: {e}", path.display());
-            }
-        },
-    }
-}
-
-/// Replaces a region in a text delimited by two strings. Returns the new text if both delimiters
-/// were found, or the missing delimiter if not.
-fn replace_region_in_text<'a>(
-    text: &str,
-    start: &'a str,
-    end: &'a str,
-    mut write_replacement: impl FnMut(&mut String),
-) -> Result<String, &'a str> {
-    let (text_start, rest) = text.split_once(start).ok_or(start)?;
-    let (_, text_end) = rest.split_once(end).ok_or(end)?;
-
-    let mut res = String::with_capacity(text.len() + 4096);
-    res.push_str(text_start);
-    res.push_str(start);
-    write_replacement(&mut res);
-    res.push_str(end);
-    res.push_str(text_end);
-
-    Ok(res)
-}
-
 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),
diff --git a/src/tools/clippy/clippy_dev/src/utils.rs b/src/tools/clippy/clippy_dev/src/utils.rs
new file mode 100644
index 00000000000..b87fcca13b1
--- /dev/null
+++ b/src/tools/clippy/clippy_dev/src/utils.rs
@@ -0,0 +1,142 @@
+use std::path::{Path, PathBuf};
+use std::process::{self, ExitStatus};
+use std::{fs, io};
+
+#[cfg(not(windows))]
+static CARGO_CLIPPY_EXE: &str = "cargo-clippy";
+#[cfg(windows)]
+static CARGO_CLIPPY_EXE: &str = "cargo-clippy.exe";
+
+/// Returns the path to the `cargo-clippy` binary
+///
+/// # Panics
+///
+/// Panics if the path of current executable could not be retrieved.
+#[must_use]
+pub fn cargo_clippy_path() -> PathBuf {
+    let mut path = std::env::current_exe().expect("failed to get current executable name");
+    path.set_file_name(CARGO_CLIPPY_EXE);
+    path
+}
+
+/// Returns the path to the Clippy project directory
+///
+/// # Panics
+///
+/// Panics if the current directory could not be retrieved, there was an error reading any of the
+/// Cargo.toml files or ancestor directory is the clippy root directory
+#[must_use]
+pub fn clippy_project_root() -> PathBuf {
+    let current_dir = std::env::current_dir().unwrap();
+    for path in current_dir.ancestors() {
+        let result = fs::read_to_string(path.join("Cargo.toml"));
+        if let Err(err) = &result {
+            if err.kind() == io::ErrorKind::NotFound {
+                continue;
+            }
+        }
+
+        let content = result.unwrap();
+        if content.contains("[package]\nname = \"clippy\"") {
+            return path.to_path_buf();
+        }
+    }
+    panic!("error: Can't determine root of project. Please run inside a Clippy working dir.");
+}
+
+/// # 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);
+        },
+    }
+}
+
+pub(crate) fn clippy_version() -> (u32, u32) {
+    fn parse_manifest(contents: &str) -> Option<(u32, u32)> {
+        let version = contents
+            .lines()
+            .filter_map(|l| l.split_once('='))
+            .find_map(|(k, v)| (k.trim() == "version").then(|| v.trim()))?;
+        let Some(("0", version)) = version.get(1..version.len() - 1)?.split_once('.') else {
+            return None;
+        };
+        let (minor, patch) = version.split_once('.')?;
+        Some((minor.parse().ok()?, patch.parse().ok()?))
+    }
+    let contents = fs::read_to_string("Cargo.toml").expect("Unable to read `Cargo.toml`");
+    parse_manifest(&contents).expect("Unable to find package version in `Cargo.toml`")
+}
+
+#[derive(Clone, Copy, PartialEq, Eq)]
+pub enum UpdateMode {
+    Check,
+    Change,
+}
+
+pub(crate) fn exit_with_failure() {
+    println!(
+        "Not all lints defined properly. \
+                 Please run `cargo dev update_lints` to make sure all lints are defined properly."
+    );
+    process::exit(1);
+}
+
+/// Replaces a region in a file delimited by two lines matching regexes.
+///
+/// `path` is the relative path to the file on which you want to perform the replacement.
+///
+/// See `replace_region_in_text` for documentation of the other options.
+///
+/// # Panics
+///
+/// Panics if the path could not read or then written
+pub(crate) fn replace_region_in_file(
+    update_mode: UpdateMode,
+    path: &Path,
+    start: &str,
+    end: &str,
+    write_replacement: impl FnMut(&mut String),
+) {
+    let contents = fs::read_to_string(path).unwrap_or_else(|e| panic!("Cannot read from `{}`: {e}", path.display()));
+    let new_contents = match replace_region_in_text(&contents, start, end, write_replacement) {
+        Ok(x) => x,
+        Err(delim) => panic!("Couldn't find `{delim}` in file `{}`", path.display()),
+    };
+
+    match update_mode {
+        UpdateMode::Check if contents != new_contents => exit_with_failure(),
+        UpdateMode::Check => (),
+        UpdateMode::Change => {
+            if let Err(e) = fs::write(path, new_contents.as_bytes()) {
+                panic!("Cannot write to `{}`: {e}", path.display());
+            }
+        },
+    }
+}
+
+/// Replaces a region in a text delimited by two strings. Returns the new text if both delimiters
+/// were found, or the missing delimiter if not.
+pub(crate) fn replace_region_in_text<'a>(
+    text: &str,
+    start: &'a str,
+    end: &'a str,
+    mut write_replacement: impl FnMut(&mut String),
+) -> Result<String, &'a str> {
+    let (text_start, rest) = text.split_once(start).ok_or(start)?;
+    let (_, text_end) = rest.split_once(end).ok_or(end)?;
+
+    let mut res = String::with_capacity(text.len() + 4096);
+    res.push_str(text_start);
+    res.push_str(start);
+    write_replacement(&mut res);
+    res.push_str(end);
+    res.push_str(text_end);
+
+    Ok(res)
+}
diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml
index 63ea6faf60d..c1f8e82f698 100644
--- a/src/tools/clippy/clippy_lints/Cargo.toml
+++ b/src/tools/clippy/clippy_lints/Cargo.toml
@@ -1,6 +1,8 @@
 [package]
 name = "clippy_lints"
-version = "0.1.84"
+# begin autogenerated version
+version = "0.1.85"
+# end autogenerated version
 description = "A bunch of helpful lints to avoid common pitfalls in Rust"
 repository = "https://github.com/rust-lang/rust-clippy"
 readme = "README.md"
diff --git a/src/tools/clippy/clippy_lints/src/almost_complete_range.rs b/src/tools/clippy/clippy_lints/src/almost_complete_range.rs
index 370f0c482fd..2af5178920d 100644
--- a/src/tools/clippy/clippy_lints/src/almost_complete_range.rs
+++ b/src/tools/clippy/clippy_lints/src/almost_complete_range.rs
@@ -1,6 +1,6 @@
 use clippy_config::Conf;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::{trim_span, walk_span_to_context};
 use rustc_ast::ast::{Expr, ExprKind, LitKind, Pat, PatKind, RangeEnd, RangeLimits};
 use rustc_errors::Applicability;
diff --git a/src/tools/clippy/clippy_lints/src/approx_const.rs b/src/tools/clippy/clippy_lints/src/approx_const.rs
index 4f8f091a095..ebd35fd2b27 100644
--- a/src/tools/clippy/clippy_lints/src/approx_const.rs
+++ b/src/tools/clippy/clippy_lints/src/approx_const.rs
@@ -1,6 +1,6 @@
 use clippy_config::Conf;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::msrvs::{self, Msrv};
 use rustc_ast::ast::{FloatTy, LitFloatType, LitKind};
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
@@ -91,7 +91,7 @@ impl ApproxConstant {
         let s = s.as_str();
         if s.parse::<f64>().is_ok() {
             for &(constant, name, min_digits, msrv) in &KNOWN_CONSTS {
-                if is_approx_const(constant, s, min_digits) && msrv.map_or(true, |msrv| self.msrv.meets(msrv)) {
+                if is_approx_const(constant, s, min_digits) && msrv.is_none_or(|msrv| self.msrv.meets(msrv)) {
                     span_lint_and_help(
                         cx,
                         APPROX_CONSTANT,
diff --git a/src/tools/clippy/clippy_lints/src/assigning_clones.rs b/src/tools/clippy/clippy_lints/src/assigning_clones.rs
index 00626a37ef8..c8dd77d9578 100644
--- a/src/tools/clippy/clippy_lints/src/assigning_clones.rs
+++ b/src/tools/clippy/clippy_lints/src/assigning_clones.rs
@@ -1,7 +1,7 @@
 use clippy_config::Conf;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::mir::{PossibleBorrowerMap, enclosing_mir};
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::sugg::Sugg;
 use clippy_utils::{is_diag_trait_item, is_in_test, last_path_segment, local_is_initialized, path_to_local};
 use rustc_errors::Applicability;
@@ -100,13 +100,13 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones {
             // TODO: This check currently bails if the local variable has no initializer.
             // That is overly conservative - the lint should fire even if there was no initializer,
             // but the variable has been initialized before `lhs` was evaluated.
-            && path_to_local(lhs).map_or(true, |lhs| local_is_initialized(cx, lhs))
+            && path_to_local(lhs).is_none_or(|lhs| local_is_initialized(cx, lhs))
             && let Some(resolved_impl) = cx.tcx.impl_of_method(resolved_fn.def_id())
             // Derived forms don't implement `clone_from`/`clone_into`.
             // See https://github.com/rust-lang/rust/pull/98445#issuecomment-1190681305
             && !cx.tcx.is_builtin_derived(resolved_impl)
             // Don't suggest calling a function we're implementing.
-            && resolved_impl.as_local().map_or(true, |block_id| {
+            && resolved_impl.as_local().is_none_or(|block_id| {
                 cx.tcx.hir().parent_owner_iter(e.hir_id).all(|(id, _)| id.def_id != block_id)
             })
             && let resolved_assoc_items = cx.tcx.associated_items(resolved_impl)
diff --git a/src/tools/clippy/clippy_lints/src/attrs/deprecated_cfg_attr.rs b/src/tools/clippy/clippy_lints/src/attrs/deprecated_cfg_attr.rs
index abf924f7542..3a462018e3e 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/deprecated_cfg_attr.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/deprecated_cfg_attr.rs
@@ -1,6 +1,6 @@
 use super::{Attribute, DEPRECATED_CFG_ATTR, DEPRECATED_CLIPPY_CFG_ATTR, unnecessary_clippy_cfg};
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
 use rustc_ast::AttrStyle;
 use rustc_errors::Applicability;
 use rustc_lint::EarlyContext;
diff --git a/src/tools/clippy/clippy_lints/src/attrs/mod.rs b/src/tools/clippy/clippy_lints/src/attrs/mod.rs
index c29c9f08cf7..a9766597d50 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/mod.rs
@@ -13,7 +13,7 @@ mod useless_attribute;
 mod utils;
 
 use clippy_config::Conf;
-use clippy_config::msrvs::{self, Msrv};
+use clippy_utils::msrvs::{self, Msrv};
 use rustc_ast::{self as ast, Attribute, MetaItemInner, MetaItemKind};
 use rustc_hir::{ImplItem, Item, TraitItem};
 use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
diff --git a/src/tools/clippy/clippy_lints/src/attrs/should_panic_without_expect.rs b/src/tools/clippy/clippy_lints/src/attrs/should_panic_without_expect.rs
index fadd5272880..97cffeb098e 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/should_panic_without_expect.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/should_panic_without_expect.rs
@@ -9,7 +9,7 @@ use rustc_span::sym;
 
 pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute) {
     if let AttrKind::Normal(normal_attr) = &attr.kind {
-        if let AttrArgs::Eq(_, AttrArgsEq::Ast(_)) = &normal_attr.item.args {
+        if let AttrArgs::Eq { value:  AttrArgsEq::Ast(_), .. } = &normal_attr.item.args {
             // `#[should_panic = ".."]` found, good
             return;
         }
diff --git a/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs
index 6948cf560a5..2eb0566bf9a 100644
--- a/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs
+++ b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs
@@ -1,6 +1,7 @@
 use clippy_config::Conf;
+use clippy_config::types::create_disallowed_map;
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::{create_disallowed_map, match_def_path, paths};
+use clippy_utils::{match_def_path, paths};
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, DefIdMap};
 use rustc_lint::{LateContext, LateLintPass};
diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs
index 896bd5fd03d..6eef0d42a55 100644
--- a/src/tools/clippy/clippy_lints/src/booleans.rs
+++ b/src/tools/clippy/clippy_lints/src/booleans.rs
@@ -1,7 +1,7 @@
 use clippy_config::Conf;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
 use clippy_utils::eq_expr_value;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
 use rustc_ast::ast::LitKind;
diff --git a/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs b/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs
index f2551a05b1a..8892a9e6b6b 100644
--- a/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs
@@ -57,7 +57,7 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef {
             && !addrof_target.span.from_expansion()
             && let ref_ty = cx.typeck_results().expr_ty(deref_target)
             && let ty::Ref(_, inner_ty, Mutability::Not) = ref_ty.kind()
-            && get_parent_expr(cx, e).map_or(true, |parent| {
+            && get_parent_expr(cx, e).is_none_or(|parent| {
                 match parent.kind {
                     // `*&*foo` should lint `deref_addrof` instead.
                     ExprKind::Unary(UnOp::Deref, _) => is_lint_allowed(cx, DEREF_ADDROF, parent.hir_id),
diff --git a/src/tools/clippy/clippy_lints/src/cargo/common_metadata.rs b/src/tools/clippy/clippy_lints/src/cargo/common_metadata.rs
index 6714c053913..80514cb52e6 100644
--- a/src/tools/clippy/clippy_lints/src/cargo/common_metadata.rs
+++ b/src/tools/clippy/clippy_lints/src/cargo/common_metadata.rs
@@ -43,7 +43,7 @@ fn missing_warning(cx: &LateContext<'_>, package: &cargo_metadata::Package, fiel
 }
 
 fn is_empty_str<T: AsRef<std::ffi::OsStr>>(value: Option<&T>) -> bool {
-    value.map_or(true, |s| s.as_ref().is_empty())
+    value.is_none_or(|s| s.as_ref().is_empty())
 }
 
 fn is_empty_vec(value: &[String]) -> bool {
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs b/src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs
index b7b63250864..ae433773193 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_abs_to_unsigned.rs
@@ -1,5 +1,5 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::sugg::Sugg;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs
index 84a44b14dde..4ad39d9160d 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_lossless.rs
@@ -1,6 +1,6 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_in_const_context;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::SpanRangeExt;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::is_isize_or_usize;
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs
index 40a1a9d1ce8..48e9f1d690e 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs
@@ -134,7 +134,7 @@ pub(super) fn check(
             };
             let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
 
-            let cast_from_ptr_size = def.repr().int.map_or(true, |ty| matches!(ty, IntegerType::Pointer(_),));
+            let cast_from_ptr_size = def.repr().int.is_none_or(|ty| matches!(ty, IntegerType::Pointer(_),));
             let suffix = match (cast_from_ptr_size, is_isize_or_usize(cast_to)) {
                 (_, false) if from_nbits > to_nbits => "",
                 (false, true) if from_nbits > 64 => "",
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs b/src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs
index 285f0357112..030c2d322db 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs
@@ -1,5 +1,5 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source;
 use rustc_ast::Mutability;
 use rustc_hir::{Expr, ExprKind, Node};
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 1d89f6c75e1..c3bc5c0c9f2 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,5 +1,5 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_with_context;
 use rustc_errors::Applicability;
 use rustc_hir::def_id::DefId;
diff --git a/src/tools/clippy/clippy_lints/src/casts/mod.rs b/src/tools/clippy/clippy_lints/src/casts/mod.rs
index 3acd4eca420..8b884399f92 100644
--- a/src/tools/clippy/clippy_lints/src/casts/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/mod.rs
@@ -24,8 +24,8 @@ mod utils;
 mod zero_ptr;
 
 use clippy_config::Conf;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::is_hir_ty_cfg_dependant;
+use clippy_utils::msrvs::{self, Msrv};
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
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 46d67e615c7..a138ade54aa 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,5 +1,5 @@
-use clippy_config::msrvs::{self, Msrv};
 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 rustc_errors::Applicability;
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 7518dd2435a..945c05ee943 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,5 +1,5 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::std_or_core;
 use clippy_utils::sugg::Sugg;
 use rustc_errors::Applicability;
diff --git a/src/tools/clippy/clippy_lints/src/checked_conversions.rs b/src/tools/clippy/clippy_lints/src/checked_conversions.rs
index f76e399517c..364f5c7dc7a 100644
--- a/src/tools/clippy/clippy_lints/src/checked_conversions.rs
+++ b/src/tools/clippy/clippy_lints/src/checked_conversions.rs
@@ -1,6 +1,6 @@
 use clippy_config::Conf;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::{SpanlessEq, is_in_const_context, is_integer_literal};
 use rustc_errors::Applicability;
diff --git a/src/tools/clippy/clippy_lints/src/copies.rs b/src/tools/clippy/clippy_lints/src/copies.rs
index 3ecd36d3711..89808d38b9f 100644
--- a/src/tools/clippy/clippy_lints/src/copies.rs
+++ b/src/tools/clippy/clippy_lints/src/copies.rs
@@ -212,7 +212,7 @@ fn lint_if_same_then_else(cx: &LateContext<'_>, conds: &[&Expr<'_>], blocks: &[&
         .array_windows::<2>()
         .enumerate()
         .fold(true, |all_eq, (i, &[lhs, rhs])| {
-            if eq.eq_block(lhs, rhs) && !contains_let(conds[i]) && conds.get(i + 1).map_or(true, |e| !contains_let(e)) {
+            if eq.eq_block(lhs, rhs) && !contains_let(conds[i]) && conds.get(i + 1).is_none_or(|e| !contains_let(e)) {
                 span_lint_and_note(
                     cx,
                     IF_SAME_THEN_ELSE,
@@ -470,7 +470,7 @@ fn scan_block_for_eq<'tcx>(
                 b.stmts
                     // the bounds check will catch the underflow
                     .get(b.stmts.len().wrapping_sub(offset + 1))
-                    .map_or(true, |s| hash != hash_stmt(cx, s))
+                    .is_none_or(|s| hash != hash_stmt(cx, s))
             })
         })
         .map_or(block.stmts.len() - start_end_eq, |(i, _)| i);
diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs
index dff60f76b74..022ed180ed8 100644
--- a/src/tools/clippy/clippy_lints/src/declared_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs
@@ -135,6 +135,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
     crate::disallowed_names::DISALLOWED_NAMES_INFO,
     crate::disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS_INFO,
     crate::disallowed_types::DISALLOWED_TYPES_INFO,
+    crate::doc::DOC_INCLUDE_WITHOUT_CFG_INFO,
     crate::doc::DOC_LAZY_CONTINUATION_INFO,
     crate::doc::DOC_LINK_WITH_QUOTES_INFO,
     crate::doc::DOC_MARKDOWN_INFO,
@@ -743,7 +744,6 @@ pub static LINTS: &[&crate::LintInfo] = &[
     crate::unit_types::LET_UNIT_VALUE_INFO,
     crate::unit_types::UNIT_ARG_INFO,
     crate::unit_types::UNIT_CMP_INFO,
-    crate::unnamed_address::FN_ADDRESS_COMPARISONS_INFO,
     crate::unnecessary_box_returns::UNNECESSARY_BOX_RETURNS_INFO,
     crate::unnecessary_literal_bound::UNNECESSARY_LITERAL_BOUND_INFO,
     crate::unnecessary_map_on_constructor::UNNECESSARY_MAP_ON_CONSTRUCTOR_INFO,
diff --git a/src/tools/clippy/clippy_lints/src/default.rs b/src/tools/clippy/clippy_lints/src/default.rs
index de775b64795..ffdd946aadb 100644
--- a/src/tools/clippy/clippy_lints/src/default.rs
+++ b/src/tools/clippy/clippy_lints/src/default.rs
@@ -5,7 +5,7 @@ use clippy_utils::{contains_name, get_parent_expr, in_automatically_derived, is_
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
-use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind};
+use rustc_hir::{Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind, StructTailExpr};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
 use rustc_middle::ty::print::with_forced_trimmed_paths;
@@ -285,7 +285,7 @@ fn field_reassigned_by_stmt<'tcx>(this: &Stmt<'tcx>, binding_name: Symbol) -> Op
 /// Returns whether `expr` is the update syntax base: `Foo { a: 1, .. base }`
 fn is_update_syntax_base<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
     if let Some(parent) = get_parent_expr(cx, expr)
-        && let ExprKind::Struct(_, _, Some(base)) = parent.kind
+        && let ExprKind::Struct(_, _, StructTailExpr::Base(base)) = parent.kind
     {
         base.hir_id == expr.hir_id
     } else {
diff --git a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs
index ef6b141920d..6819ad547f8 100644
--- a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs
+++ b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs
@@ -4,7 +4,7 @@ use clippy_utils::source::snippet_opt;
 use rustc_ast::ast::{LitFloatType, LitIntType, LitKind};
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{Visitor, walk_expr, walk_stmt};
-use rustc_hir::{Block, Body, ConstContext, Expr, ExprKind, FnRetTy, HirId, Lit, Stmt, StmtKind};
+use rustc_hir::{Block, Body, ConstContext, Expr, ExprKind, FnRetTy, HirId, Lit, Stmt, StmtKind, StructTailExpr};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::{self, FloatTy, IntTy, PolyFnSig, Ty};
@@ -197,7 +197,7 @@ impl<'tcx> Visitor<'tcx> for NumericFallbackVisitor<'_, 'tcx> {
                     }
 
                     // Visit base with no bound.
-                    if let Some(base) = base {
+                    if let StructTailExpr::Base(base) = base {
                         self.ty_bounds.push(ExplicitTyBound(false));
                         self.visit_expr(base);
                         self.ty_bounds.pop();
diff --git a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs
index 77dbe9b78a1..3ea792d8b83 100644
--- a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs
@@ -74,6 +74,8 @@ declare_with_version! { RENAMED(RENAMED_VERSION): &[(&str, &str)] = &[
     #[clippy::version = "1.53.0"]
     ("clippy::filter_map", "clippy::manual_filter_map"),
     #[clippy::version = ""]
+    ("clippy::fn_address_comparisons", "unpredictable_function_pointer_comparisons"),
+    #[clippy::version = ""]
     ("clippy::identity_conversion", "clippy::useless_conversion"),
     #[clippy::version = "pre 1.29.0"]
     ("clippy::if_let_redundant_pattern_matching", "clippy::redundant_pattern_matching"),
diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs
index 1eebfc22af2..c449a1a875b 100644
--- a/src/tools/clippy/clippy_lints/src/dereference.rs
+++ b/src/tools/clippy/clippy_lints/src/dereference.rs
@@ -7,15 +7,15 @@ use clippy_utils::{
     peel_middle_ty_refs,
 };
 use core::mem;
-use rustc_ast::util::parser::{PREC_PREFIX, PREC_UNAMBIGUOUS};
+use rustc_ast::util::parser::ExprPrecedence;
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_errors::Applicability;
+use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::{Visitor, walk_ty};
 use rustc_hir::{
     self as hir, BindingMode, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node, Pat,
     PatKind, Path, QPath, TyKind, UnOp,
 };
-use rustc_hir::def_id::DefId;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
 use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypeckResults};
@@ -453,7 +453,7 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing<'tcx> {
                             ));
                         } else if stability.is_deref_stable()
                             // Auto-deref doesn't combine with other adjustments
-                            && next_adjust.map_or(true, |a| matches!(a.kind, Adjust::Deref(_) | Adjust::Borrow(_)))
+                            && next_adjust.is_none_or(|a| matches!(a.kind, Adjust::Deref(_) | Adjust::Borrow(_)))
                             && iter.all(|a| matches!(a.kind, Adjust::Deref(_) | Adjust::Borrow(_)))
                         {
                             self.state = Some((State::Borrow { mutability }, StateData {
@@ -963,7 +963,7 @@ fn report<'tcx>(
             // expr_str (the suggestion) is never shown if is_final_ufcs is true, since it's
             // `expr.kind == ExprKind::Call`. Therefore, this is, afaik, always unnecessary.
             /*
-            expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence() < PREC_PREFIX {
+            expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence() < ExprPrecedence::Prefix {
                 Cow::Owned(format!("({expr_str})"))
             } else {
                 expr_str
@@ -999,13 +999,13 @@ fn report<'tcx>(
                 data.first_expr.span,
                 state.msg,
                 |diag| {
-                    let (precedence, calls_field) = match cx.tcx.parent_hir_node(data.first_expr.hir_id) {
+                    let needs_paren = match cx.tcx.parent_hir_node(data.first_expr.hir_id) {
                         Node::Expr(e) => match e.kind {
-                            ExprKind::Call(callee, _) if callee.hir_id != data.first_expr.hir_id => (0, false),
-                            ExprKind::Call(..) => (PREC_UNAMBIGUOUS, matches!(expr.kind, ExprKind::Field(..))),
-                            _ => (e.precedence(), false),
+                            ExprKind::Call(callee, _) if callee.hir_id != data.first_expr.hir_id => false,
+                            ExprKind::Call(..) => expr.precedence() < ExprPrecedence::Unambiguous || matches!(expr.kind, ExprKind::Field(..)),
+                            _ => expr.precedence() < e.precedence(),
                         },
-                        _ => (0, false),
+                        _ => false,
                     };
                     let is_in_tuple = matches!(
                         get_parent_expr(cx, data.first_expr),
@@ -1016,7 +1016,7 @@ fn report<'tcx>(
                     );
 
                     let sugg = if !snip_is_macro
-                        && (calls_field || expr.precedence() < precedence)
+                        && needs_paren
                         && !has_enclosing_paren(&snip)
                         && !is_in_tuple
                     {
@@ -1049,16 +1049,16 @@ fn report<'tcx>(
                 }
             }
 
-            let (prefix, precedence) = match mutability {
+            let (prefix, needs_paren) = match mutability {
                 Some(mutability) if !ty.is_ref() => {
                     let prefix = match mutability {
                         Mutability::Not => "&",
                         Mutability::Mut => "&mut ",
                     };
-                    (prefix, PREC_PREFIX)
+                    (prefix, expr.precedence() < ExprPrecedence::Prefix)
                 },
-                None if !ty.is_ref() && data.adjusted_ty.is_ref() => ("&", 0),
-                _ => ("", 0),
+                None if !ty.is_ref() && data.adjusted_ty.is_ref() => ("&", false),
+                _ => ("", false),
             };
             span_lint_hir_and_then(
                 cx,
@@ -1070,12 +1070,11 @@ fn report<'tcx>(
                     let mut app = Applicability::MachineApplicable;
                     let (snip, snip_is_macro) =
                         snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app);
-                    let sugg =
-                        if !snip_is_macro && expr.precedence() < precedence && !has_enclosing_paren(&snip) {
-                            format!("{prefix}({snip})")
-                        } else {
-                            format!("{prefix}{snip}")
-                        };
+                    let sugg = if !snip_is_macro && needs_paren && !has_enclosing_paren(&snip) {
+                        format!("{prefix}({snip})")
+                    } else {
+                        format!("{prefix}{snip}")
+                    };
                     diag.span_suggestion(data.first_expr.span, "try", sugg, app);
                 },
             );
@@ -1158,7 +1157,7 @@ impl<'tcx> Dereferencing<'tcx> {
                         },
                         Some(parent) if !parent.span.from_expansion() => {
                             // Double reference might be needed at this point.
-                            if parent.precedence() == PREC_UNAMBIGUOUS {
+                            if parent.precedence() == ExprPrecedence::Unambiguous {
                                 // Parentheses would be needed here, don't lint.
                                 *outer_pat = None;
                             } else {
diff --git a/src/tools/clippy/clippy_lints/src/derivable_impls.rs b/src/tools/clippy/clippy_lints/src/derivable_impls.rs
index 767dda552bc..9569081ad08 100644
--- a/src/tools/clippy/clippy_lints/src/derivable_impls.rs
+++ b/src/tools/clippy/clippy_lints/src/derivable_impls.rs
@@ -1,6 +1,6 @@
 use clippy_config::Conf;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::indent_of;
 use clippy_utils::{is_default_equivalent, peel_blocks};
 use rustc_errors::Applicability;
@@ -132,17 +132,15 @@ fn check_struct<'tcx>(
 
     if should_emit {
         let struct_span = cx.tcx.def_span(adt_def.did());
+        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
+        ];
+
         span_lint_and_then(cx, DERIVABLE_IMPLS, item.span, "this `impl` can be derived", |diag| {
-            diag.span_suggestion_hidden(
-                item.span,
-                "remove the manual implementation...",
-                String::new(),
-                Applicability::MachineApplicable,
-            );
-            diag.span_suggestion(
-                struct_span.shrink_to_lo(),
-                "...and instead derive it",
-                "#[derive(Default)]\n".to_string(),
+            diag.multipart_suggestion(
+                "replace the manual implementation with a derive attribute",
+                suggestions,
                 Applicability::MachineApplicable,
             );
         });
@@ -161,23 +159,23 @@ fn check_enum<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>, func_expr: &Ex
         let indent_enum = indent_of(cx, enum_span).unwrap_or(0);
         let variant_span = cx.tcx.def_span(variant_def.def_id);
         let indent_variant = indent_of(cx, variant_span).unwrap_or(0);
-        span_lint_and_then(cx, DERIVABLE_IMPLS, item.span, "this `impl` can be derived", |diag| {
-            diag.span_suggestion_hidden(
-                item.span,
-                "remove the manual implementation...",
-                String::new(),
-                Applicability::MachineApplicable,
-            );
-            diag.span_suggestion(
+
+        let suggestions = vec![
+            (item.span, String::new()), // Remove the manual implementation
+            (
                 enum_span.shrink_to_lo(),
-                "...and instead derive it...",
-                format!("#[derive(Default)]\n{indent}", indent = " ".repeat(indent_enum),),
-                Applicability::MachineApplicable,
-            );
-            diag.span_suggestion(
+                format!("#[derive(Default)]\n{}", " ".repeat(indent_enum)),
+            ), // Add the derive attribute
+            (
                 variant_span.shrink_to_lo(),
-                "...and mark the default variant",
-                format!("#[default]\n{indent}", indent = " ".repeat(indent_variant),),
+                format!("#[default]\n{}", " ".repeat(indent_variant)),
+            ), // Mark the default variant
+        ];
+
+        span_lint_and_then(cx, DERIVABLE_IMPLS, item.span, "this `impl` can be derived", |diag| {
+            diag.multipart_suggestion(
+                "replace the manual implementation with a derive attribute and mark the default variant",
+                suggestions,
                 Applicability::MachineApplicable,
             );
         });
diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs
index 1a34b87e42a..8125dab8adf 100644
--- a/src/tools/clippy/clippy_lints/src/derive.rs
+++ b/src/tools/clippy/clippy_lints/src/derive.rs
@@ -505,17 +505,15 @@ fn typing_env_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId
         }
     }
 
-    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)
-            }),
-        )),
-    );
+    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/disallowed_macros.rs b/src/tools/clippy/clippy_lints/src/disallowed_macros.rs
index bdd49bf8aa7..a0cb36f88dc 100644
--- a/src/tools/clippy/clippy_lints/src/disallowed_macros.rs
+++ b/src/tools/clippy/clippy_lints/src/disallowed_macros.rs
@@ -1,5 +1,5 @@
 use clippy_config::Conf;
-use clippy_utils::create_disallowed_map;
+use clippy_config::types::create_disallowed_map;
 use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
 use clippy_utils::macros::macro_backtrace;
 use rustc_data_structures::fx::FxHashSet;
diff --git a/src/tools/clippy/clippy_lints/src/disallowed_methods.rs b/src/tools/clippy/clippy_lints/src/disallowed_methods.rs
index 5a01d76a2a6..1e660b1957a 100644
--- a/src/tools/clippy/clippy_lints/src/disallowed_methods.rs
+++ b/src/tools/clippy/clippy_lints/src/disallowed_methods.rs
@@ -1,5 +1,5 @@
 use clippy_config::Conf;
-use clippy_utils::create_disallowed_map;
+use clippy_config::types::create_disallowed_map;
 use clippy_utils::diagnostics::span_lint_and_then;
 use rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::def_id::DefIdMap;
diff --git a/src/tools/clippy/clippy_lints/src/doc/include_in_doc_without_cfg.rs b/src/tools/clippy/clippy_lints/src/doc/include_in_doc_without_cfg.rs
new file mode 100644
index 00000000000..2182689f985
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/doc/include_in_doc_without_cfg.rs
@@ -0,0 +1,45 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_opt;
+use rustc_ast::{AttrArgs, AttrArgsEq, AttrKind, AttrStyle, Attribute};
+use rustc_errors::Applicability;
+use rustc_lint::LateContext;
+use rustc_span::sym;
+
+use super::DOC_INCLUDE_WITHOUT_CFG;
+
+pub fn check(cx: &LateContext<'_>, attrs: &[Attribute]) {
+    for attr in attrs {
+        if !attr.span.from_expansion()
+            && let AttrKind::Normal(ref normal) = attr.kind
+            && normal.item.path == sym::doc
+            && let AttrArgs::Eq { value: AttrArgsEq::Hir(ref meta), .. } = normal.item.args
+            && !attr.span.contains(meta.span)
+            // Since the `include_str` is already expanded at this point, we can only take the
+            // whole attribute snippet and then modify for our suggestion.
+            && let Some(snippet) = snippet_opt(cx, attr.span)
+            // We cannot remove this because a `#[doc = include_str!("...")]` attribute can occupy
+            // several lines.
+            && let Some(start) = snippet.find('[')
+            && let Some(end) = snippet.rfind(']')
+            && let snippet = &snippet[start + 1..end]
+            // We check that the expansion actually comes from `include_str!` and not just from
+            // another macro.
+            && let Some(sub_snippet) = snippet.trim().strip_prefix("doc")
+            && let Some(sub_snippet) = sub_snippet.trim().strip_prefix("=")
+            && sub_snippet.trim().starts_with("include_str!")
+        {
+            span_lint_and_sugg(
+                cx,
+                DOC_INCLUDE_WITHOUT_CFG,
+                attr.span,
+                "included a file in documentation unconditionally",
+                "use `cfg_attr(doc, doc = \"...\")`",
+                format!(
+                    "#{}[cfg_attr(doc, {snippet})]",
+                    if attr.style == AttrStyle::Inner { "!" } else { "" }
+                ),
+                Applicability::MachineApplicable,
+            );
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs
index df7c37a192a..88ac871acf6 100644
--- a/src/tools/clippy/clippy_lints/src/doc/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs
@@ -1,3 +1,5 @@
+#![allow(clippy::lint_without_lint_pass)]
+
 mod lazy_continuation;
 mod too_long_first_doc_paragraph;
 
@@ -33,6 +35,7 @@ use std::ops::Range;
 use url::Url;
 
 mod empty_line_after;
+mod include_in_doc_without_cfg;
 mod link_with_quotes;
 mod markdown;
 mod missing_headers;
@@ -532,6 +535,35 @@ declare_clippy_lint! {
     "empty line after doc comments"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks if included files in doc comments are included only for `cfg(doc)`.
+    ///
+    /// ### Why restrict this?
+    /// These files are not useful for compilation but will still be included.
+    /// Also, if any of these non-source code file is updated, it will trigger a
+    /// recompilation.
+    ///
+    /// ### Known problems
+    ///
+    /// Excluding this will currently result in the file being left out if
+    /// the item's docs are inlined from another crate. This may be fixed in a
+    /// future version of rustdoc.
+    ///
+    /// ### Example
+    /// ```ignore
+    /// #![doc = include_str!("some_file.md")]
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// #![cfg_attr(doc, doc = include_str!("some_file.md"))]
+    /// ```
+    #[clippy::version = "1.84.0"]
+    pub DOC_INCLUDE_WITHOUT_CFG,
+    restriction,
+    "check if files included in documentation are behind `cfg(doc)`"
+}
+
 pub struct Documentation {
     valid_idents: FxHashSet<String>,
     check_private_items: bool,
@@ -561,6 +593,7 @@ impl_lint_pass!(Documentation => [
     EMPTY_LINE_AFTER_OUTER_ATTR,
     EMPTY_LINE_AFTER_DOC_COMMENTS,
     TOO_LONG_FIRST_DOC_PARAGRAPH,
+    DOC_INCLUDE_WITHOUT_CFG,
 ]);
 
 impl<'tcx> LateLintPass<'tcx> for Documentation {
@@ -690,6 +723,7 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[
         Some(("fake".into(), "fake".into()))
     }
 
+    include_in_doc_without_cfg::check(cx, attrs);
     if suspicious_doc_comments::check(cx, attrs) || empty_line_after::check(cx, attrs) || is_doc_hidden(attrs) {
         return None;
     }
@@ -917,6 +951,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
                 }
                 let trimmed_text = text.trim();
                 headers.safety |= in_heading && trimmed_text == "Safety";
+                headers.safety |= in_heading && trimmed_text == "SAFETY";
                 headers.safety |= in_heading && trimmed_text == "Implementation safety";
                 headers.safety |= in_heading && trimmed_text == "Implementation Safety";
                 headers.errors |= in_heading && trimmed_text == "Errors";
diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs
index 6c87a05ace3..8c22e43349f 100644
--- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs
+++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs
@@ -276,7 +276,7 @@ fn check_inputs(
             && typeck
                 .expr_adjustments(arg)
                 .last()
-                .map_or(true, |a| a.target == typeck.expr_ty(arg))
+                .is_none_or(|a| a.target == typeck.expr_ty(arg))
         })
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/excessive_bools.rs b/src/tools/clippy/clippy_lints/src/excessive_bools.rs
index c88fb50b5af..0011da03dda 100644
--- a/src/tools/clippy/clippy_lints/src/excessive_bools.rs
+++ b/src/tools/clippy/clippy_lints/src/excessive_bools.rs
@@ -165,7 +165,7 @@ impl<'tcx> LateLintPass<'tcx> for ExcessiveBools {
             && fn_header.abi == Abi::Rust
             && fn_decl.inputs.len() as u64 > self.max_fn_params_bools
             && get_parent_as_impl(cx.tcx, cx.tcx.local_def_id_to_hir_id(def_id))
-                .map_or(true, |impl_item| impl_item.of_trait.is_none())
+                .is_none_or(|impl_item| impl_item.of_trait.is_none())
         {
             check_fn_decl(cx, fn_decl, span, self.max_fn_params_bools);
         }
diff --git a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs
index 81152da8c85..ae4b6680065 100644
--- a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs
+++ b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs
@@ -6,7 +6,7 @@ use rustc_errors::Applicability;
 use rustc_hir::intravisit::{Visitor, walk_impl_item, walk_item, walk_param_bound, walk_ty};
 use rustc_hir::{
     BodyId, ExprKind, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, ImplItemKind, Item, ItemKind,
-    PredicateOrigin, Ty, WherePredicate, WherePredicateKind
+    PredicateOrigin, Ty, WherePredicate, WherePredicateKind,
 };
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::hir::nested_filter;
diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs
index 4c043f8dc14..da5825b7ab2 100644
--- a/src/tools/clippy/clippy_lints/src/format_args.rs
+++ b/src/tools/clippy/clippy_lints/src/format_args.rs
@@ -1,6 +1,5 @@
 use arrayvec::ArrayVec;
 use clippy_config::Conf;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::is_diag_trait_item;
 use clippy_utils::macros::{
@@ -8,6 +7,7 @@ use clippy_utils::macros::{
     format_placeholder_format_span, is_assert_macro, is_format_macro, is_panic, matching_root_macro_call,
     root_macro_call_first_node,
 };
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::{implements_trait, is_type_lang_item};
 use itertools::Itertools;
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 14da0b515a5..e43c311eb85 100644
--- a/src/tools/clippy/clippy_lints/src/from_over_into.rs
+++ b/src/tools/clippy/clippy_lints/src/from_over_into.rs
@@ -1,9 +1,9 @@
 use std::ops::ControlFlow;
 
 use clippy_config::Conf;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::macros::span_is_local;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::path_def_id;
 use clippy_utils::source::SpanRangeExt;
 use rustc_errors::Applicability;
@@ -92,7 +92,7 @@ impl<'tcx> LateLintPass<'tcx> for FromOverInto {
                 |diag| {
                     // If the target type is likely foreign mention the orphan rules as it's a common source of
                     // confusion
-                    if path_def_id(cx, target_ty.peel_refs()).map_or(true, |id| !id.is_local()) {
+                    if path_def_id(cx, target_ty.peel_refs()).is_none_or(|id| !id.is_local()) {
                         diag.help(
                             "`impl From<Local> for Foreign` is allowed by the orphan rules, for more information see\n\
                             https://doc.rust-lang.org/reference/items/implementations.html#trait-implementation-coherence"
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 cf08c16458b..bb2dc9995df 100644
--- a/src/tools/clippy/clippy_lints/src/future_not_send.rs
+++ b/src/tools/clippy/clippy_lints/src/future_not_send.rs
@@ -1,3 +1,5 @@
+use std::ops::ControlFlow;
+
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::return_ty;
 use rustc_hir::intravisit::FnKind;
@@ -5,7 +7,9 @@ use rustc_hir::{Body, FnDecl};
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::print::PrintTraitRefExt;
-use rustc_middle::ty::{self, AliasTy, ClauseKind, PredicateKind};
+use rustc_middle::ty::{
+    self, AliasTy, Binder, ClauseKind, PredicateKind, Ty, TyCtxt, TypeVisitable, TypeVisitableExt, TypeVisitor,
+};
 use rustc_session::declare_lint_pass;
 use rustc_span::def_id::LocalDefId;
 use rustc_span::{Span, sym};
@@ -15,9 +19,16 @@ use rustc_trait_selection::traits::{self, FulfillmentError, ObligationCtxt};
 declare_clippy_lint! {
     /// ### What it does
     /// This lint requires Future implementations returned from
-    /// functions and methods to implement the `Send` marker trait. It is mostly
-    /// used by library authors (public and internal) that target an audience where
-    /// multithreaded executors are likely to be used for running these Futures.
+    /// functions and methods to implement the `Send` marker trait,
+    /// ignoring type parameters.
+    ///
+    /// If a function is generic and its Future conditionally implements `Send`
+    /// based on a generic parameter then it is considered `Send` and no warning is emitted.
+    ///
+    /// This can be used by library authors (public and internal) to ensure
+    /// their functions are compatible with both multi-threaded runtimes that require `Send` futures,
+    /// as well as single-threaded runtimes where callers may choose `!Send` types
+    /// for generic parameters.
     ///
     /// ### Why is this bad?
     /// A Future implementation captures some state that it
@@ -64,22 +75,46 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend {
             return;
         }
         let ret_ty = return_ty(cx, cx.tcx.local_def_id_to_hir_id(fn_def_id).expect_owner());
-        if let ty::Alias(ty::Opaque, AliasTy { def_id, args, .. }) = *ret_ty.kind() {
+        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_super_predicates(def_id);
             let is_future = preds.iter_instantiated_copied(cx.tcx, args).any(|(p, _)| {
-                p.as_trait_clause().is_some_and(|trait_pred| {
-                    Some(trait_pred.skip_binder().trait_ref.def_id) == cx.tcx.lang_items().future_trait()
-                })
+                p.as_trait_clause()
+                    .is_some_and(|trait_pred| trait_pred.skip_binder().trait_ref.def_id == future_trait)
             });
             if is_future {
-                let send_trait = cx.tcx.get_diagnostic_item(sym::Send).unwrap();
                 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();
-                if !send_errors.is_empty() {
+
+                // 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)
+                        })
+                });
+
+                if !is_send {
                     span_lint_and_then(
                         cx,
                         FUTURE_NOT_SEND,
@@ -107,3 +142,15 @@ impl<'tcx> LateLintPass<'tcx> for FutureNotSend {
         }
     }
 }
+
+struct TyParamAtTopLevelVisitor;
+impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for TyParamAtTopLevelVisitor {
+    type Result = ControlFlow<bool>;
+    fn visit_ty(&mut self, ty: Ty<'tcx>) -> Self::Result {
+        match ty.kind() {
+            ty::Param(_) => ControlFlow::Break(true),
+            ty::Alias(ty::AliasTyKind::Projection, ty) => ty.visit_with(self),
+            _ => ControlFlow::Break(false),
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs
index ba80c099a01..6444e99ae9c 100644
--- a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs
+++ b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs
@@ -7,6 +7,7 @@ use rustc_errors::Diag;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
+use rustc_span::edition::Edition::Edition2024;
 use rustc_span::sym;
 
 declare_clippy_lint! {
@@ -14,6 +15,12 @@ declare_clippy_lint! {
     /// Checks for `Mutex::lock` calls in `if let` expression
     /// with lock calls in any of the else blocks.
     ///
+    /// ### Disabled starting in Edition 2024
+    /// This lint is effectively disabled starting in
+    /// Edition 2024 as `if let ... else` scoping was reworked
+    /// such that this is no longer an issue. See
+    /// [Proposal: stabilize if_let_rescope for Edition 2024](https://github.com/rust-lang/rust/issues/131154)
+    ///
     /// ### Why is this bad?
     /// The Mutex lock remains held for the whole
     /// `if let ... else` block and deadlocks.
@@ -45,6 +52,10 @@ declare_lint_pass!(IfLetMutex => [IF_LET_MUTEX]);
 
 impl<'tcx> LateLintPass<'tcx> for IfLetMutex {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
+        if cx.tcx.sess.edition() >= Edition2024 {
+            return;
+        }
+
         if let Some(higher::IfLet {
             let_expr,
             if_then,
@@ -86,7 +97,7 @@ fn mutex_lock_call<'tcx>(
         && path.ident.as_str() == "lock"
         && let ty = cx.typeck_results().expr_ty(self_arg).peel_refs()
         && is_type_diagnostic_item(cx, ty, sym::Mutex)
-        && op_mutex.map_or(true, |op| eq_expr_value(cx, self_arg, op))
+        && op_mutex.is_none_or(|op| eq_expr_value(cx, self_arg, op))
     {
         ControlFlow::Break(self_arg)
     } else {
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 d63c18c0eda..3fc0a696522 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
@@ -1,7 +1,7 @@
 use clippy_config::Conf;
-use clippy_config::msrvs::{self, Msrv};
 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::sugg::Sugg;
 use clippy_utils::{
diff --git a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs
index 3b84b569c3e..37481dc7feb 100644
--- a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs
+++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs
@@ -1,6 +1,6 @@
 use clippy_config::Conf;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_opt;
 use clippy_utils::{
     SpanlessEq, higher, is_in_const_context, is_integer_literal, path_to_local, peel_blocks, peel_blocks_with_stmt,
diff --git a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs
index 0b3a6ee1bea..f3467adacc5 100644
--- a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs
+++ b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs
@@ -1,7 +1,7 @@
 use clippy_config::Conf;
-use clippy_config::msrvs::Msrv;
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::is_in_test;
+use clippy_utils::msrvs::Msrv;
 use rustc_attr::{StabilityLevel, StableSince};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::{Expr, ExprKind, HirId};
diff --git a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs
index d386bfca6ba..4fcd2abb769 100644
--- a/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs
+++ b/src/tools/clippy/clippy_lints/src/inconsistent_struct_constructor.rs
@@ -3,7 +3,7 @@ use clippy_utils::fulfill_or_allowed;
 use clippy_utils::source::snippet;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::Applicability;
-use rustc_hir::{self as hir, ExprKind};
+use rustc_hir::{self as hir, ExprKind, StructTailExpr};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
 use rustc_span::symbol::Symbol;
@@ -95,7 +95,7 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor {
             }
             fields_snippet.push_str(&last_ident.to_string());
 
-            let base_snippet = if let Some(base) = base {
+            let base_snippet = if let StructTailExpr::Base(base) = base {
                 format!(", ..{}", snippet(cx, base.span, ".."))
             } else {
                 String::new()
diff --git a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs
index 96550c4d1cb..c2030a5ab09 100644
--- a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs
+++ b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs
@@ -1,8 +1,8 @@
 use clippy_config::Conf;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::higher::IfLet;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::ty::is_copy;
 use clippy_utils::{is_expn_of, is_lint_allowed, path_to_local};
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
diff --git a/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs b/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs
index 7f183bb601e..7a14bbfb9e8 100644
--- a/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs
+++ b/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::{Expr, ExprKind};
+use rustc_hir::{Expr, ExprKind, StructTailExpr};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
 use rustc_span::SyntaxContext;
@@ -43,7 +43,7 @@ declare_lint_pass!(NumberedFields => [INIT_NUMBERED_FIELDS]);
 
 impl<'tcx> LateLintPass<'tcx> for NumberedFields {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
-        if let ExprKind::Struct(path, fields @ [field, ..], None) = e.kind
+        if let ExprKind::Struct(path, fields @ [field, ..], StructTailExpr::None) = e.kind
             // If the first character of any field is a digit it has to be a tuple.
             && field.ident.as_str().as_bytes().first().is_some_and(u8::is_ascii_digit)
             // Type aliases can't be used as functions.
diff --git a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs
index 66931a7f98c..f4e41dc826b 100644
--- a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs
+++ b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs
@@ -1,6 +1,6 @@
 use clippy_config::Conf;
-use clippy_config::msrvs::{self, Msrv};
 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;
diff --git a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs
index 644365c9fe5..623b6b4fcc1 100644
--- a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs
+++ b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs
@@ -3,8 +3,8 @@ use clippy_utils::diagnostics::span_lint_and_then;
 use rustc_errors::Applicability;
 use rustc_hir::{Item, ItemKind};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::layout::LayoutOf;
 use rustc_middle::ty;
+use rustc_middle::ty::layout::LayoutOf;
 use rustc_session::impl_lint_pass;
 use rustc_span::{BytePos, Pos, Span};
 
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 4f22931a4de..b38e362410e 100644
--- a/src/tools/clippy/clippy_lints/src/large_include_file.rs
+++ b/src/tools/clippy/clippy_lints/src/large_include_file.rs
@@ -96,7 +96,7 @@ impl LateLintPass<'_> for LargeIncludeFile {
             && let AttrKind::Normal(ref normal) = attr.kind
             && let Some(doc) = attr.doc_str()
             && doc.as_str().len() as u64 > self.max_file_size
-            && let AttrArgs::Eq(_, AttrArgsEq::Hir(ref meta)) = normal.item.args
+            && let AttrArgs::Eq { value: AttrArgsEq::Hir(ref meta), .. } = normal.item.args
             && !attr.span.contains(meta.span)
             // Since the `include_str` is already expanded at this point, we can only take the
             // whole attribute snippet and then modify for our suggestion.
diff --git a/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs b/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs
index e17d6213679..fb46bdcab6e 100644
--- a/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs
+++ b/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs
@@ -1,6 +1,6 @@
 use clippy_config::Conf;
-use clippy_config::msrvs::{Msrv, NUMERIC_ASSOCIATED_CONSTANTS};
 use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::{get_parent_expr, is_from_proc_macro};
 use hir::def_id::DefId;
 use rustc_errors::Applicability;
@@ -53,7 +53,7 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants {
         // Integer modules are "TBD" deprecated, and the contents are too,
         // so lint on the `use` statement directly.
         if let ItemKind::Use(path, kind @ (UseKind::Single | UseKind::Glob)) = item.kind
-            && self.msrv.meets(NUMERIC_ASSOCIATED_CONSTANTS)
+            && self.msrv.meets(msrvs::NUMERIC_ASSOCIATED_CONSTANTS)
             && !in_external_macro(cx.sess(), item.span)
             && let Some(def_id) = path.res[0].opt_def_id()
         {
@@ -138,7 +138,7 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants {
             return;
         };
 
-        if self.msrv.meets(NUMERIC_ASSOCIATED_CONSTANTS)
+        if self.msrv.meets(msrvs::NUMERIC_ASSOCIATED_CONSTANTS)
             && !in_external_macro(cx.sess(), expr.span)
             && !is_from_proc_macro(cx, expr)
         {
diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs
index c9064df25ac..e80cca6e7db 100644
--- a/src/tools/clippy/clippy_lints/src/lib.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.rs
@@ -363,7 +363,6 @@ mod uninhabited_references;
 mod uninit_vec;
 mod unit_return_expecting_ord;
 mod unit_types;
-mod unnamed_address;
 mod unnecessary_box_returns;
 mod unnecessary_literal_bound;
 mod unnecessary_map_on_constructor;
@@ -786,7 +785,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
     store.register_early_pass(|| Box::new(option_env_unwrap::OptionEnvUnwrap));
     store.register_late_pass(move |_| Box::new(wildcard_imports::WildcardImports::new(conf)));
     store.register_late_pass(|_| Box::<redundant_pub_crate::RedundantPubCrate>::default());
-    store.register_late_pass(|_| Box::new(unnamed_address::UnnamedAddress));
     store.register_late_pass(|_| Box::<dereference::Dereferencing<'_>>::default());
     store.register_late_pass(|_| Box::new(option_if_let_else::OptionIfLetElse));
     store.register_late_pass(|_| Box::new(future_not_send::FutureNotSend));
diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs
index 6ff1a1e5ec7..35b14776e59 100644
--- a/src/tools/clippy/clippy_lints/src/lifetimes.rs
+++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs
@@ -1,6 +1,7 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
 use clippy_utils::trait_ref_of_method;
 use itertools::Itertools;
+use rustc_ast::visit::{try_visit, walk_list};
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_errors::Applicability;
 use rustc_hir::FnRetTy::Return;
@@ -11,8 +12,9 @@ use rustc_hir::intravisit::{
 };
 use rustc_hir::{
     BareFnTy, BodyId, FnDecl, FnSig, GenericArg, GenericArgs, GenericBound, GenericParam, GenericParamKind, Generics,
-    Impl, ImplItem, ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, Node, PolyTraitRef,
-    PredicateOrigin, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, WherePredicateKind, lang_items,
+    HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, Node, PolyTraitRef,
+    PredicateOrigin, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WhereBoundPredicate, WherePredicate,
+    WherePredicateKind, lang_items,
 };
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::hir::map::Map;
@@ -483,6 +485,7 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_
 struct Usage {
     lifetime: Lifetime,
     in_where_predicate: bool,
+    in_bounded_ty: bool,
     in_generics_arg: bool,
 }
 
@@ -490,11 +493,15 @@ struct LifetimeChecker<'cx, 'tcx, F> {
     cx: &'cx LateContext<'tcx>,
     map: FxIndexMap<LocalDefId, Vec<Usage>>,
     where_predicate_depth: usize,
+    bounded_ty_depth: usize,
     generic_args_depth: usize,
     phantom: std::marker::PhantomData<F>,
 }
 
-impl<'cx, 'tcx, F> LifetimeChecker<'cx, 'tcx, F> {
+impl<'cx, 'tcx, F> LifetimeChecker<'cx, 'tcx, F>
+where
+    F: NestedFilter<'tcx>,
+{
     fn new(cx: &'cx LateContext<'tcx>, generics: &'tcx Generics<'_>) -> LifetimeChecker<'cx, 'tcx, F> {
         let map = generics
             .params
@@ -510,10 +517,30 @@ impl<'cx, 'tcx, F> LifetimeChecker<'cx, 'tcx, F> {
             cx,
             map,
             where_predicate_depth: 0,
+            bounded_ty_depth: 0,
             generic_args_depth: 0,
             phantom: std::marker::PhantomData,
         }
     }
+
+    // `visit_where_bound_predicate` is based on:
+    // https://github.com/rust-lang/rust/blob/864cee3ea383cc8254ba394ba355e648faa9cfa5/compiler/rustc_hir/src/intravisit.rs#L936-L939
+    fn visit_where_bound_predicate(
+        &mut self,
+        hir_id: HirId,
+        bounded_ty: &'tcx Ty<'tcx>,
+        bounds: &'tcx [GenericBound<'tcx>],
+        bound_generic_params: &'tcx [GenericParam<'tcx>],
+    ) {
+        try_visit!(self.visit_id(hir_id));
+
+        self.bounded_ty_depth += 1;
+        try_visit!(self.visit_ty(bounded_ty));
+        self.bounded_ty_depth -= 1;
+
+        walk_list!(self, visit_param_bound, bounds);
+        walk_list!(self, visit_generic_param, bound_generic_params);
+    }
 }
 
 impl<'tcx, F> Visitor<'tcx> for LifetimeChecker<'_, 'tcx, F>
@@ -531,6 +558,7 @@ where
             usages.push(Usage {
                 lifetime: *lifetime,
                 in_where_predicate: self.where_predicate_depth != 0,
+                in_bounded_ty: self.bounded_ty_depth != 0,
                 in_generics_arg: self.generic_args_depth != 0,
             });
         }
@@ -538,7 +566,17 @@ where
 
     fn visit_where_predicate(&mut self, predicate: &'tcx WherePredicate<'tcx>) {
         self.where_predicate_depth += 1;
-        walk_where_predicate(self, predicate);
+        if let &WherePredicateKind::BoundPredicate(WhereBoundPredicate {
+            bounded_ty,
+            bounds,
+            bound_generic_params,
+            origin: _,
+        }) = predicate.kind
+        {
+            self.visit_where_bound_predicate(predicate.hir_id, bounded_ty, bounds, bound_generic_params);
+        } else {
+            walk_where_predicate(self, predicate);
+        }
         self.where_predicate_depth -= 1;
     }
 
@@ -562,7 +600,7 @@ fn report_extra_lifetimes<'tcx>(cx: &LateContext<'tcx>, func: &'tcx FnDecl<'_>,
     for (def_id, usages) in checker.map {
         if usages
             .iter()
-            .all(|usage| usage.in_where_predicate && !usage.in_generics_arg)
+            .all(|usage| usage.in_where_predicate && !usage.in_bounded_ty && !usage.in_generics_arg)
         {
             span_lint(
                 cx,
@@ -589,7 +627,7 @@ fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<'
     for (&def_id, usages) in &checker.map {
         if usages
             .iter()
-            .all(|usage| usage.in_where_predicate && !usage.in_generics_arg)
+            .all(|usage| usage.in_where_predicate && !usage.in_bounded_ty && !usage.in_generics_arg)
         {
             span_lint(
                 cx,
@@ -605,8 +643,8 @@ fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<'
 
 // An `impl` lifetime is elidable if it satisfies the following conditions:
 // - It is used exactly once.
-// - That single use is not in `GenericArgs` in a `WherePredicate`. (Note that `GenericArgs` are
-//   different from `GenericParam`s.)
+// - That single use is not in a bounded type or `GenericArgs` in a `WherePredicate`. (Note that
+//   `GenericArgs` are different from `GenericParam`s.)
 fn report_elidable_impl_lifetimes<'tcx>(
     cx: &LateContext<'tcx>,
     impl_: &'tcx Impl<'_>,
@@ -623,6 +661,7 @@ fn report_elidable_impl_lifetimes<'tcx>(
                 }
                 | Usage {
                     lifetime,
+                    in_bounded_ty: false,
                     in_generics_arg: false,
                     ..
                 },
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 d999e1a0585..06cf901bfb2 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
@@ -1,6 +1,6 @@
 use super::EXPLICIT_ITER_LOOP;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::{
     implements_trait, implements_trait_with_env, is_copy, is_type_lang_item, make_normalized_projection,
@@ -29,7 +29,7 @@ pub(super) fn check(
             if !msrv.meets(msrvs::ARRAY_INTO_ITERATOR) {
                 return;
             }
-        } else if count.try_to_target_usize(cx.tcx).map_or(true, |x| x > 32) && !msrv.meets(msrvs::ARRAY_IMPL_ANY_LEN) {
+        } else if count.try_to_target_usize(cx.tcx).is_none_or(|x| x > 32) && !msrv.meets(msrvs::ARRAY_IMPL_ANY_LEN) {
             return;
         }
     }
@@ -181,7 +181,8 @@ fn is_ref_iterable<'tcx>(
             // Attempt to borrow
             let self_ty = Ty::new_ref(cx.tcx, cx.tcx.lifetimes.re_erased, self_ty, mutbl);
             if implements_trait(cx, self_ty, trait_id, &[])
-                && let Some(ty) = make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym!(IntoIter), [self_ty])
+                && let Some(ty) =
+                    make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym!(IntoIter), [self_ty])
                 && ty == res_ty
             {
                 return Some((AdjustKind::borrow(mutbl), self_ty));
diff --git a/src/tools/clippy/clippy_lints/src/loops/mod.rs b/src/tools/clippy/clippy_lints/src/loops/mod.rs
index 17215621d2a..f3ca4a4a571 100644
--- a/src/tools/clippy/clippy_lints/src/loops/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/mod.rs
@@ -23,8 +23,8 @@ mod while_let_loop;
 mod while_let_on_iterator;
 
 use clippy_config::Conf;
-use clippy_config::msrvs::Msrv;
 use clippy_utils::higher;
+use clippy_utils::msrvs::Msrv;
 use rustc_ast::Label;
 use rustc_hir::{Expr, ExprKind, LoopSource, Pat};
 use rustc_lint::{LateContext, LateLintPass};
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 1c55e3e22e8..ed9879de13b 100644
--- a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs
@@ -5,7 +5,7 @@ use clippy_utils::higher::ForLoop;
 use clippy_utils::macros::root_macro_call_first_node;
 use clippy_utils::source::snippet;
 use rustc_errors::Applicability;
-use rustc_hir::{Block, Destination, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind};
+use rustc_hir::{Block, Destination, Expr, ExprKind, HirId, InlineAsmOperand, Pat, Stmt, StmtKind, StructTailExpr};
 use rustc_lint::LateContext;
 use rustc_span::{Span, sym};
 use std::iter::once;
@@ -164,7 +164,7 @@ fn never_loop_expr<'tcx>(
         },
         ExprKind::Struct(_, fields, base) => {
             let fields = never_loop_expr_all(cx, fields.iter().map(|f| f.expr), local_labels, main_loop_id);
-            if let Some(base) = base {
+            if let StructTailExpr::Base(base) = base {
                 combine_seq(fields, || never_loop_expr(cx, base, local_labels, main_loop_id))
             } else {
                 fields
diff --git a/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs b/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs
index 35dc8e9aa4e..12719c4f94b 100644
--- a/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs
@@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::{indent_of, snippet, snippet_with_applicability};
 use clippy_utils::visitors::contains_break_or_continue;
 use rustc_ast::Mutability;
-use rustc_ast::util::parser::PREC_PREFIX;
+use rustc_ast::util::parser::ExprPrecedence;
 use rustc_errors::Applicability;
 use rustc_hir::{BorrowKind, Expr, ExprKind, Pat, PatKind, is_range_literal};
 use rustc_lint::LateContext;
@@ -84,7 +84,7 @@ pub(super) fn check<'tcx>(
         if !prefix.is_empty()
             && (
                 // Precedence of internal expression is less than or equal to precedence of `&expr`.
-                arg_expression.precedence() <= PREC_PREFIX || is_range_literal(arg_expression)
+                arg_expression.precedence() <= ExprPrecedence::Prefix || is_range_literal(arg_expression)
             )
         {
             arg_snip = format!("({arg_snip})").into();
diff --git a/src/tools/clippy/clippy_lints/src/manual_bits.rs b/src/tools/clippy/clippy_lints/src/manual_bits.rs
index fd71167f814..c31656f8a05 100644
--- a/src/tools/clippy/clippy_lints/src/manual_bits.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_bits.rs
@@ -1,7 +1,7 @@
 use clippy_config::Conf;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::get_parent_expr;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_with_context;
 use rustc_ast::ast::LitKind;
 use rustc_data_structures::packed::Pu128;
diff --git a/src/tools/clippy/clippy_lints/src/manual_clamp.rs b/src/tools/clippy/clippy_lints/src/manual_clamp.rs
index 016ec7320a6..484a7ba256b 100644
--- a/src/tools/clippy/clippy_lints/src/manual_clamp.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_clamp.rs
@@ -1,8 +1,8 @@
 use clippy_config::Conf;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
 use clippy_utils::higher::If;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::implements_trait;
 use clippy_utils::visitors::is_const_evaluatable;
diff --git a/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs b/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs
index 4c171e6d890..bbb89bee835 100644
--- a/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_div_ceil.rs
@@ -1,6 +1,6 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::SpanlessEq;
 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 rustc_ast::{BinOpKind, LitKind};
@@ -35,7 +35,7 @@ declare_clippy_lint! {
     /// let y: i32 = 4;
     /// let div = x.div_ceil(y);
     /// ```
-    #[clippy::version = "1.81.0"]
+    #[clippy::version = "1.83.0"]
     pub MANUAL_DIV_CEIL,
     complexity,
     "manually reimplementing `div_ceil`"
diff --git a/src/tools/clippy/clippy_lints/src/manual_float_methods.rs b/src/tools/clippy/clippy_lints/src/manual_float_methods.rs
index a269ea11397..b12f575e81a 100644
--- a/src/tools/clippy/clippy_lints/src/manual_float_methods.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_float_methods.rs
@@ -1,7 +1,7 @@
-use clippy_config::msrvs::Msrv;
-use clippy_config::{Conf, msrvs};
+use clippy_config::Conf;
 use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::SpanRangeExt;
 use clippy_utils::{is_from_proc_macro, path_to_local};
 use rustc_errors::Applicability;
diff --git a/src/tools/clippy/clippy_lints/src/manual_hash_one.rs b/src/tools/clippy/clippy_lints/src/manual_hash_one.rs
index 7a9c9963742..7e092d11f1b 100644
--- a/src/tools/clippy/clippy_lints/src/manual_hash_one.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_hash_one.rs
@@ -1,6 +1,6 @@
 use clippy_config::Conf;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_hir_and_then;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::SpanRangeExt;
 use clippy_utils::visitors::{is_local_used, local_used_once};
 use clippy_utils::{is_trait_method, path_to_local_id};
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 dec8c5d85de..3f01f3cf30a 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
@@ -1,7 +1,7 @@
 use clippy_config::Conf;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::macros::matching_root_macro_call;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::sugg::Sugg;
 use clippy_utils::{higher, is_in_const_context, path_to_local, peel_ref_operators};
 use rustc_ast::LitKind::{Byte, Char};
diff --git a/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs b/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs
index a11d3e4624c..4fee3bf7aa9 100644
--- a/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_is_power_of_two.rs
@@ -27,7 +27,7 @@ declare_clippy_lint! {
     /// let a: u32 = 4;
     /// let result = a.is_power_of_two();
     /// ```
-    #[clippy::version = "1.82.0"]
+    #[clippy::version = "1.83.0"]
     pub MANUAL_IS_POWER_OF_TWO,
     pedantic,
     "manually reimplementing `is_power_of_two`"
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 17185df5d76..a70955a7c78 100644
--- a/src/tools/clippy/clippy_lints/src/manual_let_else.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_let_else.rs
@@ -1,11 +1,10 @@
 use crate::question_mark::{QUESTION_MARK, QuestionMark};
-use clippy_config::msrvs;
 use clippy_config::types::MatchLintBehaviour;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::higher::IfLetOrMatch;
 use clippy_utils::source::snippet_with_context;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{is_lint_allowed, is_never_expr, pat_and_expr_can_be_question_mark, peel_blocks};
+use clippy_utils::{is_lint_allowed, is_never_expr, msrvs, pat_and_expr_can_be_question_mark, peel_blocks};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, QPath, Stmt, StmtKind};
diff --git a/src/tools/clippy/clippy_lints/src/manual_main_separator_str.rs b/src/tools/clippy/clippy_lints/src/manual_main_separator_str.rs
index 85e99a92cf5..b7563a2508d 100644
--- a/src/tools/clippy/clippy_lints/src/manual_main_separator_str.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_main_separator_str.rs
@@ -1,6 +1,6 @@
 use clippy_config::Conf;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::{is_trait_method, peel_hir_expr_refs};
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
diff --git a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
index 25868ccae40..00800231fe4 100644
--- a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
@@ -1,7 +1,7 @@
 use clippy_config::Conf;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
 use clippy_utils::is_doc_hidden;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_indent;
 use itertools::Itertools;
 use rustc_ast::attr;
diff --git a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs
index 86293169ea2..5e58054a986 100644
--- a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs
@@ -1,7 +1,7 @@
 use clippy_config::Conf;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::consts::{ConstEvalCtxt, FullInt};
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_with_context;
 use clippy_utils::{is_in_const_context, path_to_local};
 use rustc_errors::Applicability;
diff --git a/src/tools/clippy/clippy_lints/src/manual_retain.rs b/src/tools/clippy/clippy_lints/src/manual_retain.rs
index a60163be770..708980ac503 100644
--- a/src/tools/clippy/clippy_lints/src/manual_retain.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_retain.rs
@@ -1,7 +1,7 @@
 use clippy_config::Conf;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::SpanlessEq;
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet;
 use clippy_utils::ty::{get_type_diagnostic_name, is_type_lang_item};
 use rustc_errors::Applicability;
diff --git a/src/tools/clippy/clippy_lints/src/manual_strip.rs b/src/tools/clippy/clippy_lints/src/manual_strip.rs
index 3f401eff6bd..79de41db343 100644
--- a/src/tools/clippy/clippy_lints/src/manual_strip.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_strip.rs
@@ -1,7 +1,7 @@
 use clippy_config::Conf;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet;
 use clippy_utils::usage::mutated_variables;
 use clippy_utils::{eq_expr_value, higher};
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 50e6dfc6298..99a7b8c74be 100644
--- a/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/collapsible_match.rs
@@ -1,6 +1,6 @@
-use clippy_config::msrvs::Msrv;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::higher::IfLetOrMatch;
+use clippy_utils::msrvs::Msrv;
 use clippy_utils::source::snippet;
 use clippy_utils::visitors::is_local_used;
 use clippy_utils::{
@@ -72,14 +72,13 @@ fn check_arm<'tcx>(
             (Some(a), Some(b)) => SpanlessEq::new(cx).eq_expr(a, b),
         }
         // the binding must not be used in the if guard
-        && outer_guard.map_or(
-            true,
+        && outer_guard.is_none_or(
             |e| !is_local_used(cx, e, binding_id)
         )
         // ...or anywhere in the inner expression
         && match inner {
             IfLetOrMatch::IfLet(_, _, body, els, _) => {
-                !is_local_used(cx, body, binding_id) && els.map_or(true, |e| !is_local_used(cx, e, binding_id))
+                !is_local_used(cx, body, binding_id) && els.is_none_or(|e| !is_local_used(cx, e, binding_id))
             },
             IfLetOrMatch::Match(_, arms, ..) => !arms.iter().any(|arm| is_local_used(cx, arm, binding_id)),
         }
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 9c6df4d8ac0..bac5cf88cfb 100644
--- a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs
@@ -7,7 +7,7 @@ use clippy_utils::{
     CaptureKind, can_move_expr_to_closure, 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,
 };
-use rustc_ast::util::parser::PREC_UNAMBIGUOUS;
+use rustc_ast::util::parser::ExprPrecedence;
 use rustc_errors::Applicability;
 use rustc_hir::LangItem::{OptionNone, OptionSome};
 use rustc_hir::def::Res;
@@ -117,7 +117,7 @@ where
     // it's being passed by value.
     let scrutinee = peel_hir_expr_refs(scrutinee).0;
     let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app);
-    let scrutinee_str = if scrutinee.span.eq_ctxt(expr.span) && scrutinee.precedence() < PREC_UNAMBIGUOUS {
+    let scrutinee_str = if scrutinee.span.eq_ctxt(expr.span) && scrutinee.precedence() < ExprPrecedence::Unambiguous {
         format!("({scrutinee_str})")
     } else {
         scrutinee_str.into()
diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs
index 28adcc2f227..64969271764 100644
--- a/src/tools/clippy/clippy_lints/src/matches/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs
@@ -25,7 +25,7 @@ mod try_err;
 mod wild_in_or_pats;
 
 use clippy_config::Conf;
-use clippy_config::msrvs::{self, Msrv};
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::walk_span_to_context;
 use clippy_utils::{higher, is_direct_expn_of, is_in_const_context, is_span_match, span_contains_cfg};
 use rustc_hir::{Arm, Expr, ExprKind, LetStmt, MatchSource, Pat, PatKind};
diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs
index 9e54475033c..dfc0513add9 100644
--- a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs
@@ -1,6 +1,6 @@
-use clippy_config::msrvs::Msrv;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::macros::matching_root_macro_call;
+use clippy_utils::msrvs::Msrv;
 use clippy_utils::source::snippet;
 use clippy_utils::visitors::{for_each_expr_without_closures, is_local_used};
 use clippy_utils::{is_in_const_context, path_to_local};
@@ -243,11 +243,6 @@ fn emit_redundant_guards<'tcx>(
 }
 
 /// Checks if the given `Expr` can also be represented as a `Pat`.
-///
-/// All literals generally also work as patterns, however float literals are special.
-/// They are currently (as of 2023/08/08) still allowed in patterns, but that will become
-/// an error in the future, and rustc already actively warns against this (see rust#41620),
-/// so we don't consider those as usable within patterns for linting purposes.
 fn expr_can_be_pat(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     for_each_expr_without_closures(expr, |expr| {
         if match expr.kind {
@@ -267,7 +262,7 @@ fn expr_can_be_pat(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
             | ExprKind::Tup(..)
             | ExprKind::Struct(..)
             | ExprKind::Unary(UnOp::Neg, _) => true,
-            ExprKind::Lit(lit) if !matches!(lit.node, LitKind::Float(..)) => true,
+            ExprKind::Lit(lit) if !matches!(lit.node, LitKind::CStr(..)) => true,
             _ => false,
         } {
             return ControlFlow::Continue(());
diff --git a/src/tools/clippy/clippy_lints/src/mem_replace.rs b/src/tools/clippy/clippy_lints/src/mem_replace.rs
index 146748734cf..5597cd85abc 100644
--- a/src/tools/clippy/clippy_lints/src/mem_replace.rs
+++ b/src/tools/clippy/clippy_lints/src/mem_replace.rs
@@ -1,6 +1,6 @@
 use clippy_config::Conf;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::{snippet, snippet_with_applicability};
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::is_non_aggregate_primitive_type;
diff --git a/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs b/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs
index c9604c7b2e2..1ee27d90d05 100644
--- a/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/clone_on_copy.rs
@@ -30,7 +30,7 @@ pub(super) fn check(
         .type_dependent_def_id(expr.hir_id)
         .and_then(|id| cx.tcx.trait_of_item(id))
         .zip(cx.tcx.lang_items().clone_trait())
-        .map_or(true, |(x, y)| x != y)
+        .is_none_or(|(x, y)| x != y)
     {
         return;
     }
diff --git a/src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs b/src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs
index fa04f74eec1..2a0a9d3710d 100644
--- a/src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/cloned_instead_of_copied.rs
@@ -1,6 +1,6 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::is_trait_method;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::ty::{get_iterator_item_ty, is_copy};
 use rustc_errors::Applicability;
 use rustc_hir::Expr;
diff --git a/src/tools/clippy/clippy_lints/src/methods/err_expect.rs b/src/tools/clippy/clippy_lints/src/methods/err_expect.rs
index 3b1adb16b80..44b55570eea 100644
--- a/src/tools/clippy/clippy_lints/src/methods/err_expect.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/err_expect.rs
@@ -1,6 +1,6 @@
 use super::ERR_EXPECT;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::ty::{has_debug_impl, is_type_diagnostic_item};
 use rustc_errors::Applicability;
 use rustc_lint::LateContext;
diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map_next.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map_next.rs
index f94fe221833..3f89e593148 100644
--- a/src/tools/clippy/clippy_lints/src/methods/filter_map_next.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/filter_map_next.rs
@@ -1,6 +1,6 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
 use clippy_utils::is_trait_method;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
diff --git a/src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs b/src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs
index 40b48ccca5d..d8bb9e377a0 100644
--- a/src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/is_digit_ascii_radix.rs
@@ -1,7 +1,7 @@
 use super::IS_DIGIT_ASCII_RADIX;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::consts::{ConstEvalCtxt, FullInt};
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_with_applicability;
 use rustc_errors::Applicability;
 use rustc_hir::Expr;
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs b/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs
index 390dd24b505..299f6d10112 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs
@@ -1,6 +1,6 @@
 use super::ITER_KV_MAP;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::pat_is_wild;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::is_type_diagnostic_item;
diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs b/src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs
index 22f4748de70..7d5ebdedd0c 100644
--- a/src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs
@@ -1,6 +1,6 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::get_parent_expr;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet;
 use rustc_ast::{LitKind, StrStyle};
 use rustc_errors::Applicability;
diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs b/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs
index 7aa13d8d5b6..20e4d233525 100644
--- a/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs
@@ -1,5 +1,5 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::{IntoSpan, SpanRangeExt};
 use clippy_utils::ty::get_field_by_name;
 use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures};
@@ -148,7 +148,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name:
                                 _ => {},
                             }
                         }
-                        requires_copy |= !ty.is_copy_modulo_regions(cx.tcx, cx.typing_env());
+                        requires_copy |= !cx.type_is_copy_modulo_regions(ty);
                         break;
                     }
                 },
@@ -158,7 +158,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name:
         }
 
         if can_lint
-            && (!requires_copy || arg_ty.is_copy_modulo_regions(cx.tcx, cx.typing_env()))
+            && (!requires_copy || cx.type_is_copy_modulo_regions(arg_ty))
             // This case could be handled, but a fair bit of care would need to be taken.
             && (!requires_deref || arg_ty.is_freeze(cx.tcx, cx.typing_env()))
         {
diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_is_variant_and.rs b/src/tools/clippy/clippy_lints/src/methods/manual_is_variant_and.rs
index c377abd6237..90e502f244f 100644
--- a/src/tools/clippy/clippy_lints/src/methods/manual_is_variant_and.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/manual_is_variant_and.rs
@@ -1,5 +1,5 @@
-use clippy_config::msrvs::{Msrv, OPTION_RESULT_IS_VARIANT_AND};
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet;
 use clippy_utils::ty::is_type_diagnostic_item;
 use rustc_errors::Applicability;
@@ -36,7 +36,7 @@ pub(super) fn check<'tcx>(
     }
 
     // 4. msrv doesn't meet `OPTION_RESULT_IS_VARIANT_AND`
-    if !msrv.meets(OPTION_RESULT_IS_VARIANT_AND) {
+    if !msrv.meets(msrvs::OPTION_RESULT_IS_VARIANT_AND) {
         return;
     }
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs b/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs
index 31449d41770..4a48d4b547c 100644
--- a/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/manual_try_fold.rs
@@ -1,5 +1,5 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::implements_trait;
 use clippy_utils::{is_from_proc_macro, is_trait_method};
diff --git a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs
index d5594b21db5..1252f7ccd35 100644
--- a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs
@@ -1,5 +1,5 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::{is_copy, is_type_diagnostic_item, should_call_clone_as_function};
 use clippy_utils::{is_diag_trait_item, peel_blocks};
diff --git a/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs
index 3226fa9cd3f..428da0cf107 100644
--- a/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs
@@ -1,5 +1,5 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::usage::mutated_variables;
diff --git a/src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs b/src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs
index fc656fd78ba..80703618a11 100644
--- a/src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs
@@ -1,6 +1,6 @@
 use crate::methods::MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::{eager_or_lazy, higher, usage};
diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs
index 795e041ffd9..7d1d5d69c99 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs
@@ -137,10 +137,10 @@ mod wrong_self_convention;
 mod zst_offset;
 
 use clippy_config::Conf;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
 use clippy_utils::macros::FormatArgsStorage;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item};
 use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, peel_blocks, return_ty};
 pub use path_ends_with_ext::DEFAULT_ALLOWED_DOTFILES;
@@ -4107,24 +4107,32 @@ declare_clippy_lint! {
     /// ### Why is this bad?
     /// Calls such as `opt.map_or(false, |val| val == 5)` are needlessly long and cumbersome,
     /// and can be reduced to, for example, `opt == Some(5)` assuming `opt` implements `PartialEq`.
+    /// Also, calls such as `opt.map_or(true, |val| val == 5)` can be reduced to
+    /// `opt.is_none_or(|val| val == 5)`.
     /// This lint offers readability and conciseness improvements.
     ///
     /// ### Example
     /// ```no_run
-    /// pub fn a(x: Option<i32>) -> bool {
-    ///     x.map_or(false, |n| n == 5)
+    /// pub fn a(x: Option<i32>) -> (bool, bool) {
+    ///     (
+    ///         x.map_or(false, |n| n == 5),
+    ///         x.map_or(true, |n| n > 5),
+    ///     )
     /// }
     /// ```
     /// Use instead:
     /// ```no_run
-    /// pub fn a(x: Option<i32>) -> bool {
-    ///     x == Some(5)
+    /// pub fn a(x: Option<i32>) -> (bool, bool) {
+    ///     (
+    ///         x == Some(5),
+    ///         x.is_none_or(|n| n > 5),
+    ///     )
     /// }
     /// ```
-    #[clippy::version = "1.75.0"]
+    #[clippy::version = "1.84.0"]
     pub UNNECESSARY_MAP_OR,
     style,
-    "reduce unnecessary pattern matching for constructs that implement `PartialEq`"
+    "reduce unnecessary calls to `.map_or(bool, …)`"
 }
 
 declare_clippy_lint! {
@@ -4531,7 +4539,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
                         && method_config.output_type.matches(&sig.decl.output)
                         // in case there is no first arg, since we already have checked the number of arguments
                         // it's should be always true
-                        && first_arg_ty_opt.map_or(true, |first_arg_ty| method_config
+                        && first_arg_ty_opt.is_none_or(|first_arg_ty| method_config
                             .self_kind.matches(cx, self_ty, first_arg_ty)
                             )
                         && fn_header_equals(method_config.fn_header, sig.header)
diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs
index c00b9b368c4..ea4984f83ad 100644
--- a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs
@@ -203,7 +203,8 @@ fn is_is_empty_sig(cx: &LateContext<'_>, call_id: HirId) -> bool {
 fn iterates_same_ty<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<'tcx>, collect_ty: Ty<'tcx>) -> bool {
     if let Some(iter_trait) = cx.tcx.get_diagnostic_item(sym::Iterator)
         && let Some(into_iter_trait) = cx.tcx.get_diagnostic_item(sym::IntoIterator)
-        && let Some(iter_item_ty) = make_normalized_projection(cx.tcx, cx.typing_env(), iter_trait, sym::Item, [iter_ty])
+        && let Some(iter_item_ty) =
+            make_normalized_projection(cx.tcx, cx.typing_env(), iter_trait, sym::Item, [iter_ty])
         && let Some(into_iter_item_proj) = make_projection(cx.tcx, into_iter_trait, sym::Item, [collect_ty])
         && let Ok(into_iter_item_ty) = cx.tcx.try_normalize_erasing_regions(
             cx.typing_env(),
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 998bdee0157..8d97d1c72a6 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
@@ -1,5 +1,5 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::{path_to_local_id, peel_blocks};
diff --git a/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs
index 528e2204cf8..7c4dc4ffb20 100644
--- a/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/option_map_unwrap_or.rs
@@ -1,5 +1,5 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::{is_copy, is_type_diagnostic_item};
 use rustc_data_structures::fx::FxHashSet;
diff --git a/src/tools/clippy/clippy_lints/src/methods/path_ends_with_ext.rs b/src/tools/clippy/clippy_lints/src/methods/path_ends_with_ext.rs
index cfb823dbf5d..febd7fd5cf2 100644
--- a/src/tools/clippy/clippy_lints/src/methods/path_ends_with_ext.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/path_ends_with_ext.rs
@@ -1,6 +1,6 @@
 use super::PATH_ENDS_WITH_EXT;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet;
 use clippy_utils::ty::is_type_diagnostic_item;
 use rustc_ast::{LitKind, StrStyle};
diff --git a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs
index 1cee28e1986..c91be33b1cd 100644
--- a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs
@@ -1,6 +1,6 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_with_context;
 use clippy_utils::usage::local_used_after_expr;
 use clippy_utils::visitors::{Descend, for_each_expr};
diff --git a/src/tools/clippy/clippy_lints/src/methods/string_lit_chars_any.rs b/src/tools/clippy/clippy_lints/src/methods/string_lit_chars_any.rs
index cc0d432b799..cb719b34b1f 100644
--- a/src/tools/clippy/clippy_lints/src/methods/string_lit_chars_any.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/string_lit_chars_any.rs
@@ -1,5 +1,5 @@
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::SpanRangeExt;
 use clippy_utils::{is_from_proc_macro, is_trait_method, path_to_local};
 use itertools::Itertools;
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs
index bab439015c5..3de51bc661e 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs
@@ -23,7 +23,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, a
     if let hir::ExprKind::Closure(&hir::Closure { body, .. }) = arg.kind {
         let body = cx.tcx.hir().body(body);
         let arg_id = body.params[0].pat.hir_id;
-        let mutates_arg = mutated_variables(body.value, cx).map_or(true, |used_mutably| used_mutably.contains(&arg_id));
+        let mutates_arg = mutated_variables(body.value, cx).is_none_or(|used_mutably| used_mutably.contains(&arg_id));
         let (clone_or_copy_needed, _) = clone_or_copy_needed(cx, body.params[0].pat, body.value);
 
         let (mut found_mapping, mut found_filtering) = check_expression(cx, arg_id, body.value);
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs
index adc27cd437f..1199d289761 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs
@@ -1,8 +1,8 @@
 use std::borrow::Cow;
 
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::eager_or_lazy::switch_to_eager_eval;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_opt;
 use clippy_utils::sugg::{Sugg, make_binop};
 use clippy_utils::ty::{get_type_diagnostic_name, implements_trait};
@@ -60,7 +60,7 @@ pub(super) fn check<'a>(
         Some(_) | None => return,
     };
 
-    let (sugg, method) = if let ExprKind::Closure(map_closure) = map.kind
+    let (sugg, method, applicability) = if let ExprKind::Closure(map_closure) = map.kind
             && let closure_body = cx.tcx.hir().body(map_closure.body)
             && let closure_body_value = closure_body.value.peel_blocks()
             && let ExprKind::Binary(op, l, r) = closure_body_value.kind
@@ -100,7 +100,7 @@ pub(super) fn check<'a>(
             .maybe_par()
             .into_string();
 
-        (binop, "a standard comparison")
+        (binop, "a standard comparison", Applicability::MaybeIncorrect)
     } else if !def_bool
         && msrv.meets(msrvs::OPTION_RESULT_IS_VARIANT_AND)
         && let Some(recv_callsite) = snippet_opt(cx, recv.span.source_callsite())
@@ -110,6 +110,18 @@ pub(super) fn check<'a>(
         (
             format!("{recv_callsite}.{suggested_name}({span_callsite})",),
             suggested_name,
+            Applicability::MachineApplicable,
+        )
+    } else if def_bool
+        && matches!(variant, Variant::Some)
+        && msrv.meets(msrvs::IS_NONE_OR)
+        && let Some(recv_callsite) = snippet_opt(cx, recv.span.source_callsite())
+        && let Some(span_callsite) = snippet_opt(cx, map.span.source_callsite())
+    {
+        (
+            format!("{recv_callsite}.is_none_or({span_callsite})"),
+            "is_none_or",
+            Applicability::MachineApplicable,
         )
     } else {
         return;
@@ -123,9 +135,9 @@ pub(super) fn check<'a>(
         cx,
         UNNECESSARY_MAP_OR,
         expr.span,
-        "this `map_or` is redundant",
+        "this `map_or` can be simplified",
         format!("use {method} instead"),
         sugg,
-        Applicability::MaybeIncorrect,
+        applicability,
     );
 }
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 84ea3554a35..d19064fd57e 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
@@ -1,7 +1,7 @@
 use super::implicit_clone::is_clone_like;
 use super::unnecessary_iter_cloned::{self, is_into_iter};
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::{span_lint_and_sugg, 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::visitors::find_all_ret_expressions;
diff --git a/src/tools/clippy/clippy_lints/src/misc.rs b/src/tools/clippy/clippy_lints/src/misc.rs
index 408dbef9cb1..b856c929cf6 100644
--- a/src/tools/clippy/clippy_lints/src/misc.rs
+++ b/src/tools/clippy/clippy_lints/src/misc.rs
@@ -114,7 +114,7 @@ declare_clippy_lint! {
     ///     let _ = FooStruct{};
     /// }
     /// ```
-    #[clippy::version = "pre 1.29.0"]
+    #[clippy::version = "1.83.0"]
     pub USED_UNDERSCORE_ITEMS,
     pedantic,
     "using a item which is prefixed with an underscore"
@@ -351,7 +351,7 @@ fn used_underscore_binding<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
 /// `unused_variables`'s idea
 /// of what it means for an expression to be "used".
 fn is_used(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    get_parent_expr(cx, expr).map_or(true, |parent| match parent.kind {
+    get_parent_expr(cx, expr).is_none_or(|parent| match parent.kind {
         ExprKind::Assign(_, rhs, _) | ExprKind::AssignOp(_, _, rhs) => SpanlessEq::new(cx).eq_expr(rhs, expr),
         _ => is_used(cx, parent),
     })
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 eea0459e026..121c4326d64 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
@@ -1,6 +1,6 @@
 use clippy_config::Conf;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::qualify_min_const_fn::is_min_const_fn;
 use clippy_utils::{fn_has_unsatisfiable_preds, is_entrypoint_fn, is_from_proc_macro, trait_ref_of_method};
 use rustc_errors::Applicability;
diff --git a/src/tools/clippy/clippy_lints/src/missing_const_for_thread_local.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_thread_local.rs
index c2f524a6353..9a44a3c980c 100644
--- a/src/tools/clippy/clippy_lints/src/missing_const_for_thread_local.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_const_for_thread_local.rs
@@ -1,7 +1,7 @@
 use clippy_config::Conf;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::macros::macro_backtrace;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::qualify_min_const_fn::is_min_const_fn;
 use clippy_utils::source::snippet;
 use clippy_utils::{fn_has_unsatisfiable_preds, peel_blocks};
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 9ebef531bc5..f69913ddbfd 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
@@ -1,7 +1,7 @@
 use clippy_config::Conf;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::mir::{PossibleBorrowerMap, enclosing_mir, expr_local, local_assignments, used_exactly_once};
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_with_context;
 use clippy_utils::ty::{implements_trait, is_copy};
 use clippy_utils::{DefinedTy, ExprUseNode, expr_use_ctxt, peel_n_hir_expr_refs};
@@ -362,7 +362,7 @@ fn referent_used_exactly_once<'tcx>(
         let body_owner_local_def_id = cx.tcx.hir().enclosing_body_owner(reference.hir_id);
         if possible_borrowers
             .last()
-            .map_or(true, |&(local_def_id, _)| local_def_id != body_owner_local_def_id)
+            .is_none_or(|&(local_def_id, _)| local_def_id != body_owner_local_def_id)
         {
             possible_borrowers.push((body_owner_local_def_id, PossibleBorrowerMap::new(cx, mir)));
         }
diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
index b65ec8c3c48..6f3f371a68d 100644
--- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
@@ -182,14 +182,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
                 && !is_copy(cx, ty)
                 && ty.is_sized(cx.tcx, cx.typing_env())
                 && !allowed_traits.iter().any(|&t| {
-                    implements_trait_with_env_from_iter(
-                        cx.tcx,
-                        cx.typing_env(),
-                        ty, 
-                        t,
-                        None,
-                        [None::<ty::GenericArg<'tcx>>]
-                    )
+                    implements_trait_with_env_from_iter(cx.tcx, cx.typing_env(), ty, t, None, [None::<
+                        ty::GenericArg<'tcx>,
+                    >])
                 })
                 && !implements_borrow_trait
                 && !all_borrowable_trait
@@ -205,6 +200,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
                                 cx.param_env,
                                 ty,
                                 traits::ObligationCause::dummy_with_span(span),
+                                rustc_hir::Safety::Safe,
                             )
                             .is_ok()
                             {
diff --git a/src/tools/clippy/clippy_lints/src/needless_update.rs b/src/tools/clippy/clippy_lints/src/needless_update.rs
index 6a2893cefbd..0cba72bd2c6 100644
--- a/src/tools/clippy/clippy_lints/src/needless_update.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_update.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint;
-use rustc_hir::{Expr, ExprKind};
+use rustc_hir::{Expr, ExprKind, StructTailExpr};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
 use rustc_session::declare_lint_pass;
@@ -51,7 +51,7 @@ declare_lint_pass!(NeedlessUpdate => [NEEDLESS_UPDATE]);
 
 impl<'tcx> LateLintPass<'tcx> for NeedlessUpdate {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if let ExprKind::Struct(_, fields, Some(base)) = expr.kind {
+        if let ExprKind::Struct(_, fields, StructTailExpr::Base(base)) = expr.kind {
             let ty = cx.typeck_results().expr_ty(expr);
             if let ty::Adt(def, _) = ty.kind() {
                 if fields.len() == def.non_enum_variant().fields.len()
diff --git a/src/tools/clippy/clippy_lints/src/neg_multiply.rs b/src/tools/clippy/clippy_lints/src/neg_multiply.rs
index a0ba2aaf552..429afff9b66 100644
--- a/src/tools/clippy/clippy_lints/src/neg_multiply.rs
+++ b/src/tools/clippy/clippy_lints/src/neg_multiply.rs
@@ -2,7 +2,7 @@ use clippy_utils::consts::{self, Constant};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_context;
 use clippy_utils::sugg::has_enclosing_paren;
-use rustc_ast::util::parser::PREC_PREFIX;
+use rustc_ast::util::parser::ExprPrecedence;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
 use rustc_lint::{LateContext, LateLintPass};
@@ -58,7 +58,7 @@ fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) {
     {
         let mut applicability = Applicability::MachineApplicable;
         let (snip, from_macro) = snippet_with_context(cx, exp.span, span.ctxt(), "..", &mut applicability);
-        let suggestion = if !from_macro && exp.precedence() < PREC_PREFIX && !has_enclosing_paren(&snip) {
+        let suggestion = if !from_macro && exp.precedence() < ExprPrecedence::Prefix && !has_enclosing_paren(&snip) {
             format!("-({snip})")
         } else {
             format!("-{snip}")
diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs
index 74536028b5d..9e44bb02c56 100644
--- a/src/tools/clippy/clippy_lints/src/no_effect.rs
+++ b/src/tools/clippy/clippy_lints/src/no_effect.rs
@@ -8,7 +8,7 @@ use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{
     BinOpKind, BlockCheckMode, Expr, ExprKind, HirId, HirIdMap, ItemKind, LocalSource, Node, PatKind, Stmt, StmtKind,
-    UnsafeSource, is_range_literal,
+    UnsafeSource, StructTailExpr, is_range_literal,
 };
 use rustc_infer::infer::TyCtxtInferExt as _;
 use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -238,7 +238,10 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
         ExprKind::Struct(_, fields, ref base) => {
             !has_drop(cx, cx.typeck_results().expr_ty(expr))
                 && fields.iter().all(|field| has_no_effect(cx, field.expr))
-                && base.as_ref().map_or(true, |base| has_no_effect(cx, base))
+                && match &base {
+                    StructTailExpr::None | StructTailExpr::DefaultFields(_) => true,
+                    StructTailExpr::Base(base) => has_no_effect(cx, base),
+                }
         },
         ExprKind::Call(callee, args) => {
             if let ExprKind::Path(ref qpath) = callee.kind {
@@ -342,6 +345,10 @@ fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Vec
             if has_drop(cx, cx.typeck_results().expr_ty(expr)) {
                 None
             } else {
+                let base = match base {
+                    StructTailExpr::Base(base) => Some(base),
+                    StructTailExpr::None | StructTailExpr::DefaultFields(_) => None,
+                };
                 Some(fields.iter().map(|f| &f.expr).chain(base).map(Deref::deref).collect())
             }
         },
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 5416e00fe0c..ebd301d5156 100644
--- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs
+++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs
@@ -11,7 +11,7 @@ use rustc_hir::{
     BodyId, Expr, ExprKind, HirId, Impl, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind, UnOp,
 };
 use rustc_lint::{LateContext, LateLintPass, Lint};
-use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult, GlobalId};
+use rustc_middle::mir::interpret::{ErrorHandled, EvalToValTreeResult, GlobalId, ReportedErrorInfo};
 use rustc_middle::ty::adjustment::Adjust;
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_session::impl_lint_pass;
@@ -278,7 +278,12 @@ impl<'tcx> NonCopyConst<'tcx> {
     fn is_value_unfrozen_expr(cx: &LateContext<'tcx>, hir_id: HirId, def_id: DefId, ty: Ty<'tcx>) -> bool {
         let args = cx.typeck_results().node_args(hir_id);
 
-        let result = Self::const_eval_resolve(cx.tcx, cx.typing_env(), ty::UnevaluatedConst::new(def_id, args), DUMMY_SP);
+        let result = Self::const_eval_resolve(
+            cx.tcx,
+            cx.typing_env(),
+            ty::UnevaluatedConst::new(def_id, args),
+            DUMMY_SP,
+        );
         Self::is_value_unfrozen_raw(cx, result, ty)
     }
 
@@ -297,7 +302,10 @@ impl<'tcx> NonCopyConst<'tcx> {
                 tcx.const_eval_global_id_for_typeck(typing_env, cid, span)
             },
             Ok(None) => Err(ErrorHandled::TooGeneric(span)),
-            Err(err) => Err(ErrorHandled::Reported(err.into(), span)),
+            Err(err) => Err(ErrorHandled::Reported(
+                ReportedErrorInfo::non_const_eval_error(err),
+                span,
+            )),
         }
     }
 }
@@ -335,7 +343,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
                 // i.e. having an enum doesn't necessary mean a type has a frozen variant.
                 // And, implementing it isn't a trivial task; it'll probably end up
                 // re-implementing the trait predicate evaluation specific to `Freeze`.
-                && body_id_opt.map_or(true, |body_id| Self::is_value_unfrozen_poly(cx, body_id, normalized))
+                && body_id_opt.is_none_or(|body_id| Self::is_value_unfrozen_poly(cx, body_id, normalized))
             {
                 lint(cx, Source::Assoc { item: trait_item.span });
             }
diff --git a/src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs b/src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs
index aefb665b52e..f6ce1d1d586 100644
--- a/src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs
+++ b/src/tools/clippy/clippy_lints/src/non_zero_suggestions.rs
@@ -39,7 +39,7 @@ declare_clippy_lint! {
     ///     let r2 = x % NonZeroU64::from(y);
     /// }
     /// ```
-    #[clippy::version = "1.81.0"]
+    #[clippy::version = "1.83.0"]
     pub NON_ZERO_SUGGESTIONS,
     restriction,
     "suggests using `NonZero#` from `u#` or `i#` for more efficient and type-safe conversions"
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 13b3d240700..6de203e068b 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
@@ -200,7 +200,7 @@ impl Params {
                 if self
                     .get_by_fn(param.fn_id, usage.idx)
                     // If the parameter can't be found, then it's used for more than just recursion.
-                    .map_or(true, |p| self.try_disable_lint_for_param(p, eval_stack))
+                    .is_none_or(|p| self.try_disable_lint_for_param(p, eval_stack))
                 {
                     param.apply_lint.set(false);
                     eval_stack.pop();
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 0dcaec1c9a7..1315c3dfc12 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
@@ -27,7 +27,7 @@ pub(super) fn check<'tcx>(
             if let Some((_, lang_item)) = binop_traits(op.node)
                 && let Some(trait_id) = cx.tcx.lang_items().get(lang_item)
                 && let parent_fn = cx.tcx.hir().get_parent_item(e.hir_id).def_id
-                && trait_ref_of_method(cx, parent_fn).map_or(true, |t| t.path.res.def_id() != trait_id)
+                && trait_ref_of_method(cx, parent_fn).is_none_or(|t| t.path.res.def_id() != trait_id)
                 && implements_trait(cx, ty, trait_id, &[rty.into()])
             {
                 // Primitive types execute assign-ops right-to-left. Every other type is left-to-right.
diff --git a/src/tools/clippy/clippy_lints/src/operators/bit_mask.rs b/src/tools/clippy/clippy_lints/src/operators/bit_mask.rs
index 4414056a467..e87cfd103c3 100644
--- a/src/tools/clippy/clippy_lints/src/operators/bit_mask.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/bit_mask.rs
@@ -1,5 +1,6 @@
 use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint;
+use clippy_utils::is_from_proc_macro;
 use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::LateContext;
 use rustc_span::Span;
@@ -35,9 +36,9 @@ fn invert_cmp(cmp: BinOpKind) -> BinOpKind {
     }
 }
 
-fn check_compare(cx: &LateContext<'_>, bit_op: &Expr<'_>, cmp_op: BinOpKind, cmp_value: u128, span: Span) {
+fn check_compare<'a>(cx: &LateContext<'a>, bit_op: &Expr<'a>, cmp_op: BinOpKind, cmp_value: u128, span: Span) {
     if let ExprKind::Binary(op, left, right) = &bit_op.kind {
-        if op.node != BinOpKind::BitAnd && op.node != BinOpKind::BitOr {
+        if op.node != BinOpKind::BitAnd && op.node != BinOpKind::BitOr || is_from_proc_macro(cx, bit_op) {
             return;
         }
         if let Some(mask) = fetch_int_literal(cx, right).or_else(|| fetch_int_literal(cx, left)) {
diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs
index ecc095f3859..dec4c18a309 100644
--- a/src/tools/clippy/clippy_lints/src/ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/ptr.rs
@@ -541,9 +541,7 @@ fn check_mut_from_ref<'tcx>(cx: &LateContext<'tcx>, sig: &FnSig<'_>, body: Optio
             .collect();
         if let Some(args) = args
             && !args.is_empty()
-            && body.map_or(true, |body| {
-                sig.header.safety == Safety::Unsafe || contains_unsafe_block(cx, body.value)
-            })
+            && body.is_none_or(|body| sig.header.safety == Safety::Unsafe || contains_unsafe_block(cx, body.value))
         {
             span_lint_and_then(
                 cx,
diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs
index f69cb9be4ca..77abe7151f0 100644
--- a/src/tools/clippy/clippy_lints/src/question_mark.rs
+++ b/src/tools/clippy/clippy_lints/src/question_mark.rs
@@ -1,9 +1,9 @@
 use crate::manual_let_else::MANUAL_LET_ELSE;
 use crate::question_mark_used::QUESTION_MARK_USED;
 use clippy_config::Conf;
-use clippy_config::msrvs::Msrv;
 use clippy_config::types::MatchLintBehaviour;
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::Msrv;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
 use clippy_utils::{
@@ -251,7 +251,7 @@ fn check_is_none_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Ex
     {
         let mut applicability = Applicability::MachineApplicable;
         let receiver_str = snippet_with_applicability(cx, caller.span, "..", &mut applicability);
-        let by_ref = !caller_ty.is_copy_modulo_regions(cx.tcx, cx.typing_env())
+        let by_ref = !cx.type_is_copy_modulo_regions(caller_ty)
             && !matches!(caller.kind, ExprKind::Call(..) | ExprKind::MethodCall(..));
         let sugg = if let Some(else_inner) = r#else {
             if eq_expr_value(cx, caller, peel_blocks(else_inner)) {
diff --git a/src/tools/clippy/clippy_lints/src/ranges.rs b/src/tools/clippy/clippy_lints/src/ranges.rs
index 21cd3367262..1b0c0a4956f 100644
--- a/src/tools/clippy/clippy_lints/src/ranges.rs
+++ b/src/tools/clippy/clippy_lints/src/ranges.rs
@@ -1,7 +1,7 @@
 use clippy_config::Conf;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::{SpanRangeExt, snippet, snippet_with_applicability};
 use clippy_utils::sugg::Sugg;
 use clippy_utils::{get_parent_expr, higher, is_in_const_context, is_integer_const, path_to_local};
diff --git a/src/tools/clippy/clippy_lints/src/redundant_async_block.rs b/src/tools/clippy/clippy_lints/src/redundant_async_block.rs
index 313e4083256..3ade6bcee84 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_async_block.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_async_block.rs
@@ -88,7 +88,7 @@ fn desugar_async_block<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Op
         cx.typeck_results()
             .closure_min_captures
             .get(def_id)
-            .map_or(true, |m| {
+            .is_none_or(|m| {
                 m.values().all(|places| {
                     places
                         .iter()
diff --git a/src/tools/clippy/clippy_lints/src/redundant_field_names.rs b/src/tools/clippy/clippy_lints/src/redundant_field_names.rs
index d0dbff081f9..347540e7344 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_field_names.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_field_names.rs
@@ -1,6 +1,6 @@
 use clippy_config::Conf;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
 use rustc_ast::ast::{Expr, ExprKind};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
diff --git a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs
index 79baa914b03..8e3472b1b5a 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
 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 rustc_ast::util::parser::PREC_PREFIX;
+use rustc_ast::util::parser::ExprPrecedence;
 use rustc_errors::Applicability;
 use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability};
 use rustc_lint::{LateContext, LateLintPass, Lint};
@@ -85,7 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing {
             let (expr_ty, expr_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(expr));
             let (indexed_ty, indexed_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(indexed));
             let parent_expr = get_parent_expr(cx, expr);
-            let needs_parens_for_prefix = parent_expr.is_some_and(|parent| parent.precedence() > PREC_PREFIX);
+            let needs_parens_for_prefix = parent_expr.is_some_and(|parent| parent.precedence() > ExprPrecedence::Prefix);
 
             if expr_ty == indexed_ty {
                 if expr_ref_count > indexed_ref_count {
diff --git a/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs b/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
index b27bb2e78af..06c85433806 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
@@ -1,6 +1,6 @@
 use clippy_config::Conf;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet;
 use rustc_ast::ast::{ConstItem, Item, ItemKind, StaticItem, Ty, TyKind};
 use rustc_errors::Applicability;
diff --git a/src/tools/clippy/clippy_lints/src/single_call_fn.rs b/src/tools/clippy/clippy_lints/src/single_call_fn.rs
index abe13a97c0d..0176077c70e 100644
--- a/src/tools/clippy/clippy_lints/src/single_call_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/single_call_fn.rs
@@ -93,7 +93,7 @@ impl SingleCallFn {
                 .tcx
                 .hir()
                 .maybe_body_owned_by(fn_def_id)
-                .map_or(true, |body| is_in_test_function(cx.tcx, body.value.hir_id))
+                .is_none_or(|body| is_in_test_function(cx.tcx, body.value.hir_id))
             || match cx.tcx.hir_node(fn_hir_id) {
                 Node::Item(item) => is_from_proc_macro(cx, item),
                 Node::ImplItem(item) => is_from_proc_macro(cx, item),
diff --git a/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs b/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs
index 44e585953bf..bb11daecc07 100644
--- a/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs
+++ b/src/tools/clippy/clippy_lints/src/single_range_in_vec_init.rs
@@ -6,7 +6,7 @@ use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::implements_trait;
 use rustc_ast::{LitIntType, LitKind, UintTy};
 use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind, LangItem, QPath};
+use rustc_hir::{Expr, ExprKind, LangItem, QPath, StructTailExpr};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
 use std::fmt::{self, Display, Formatter};
@@ -86,7 +86,7 @@ impl LateLintPass<'_> for SingleRangeInVecInit {
             return;
         };
 
-        let ExprKind::Struct(QPath::LangItem(lang_item, ..), [start, end], None) = inner_expr.kind else {
+        let ExprKind::Struct(QPath::LangItem(lang_item, ..), [start, end], StructTailExpr::None) = inner_expr.kind else {
             return;
         };
 
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 8dd99858793..2941b9c3960 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
@@ -1,7 +1,7 @@
 use clippy_config::Conf;
-use clippy_config::msrvs::Msrv;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_from_proc_macro;
+use clippy_utils::msrvs::Msrv;
 use rustc_attr::{StabilityLevel, StableSince};
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
diff --git a/src/tools/clippy/clippy_lints/src/string_patterns.rs b/src/tools/clippy/clippy_lints/src/string_patterns.rs
index ba2ddac2ec3..0d85b1b858a 100644
--- a/src/tools/clippy/clippy_lints/src/string_patterns.rs
+++ b/src/tools/clippy/clippy_lints/src/string_patterns.rs
@@ -1,10 +1,10 @@
 use std::ops::ControlFlow;
 
 use clippy_config::Conf;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::eager_or_lazy::switch_to_eager_eval;
 use clippy_utils::macros::matching_root_macro_call;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::path_to_local_id;
 use clippy_utils::source::{snippet, str_literal_to_char_literal};
 use clippy_utils::visitors::{Descend, for_each_expr};
diff --git a/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs b/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs
index 50a1577b288..a1d92c3ac71 100644
--- a/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs
+++ b/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs
@@ -56,9 +56,10 @@ impl<'tcx> LateLintPass<'tcx> for TrailingEmptyArray {
 fn is_struct_with_trailing_zero_sized_array<'tcx>(cx: &LateContext<'tcx>, item: &Item<'tcx>) -> bool {
     if let ItemKind::Struct(data, _) = &item.kind
         && let Some(last_field) = data.fields().last()
-        && let field_ty = cx
-            .tcx
-            .normalize_erasing_regions(cx.typing_env(), cx.tcx.type_of(last_field.def_id).instantiate_identity())
+        && let field_ty = cx.tcx.normalize_erasing_regions(
+            cx.typing_env(),
+            cx.tcx.type_of(last_field.def_id).instantiate_identity(),
+        )
         && let ty::Array(_, array_len) = *field_ty.kind()
         && let Some(0) = array_len.try_to_target_usize(cx.tcx)
     {
diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs
index 6f3c6682305..99844beb8f0 100644
--- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs
+++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs
@@ -1,6 +1,6 @@
 use clippy_config::Conf;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::{SpanRangeExt, snippet, snippet_with_applicability};
 use clippy_utils::{SpanlessEq, SpanlessHash, is_from_proc_macro};
 use core::hash::{Hash, Hasher};
diff --git a/src/tools/clippy/clippy_lints/src/transmute/mod.rs b/src/tools/clippy/clippy_lints/src/transmute/mod.rs
index 25fec9f688c..1cb0f837227 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/mod.rs
@@ -20,8 +20,8 @@ mod utils;
 mod wrong_transmute;
 
 use clippy_config::Conf;
-use clippy_config::msrvs::Msrv;
 use clippy_utils::is_in_const_context;
+use clippy_utils::msrvs::Msrv;
 use rustc_hir::{Expr, ExprKind, QPath};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_float_to_int.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_float_to_int.rs
index 3507eb9a124..f0b8abf9af6 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/transmute_float_to_int.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_float_to_int.rs
@@ -1,6 +1,6 @@
 use super::TRANSMUTE_FLOAT_TO_INT;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::sugg;
 use rustc_ast as ast;
 use rustc_errors::Applicability;
diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_float.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_float.rs
index c5c7ed6d398..e5b9aea6423 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_float.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_float.rs
@@ -1,6 +1,6 @@
 use super::TRANSMUTE_INT_TO_FLOAT;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::sugg;
 use rustc_errors::Applicability;
 use rustc_hir::Expr;
diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_num_to_bytes.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_num_to_bytes.rs
index a94cd27c7fd..6d828bad9b3 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/transmute_num_to_bytes.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_num_to_bytes.rs
@@ -1,6 +1,6 @@
 use super::TRANSMUTE_NUM_TO_BYTES;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::sugg;
 use rustc_errors::Applicability;
 use rustc_hir::Expr;
diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs
index bf6700b1b6b..c4a2e20fa9d 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs
@@ -1,6 +1,6 @@
 use super::TRANSMUTE_PTR_TO_PTR;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::sugg;
 use rustc_errors::Applicability;
 use rustc_hir::Expr;
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 eaf927c0005..ef18633d945 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs
@@ -1,6 +1,6 @@
 use super::TRANSMUTE_PTR_TO_REF;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::sugg;
 use rustc_errors::Applicability;
diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs b/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs
index cad15b1e982..0d5cf45a5e6 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs
@@ -1,7 +1,7 @@
 use super::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::sugg::Sugg;
-use rustc_ast::util::parser::AssocOp;
+use rustc_ast::util::parser::ExprPrecedence;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, Node};
 use rustc_hir_typeck::cast::check_cast;
@@ -44,7 +44,7 @@ pub(super) fn check<'tcx>(
     };
 
     if let Node::Expr(parent) = cx.tcx.parent_hir_node(e.hir_id)
-        && parent.precedence() > AssocOp::As.precedence() as i8
+        && parent.precedence() > ExprPrecedence::Cast
     {
         sugg = format!("({sugg})");
     }
diff --git a/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs b/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs
index 07d0f59b91c..99a55f9fc35 100644
--- a/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs
+++ b/src/tools/clippy/clippy_lints/src/tuple_array_conversions.rs
@@ -1,6 +1,6 @@
 use clippy_config::Conf;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::visitors::for_each_local_use_after_expr;
 use clippy_utils::{is_from_proc_macro, path_to_local};
 use itertools::Itertools;
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 0bba611116b..b79e59f857b 100644
--- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
+++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
@@ -337,7 +337,7 @@ fn is_unsafe_from_proc_macro(cx: &LateContext<'_>, span: Span) -> bool {
         .src
         .as_deref()
         .and_then(|src| src.get(file_pos.pos.to_usize()..))
-        .map_or(true, |src| !src.starts_with("unsafe"))
+        .is_none_or(|src| !src.starts_with("unsafe"))
 }
 
 // Checks if any parent {expression, statement, block, local, const, static}
diff --git a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs
index 6eef582b4b2..0702f6d1e74 100644
--- a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs
+++ b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs
@@ -145,7 +145,7 @@ fn expr_needs_inferred_result<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -
     }
     while let Some(id) = locals_to_check.pop() {
         if let Node::LetStmt(l) = cx.tcx.parent_hir_node(id) {
-            if !l.ty.map_or(true, |ty| matches!(ty.kind, TyKind::Infer)) {
+            if !l.ty.is_none_or(|ty| matches!(ty.kind, TyKind::Infer)) {
                 return false;
             }
             if let Some(e) = l.init {
diff --git a/src/tools/clippy/clippy_lints/src/unnamed_address.rs b/src/tools/clippy/clippy_lints/src/unnamed_address.rs
deleted file mode 100644
index cd2dacc9f09..00000000000
--- a/src/tools/clippy/clippy_lints/src/unnamed_address.rs
+++ /dev/null
@@ -1,60 +0,0 @@
-use clippy_utils::diagnostics::span_lint;
-use rustc_hir::{BinOpKind, Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty;
-use rustc_session::declare_lint_pass;
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for comparisons with an address of a function item.
-    ///
-    /// ### Why is this bad?
-    /// Function item address is not guaranteed to be unique and could vary
-    /// between different code generation units. Furthermore different function items could have
-    /// the same address after being merged together.
-    ///
-    /// ### Example
-    /// ```no_run
-    /// type F = fn();
-    /// fn a() {}
-    /// let f: F = a;
-    /// if f == a {
-    ///     // ...
-    /// }
-    /// ```
-    #[clippy::version = "1.44.0"]
-    pub FN_ADDRESS_COMPARISONS,
-    correctness,
-    "comparison with an address of a function item"
-}
-
-declare_lint_pass!(UnnamedAddress => [FN_ADDRESS_COMPARISONS]);
-
-impl LateLintPass<'_> for UnnamedAddress {
-    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
-        fn is_comparison(binop: BinOpKind) -> bool {
-            matches!(
-                binop,
-                BinOpKind::Eq | BinOpKind::Lt | BinOpKind::Le | BinOpKind::Ne | BinOpKind::Ge | BinOpKind::Gt
-            )
-        }
-
-        fn is_fn_def(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-            matches!(cx.typeck_results().expr_ty(expr).kind(), ty::FnDef(..))
-        }
-
-        if let ExprKind::Binary(binop, left, right) = expr.kind
-            && is_comparison(binop.node)
-            && cx.typeck_results().expr_ty_adjusted(left).is_fn_ptr()
-            && cx.typeck_results().expr_ty_adjusted(right).is_fn_ptr()
-            && (is_fn_def(cx, left) || is_fn_def(cx, right))
-        {
-            span_lint(
-                cx,
-                FN_ADDRESS_COMPARISONS,
-                expr.span,
-                "comparing with a non-unique address of a function item",
-            );
-        }
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs b/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs
index afdd3505cdd..0a90d31db7e 100644
--- a/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_struct_initialization.rs
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet;
 use clippy_utils::ty::is_copy;
 use clippy_utils::{get_parent_expr, path_to_local};
-use rustc_hir::{BindingMode, Expr, ExprField, ExprKind, Node, PatKind, Path, QPath, UnOp};
+use rustc_hir::{BindingMode, Expr, ExprField, ExprKind, Node, PatKind, Path, QPath, UnOp, StructTailExpr};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
 
@@ -59,15 +59,15 @@ impl LateLintPass<'_> for UnnecessaryStruct {
         let field_path = same_path_in_all_fields(cx, expr, fields);
 
         let sugg = match (field_path, base) {
-            (Some(&path), None) => {
+            (Some(&path), StructTailExpr::None | StructTailExpr::DefaultFields(_)) => {
                 // all fields match, no base given
                 path.span
             },
-            (Some(path), Some(base)) if base_is_suitable(cx, expr, base) && path_matches_base(path, base) => {
+            (Some(path), StructTailExpr::Base(base)) if base_is_suitable(cx, expr, base) && path_matches_base(path, base) => {
                 // all fields match, has base: ensure that the path of the base matches
                 base.span
             },
-            (None, Some(base)) if fields.is_empty() && base_is_suitable(cx, expr, base) => {
+            (None, StructTailExpr::Base(base)) if fields.is_empty() && base_is_suitable(cx, expr, base) => {
                 // just the base, no explicit fields
                 base.span
             },
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 c7c837de505..50a97579df7 100644
--- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
+++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
@@ -1,9 +1,9 @@
 #![allow(clippy::wildcard_imports, clippy::enum_glob_use)]
 
 use clippy_config::Conf;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::ast_utils::{eq_field_pat, eq_id, eq_maybe_qself, eq_pat, eq_path};
 use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::over;
 use rustc_ast::PatKind::*;
 use rustc_ast::mut_visit::*;
@@ -234,7 +234,7 @@ fn transform_with_focus_on_idx(alternatives: &mut ThinVec<P<Pat>>, focus_idx: us
         // In the case of only two patterns, replacement adds net characters.
         | Ref(_, Mutability::Not)
         // Dealt with elsewhere.
-        | Or(_) | Paren(_) | Deref(_) => false,
+        | Or(_) | Paren(_) | Deref(_) | Guard(..) => false,
         // Transform `box x | ... | box y` into `box (x | y)`.
         //
         // The cases below until `Slice(...)` deal with *singleton* products.
diff --git a/src/tools/clippy/clippy_lints/src/unused_trait_names.rs b/src/tools/clippy/clippy_lints/src/unused_trait_names.rs
index 9fd6ebccd02..17ee5fc20ca 100644
--- a/src/tools/clippy/clippy_lints/src/unused_trait_names.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_trait_names.rs
@@ -1,7 +1,7 @@
 use clippy_config::Conf;
-use clippy_config::msrvs::{self, Msrv};
 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_opt;
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
diff --git a/src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs b/src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs
index 8de062a8fc1..c3843279ba2 100644
--- a/src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs
+++ b/src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs
@@ -93,7 +93,7 @@ fn check_ident(cx: &LateContext<'_>, ident: &Ident, hir_id: HirId, be_aggressive
         while let Some(c) = s.next() {
             r.push(
                 if replace(&mut prev_upper, c.is_ascii_uppercase())
-                    && s.clone().next().map_or(true, |c| c.is_ascii_uppercase())
+                    && s.clone().next().is_none_or(|c| c.is_ascii_uppercase())
                 {
                     c.to_ascii_lowercase()
                 } else {
diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs
index f5cf4a586fd..05c5be03002 100644
--- a/src/tools/clippy/clippy_lints/src/use_self.rs
+++ b/src/tools/clippy/clippy_lints/src/use_self.rs
@@ -1,7 +1,7 @@
 use clippy_config::Conf;
-use clippy_config::msrvs::{self, Msrv};
 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;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
@@ -94,7 +94,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
             && let parameters = &item_path.segments.last().expect(SEGMENTS_MSG).args
             && parameters
                 .as_ref()
-                .map_or(true, |params| params.parenthesized == GenericArgsParentheses::No)
+                .is_none_or(|params| params.parenthesized == GenericArgsParentheses::No)
             && !item.span.from_expansion()
             && !is_from_proc_macro(cx, item)
         // expensive, should be last check
diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs
index 51001d374b4..311ed427cb9 100644
--- a/src/tools/clippy/clippy_lints/src/utils/author.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/author.rs
@@ -4,7 +4,7 @@ use rustc_ast::ast::{LitFloatType, LitKind};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::{
     self as hir, BindingMode, CaptureBy, Closure, ClosureKind, ConstArg, ConstArgKind, CoroutineKind,
-    ExprKind, FnRetTy, HirId, Lit, PatKind, QPath, StmtKind, TyKind,
+    ExprKind, FnRetTy, HirId, Lit, PatKind, QPath, StmtKind, TyKind, StructTailExpr,
 };
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_session::declare_lint_pass;
@@ -598,7 +598,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
             },
             ExprKind::Struct(qpath, fields, base) => {
                 bind!(self, qpath, fields);
-                opt_bind!(self, base);
+                let base = OptionPat::new(match base {
+                    StructTailExpr::Base(base) => Some(self.bind("base", base)),
+                    StructTailExpr::None | StructTailExpr::DefaultFields(_) => None,
+                });
                 kind!("Struct({qpath}, {fields}, {base})");
                 self.qpath(qpath);
                 self.slice(fields, |field| {
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs
index 96397375d5e..a5fad68eea1 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/invalid_paths.rs
@@ -5,8 +5,8 @@ use rustc_hir as hir;
 use rustc_hir::Item;
 use rustc_hir::def::DefKind;
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::FloatTy;
 use rustc_middle::ty::fast_reject::SimplifiedType;
+use rustc_middle::ty::{self, FloatTy};
 use rustc_session::declare_lint_pass;
 use rustc_span::symbol::Symbol;
 
@@ -32,9 +32,12 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths {
         let mod_name = &cx.tcx.item_name(local_def_id.to_def_id());
         if mod_name.as_str() == "paths"
             && let hir::ItemKind::Const(.., body_id) = item.kind
-            && let Some(Constant::Vec(path)) =
-                ConstEvalCtxt::with_env(cx.tcx, cx.tcx.param_env(item.owner_id), cx.tcx.typeck(item.owner_id))
-                    .eval_simple(cx.tcx.hir().body(body_id).value)
+            && let Some(Constant::Vec(path)) = ConstEvalCtxt::with_env(
+                cx.tcx,
+                ty::TypingEnv::post_analysis(cx.tcx, item.owner_id),
+                cx.tcx.typeck(item.owner_id),
+            )
+            .eval_simple(cx.tcx.hir().body(body_id).value)
             && let Some(path) = path
                 .iter()
                 .map(|x| {
diff --git a/src/tools/clippy/clippy_lints/src/vec.rs b/src/tools/clippy/clippy_lints/src/vec.rs
index 9bcff9d7bce..ef1c46154d2 100644
--- a/src/tools/clippy/clippy_lints/src/vec.rs
+++ b/src/tools/clippy/clippy_lints/src/vec.rs
@@ -2,9 +2,9 @@ use std::collections::BTreeMap;
 use std::ops::ControlFlow;
 
 use clippy_config::Conf;
-use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_hir_and_then;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::is_copy;
 use clippy_utils::visitors::for_each_local_use_after_expr;
diff --git a/src/tools/clippy/clippy_lints/src/zombie_processes.rs b/src/tools/clippy/clippy_lints/src/zombie_processes.rs
index 8d9241cc7d9..4a13c10166f 100644
--- a/src/tools/clippy/clippy_lints/src/zombie_processes.rs
+++ b/src/tools/clippy/clippy_lints/src/zombie_processes.rs
@@ -35,7 +35,7 @@ declare_clippy_lint! {
     /// let mut child = Command::new("ls").spawn().expect("failed to execute child");
     /// child.wait().expect("failed to wait on child");
     /// ```
-    #[clippy::version = "1.74.0"]
+    #[clippy::version = "1.83.0"]
     pub ZOMBIE_PROCESSES,
     suspicious,
     "not waiting on a spawned child process"
diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml
index d8d5733da1c..945827c98c1 100644
--- a/src/tools/clippy/clippy_utils/Cargo.toml
+++ b/src/tools/clippy/clippy_utils/Cargo.toml
@@ -1,15 +1,22 @@
 [package]
 name = "clippy_utils"
-version = "0.1.84"
+# begin autogenerated version
+version = "0.1.85"
+# end autogenerated version
 edition = "2021"
-publish = false
+description = "Helpful tools for writing lints, provided as they are used in Clippy"
+repository = "https://github.com/rust-lang/rust-clippy"
+readme = "README.md"
+license = "MIT OR Apache-2.0"
+keywords = ["clippy", "lint", "utils"]
+categories = ["development-tools"]
 
 [dependencies]
-clippy_config = { path = "../clippy_config" }
 arrayvec = { version = "0.7", default-features = false }
 itertools = "0.12"
 # FIXME(f16_f128): remove when no longer needed for parsing
 rustc_apfloat = "0.2.0"
+serde = { version = "1.0", features = ["derive"] }
 
 [package.metadata.rust-analyzer]
 # This crate uses #[feature(rustc_private)]
diff --git a/src/tools/clippy/clippy_utils/README.md b/src/tools/clippy/clippy_utils/README.md
new file mode 100644
index 00000000000..61476a82ba0
--- /dev/null
+++ b/src/tools/clippy/clippy_utils/README.md
@@ -0,0 +1,40 @@
+# `clippy-utils`
+
+Helpful tools for writing lints, provided as they are used in Clippy.
+
+## Usage
+
+This crate is only guaranteed to build with this `nightly` toolchain:
+
+<!-- begin autogenerated nightly -->
+```
+nightly-2024-11-28
+```
+<!-- end autogenerated nightly -->
+
+To use `clippy-utils` in your lint, add the following to your `Cargo.toml`:
+
+```
+clippy_utils = "0.1.XY"
+```
+
+`XY` is the version of the nightly toolchain above and can be determined with `rustc +nightly-YYYY-MM-DD -V`.
+
+## :warning: Stability :warning:
+
+No stability guarantees are made for this crate! Use at your own risk.
+
+Function signatures can change or be removed without replacement without any prior notice.
+
+## LICENSE
+
+<!-- REUSE-IgnoreStart -->
+
+Copyright 2014-2024 The Rust Project Developers
+
+Licensed under the Apache License, Version 2.0
+<[https://www.apache.org/licenses/LICENSE-2.0](https://www.apache.org/licenses/LICENSE-2.0)> or the MIT license
+<[https://opensource.org/licenses/MIT](https://opensource.org/licenses/MIT)>, at your option. Files in the project may
+not be copied, modified, or distributed except according to those terms.
+
+<!-- REUSE-IgnoreEnd -->
diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs
index c90f4a6ebfe..620a9b56eb5 100644
--- a/src/tools/clippy/clippy_utils/src/ast_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs
@@ -872,8 +872,8 @@ pub fn eq_attr_args(l: &AttrArgs, r: &AttrArgs) -> bool {
     match (l, r) {
         (Empty, Empty) => true,
         (Delimited(la), Delimited(ra)) => eq_delim_args(la, ra),
-        (Eq(_, AttrArgsEq::Ast(le)), Eq(_, AttrArgsEq::Ast(re))) => eq_expr(le, re),
-        (Eq(_, AttrArgsEq::Hir(ll)), Eq(_, AttrArgsEq::Hir(rl))) => ll.kind == rl.kind,
+        (Eq { value: AttrArgsEq::Ast(le), .. }, Eq{ value: AttrArgsEq::Ast(re), .. }) => eq_expr(le, re),
+        (Eq { value: AttrArgsEq::Hir(ll), .. }, Eq{ value: AttrArgsEq::Hir(rl), .. }) => ll.kind == rl.kind,
         _ => false,
     }
 }
diff --git a/src/tools/clippy/clippy_utils/src/attrs.rs b/src/tools/clippy/clippy_utils/src/attrs.rs
index edc9c6ccdff..b2a6657baad 100644
--- a/src/tools/clippy/clippy_utils/src/attrs.rs
+++ b/src/tools/clippy/clippy_utils/src/attrs.rs
@@ -27,7 +27,10 @@ pub const BUILTIN_ATTRIBUTES: &[(&str, DeprecationStatus)] = &[
     ("cyclomatic_complexity", DeprecationStatus::Replaced("cognitive_complexity")),
     ("dump",                  DeprecationStatus::None),
     ("msrv",                  DeprecationStatus::None),
+    // The following attributes are for the 3rd party crate authors.
+    // See book/src/attribs.md
     ("has_significant_drop",  DeprecationStatus::None),
+    ("format_args",           DeprecationStatus::None),
 ];
 
 pub struct LimitStack {
diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs
index 52c98646289..43ddf06730d 100644
--- a/src/tools/clippy/clippy_utils/src/consts.rs
+++ b/src/tools/clippy/clippy_utils/src/consts.rs
@@ -226,7 +226,7 @@ impl Constant<'_> {
                     .zip(r)
                     .zip(tys)
                     .map(|((li, ri), cmp_type)| Self::partial_cmp(tcx, cmp_type, li, ri))
-                    .find(|r| r.map_or(true, |o| o != Ordering::Equal))
+                    .find(|r| r.is_none_or(|o| o != Ordering::Equal))
                     .unwrap_or_else(|| Some(l.len().cmp(&r.len()))),
                 _ => None,
             },
@@ -236,7 +236,7 @@ impl Constant<'_> {
                 };
                 iter::zip(l, r)
                     .map(|(li, ri)| Self::partial_cmp(tcx, cmp_type, li, ri))
-                    .find(|r| r.map_or(true, |o| o != Ordering::Equal))
+                    .find(|r| r.is_none_or(|o| o != Ordering::Equal))
                     .unwrap_or_else(|| Some(l.len().cmp(&r.len())))
             },
             (Self::Repeat(lv, ls), Self::Repeat(rv, rs)) => {
diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs
index 11bbe734844..d216879cbd2 100644
--- a/src/tools/clippy/clippy_utils/src/higher.rs
+++ b/src/tools/clippy/clippy_utils/src/higher.rs
@@ -8,7 +8,7 @@ use crate::ty::is_type_diagnostic_item;
 
 use rustc_ast::ast;
 use rustc_hir as hir;
-use rustc_hir::{Arm, Block, Expr, ExprKind, HirId, LoopSource, MatchSource, Node, Pat, QPath};
+use rustc_hir::{Arm, Block, Expr, ExprKind, StructTailExpr, HirId, LoopSource, MatchSource, Node, Pat, QPath};
 use rustc_lint::LateContext;
 use rustc_span::{Span, sym, symbol};
 
@@ -236,7 +236,7 @@ impl<'a> Range<'a> {
                     limits: ast::RangeLimits::Closed,
                 })
             },
-            ExprKind::Struct(path, fields, None) => match (path, fields) {
+            ExprKind::Struct(path, fields, StructTailExpr::None) => match (path, fields) {
                 (QPath::LangItem(hir::LangItem::RangeFull, ..), []) => Some(Range {
                     start: None,
                     end: None,
diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs
index 920562f4b93..4be4340862d 100644
--- a/src/tools/clippy/clippy_utils/src/hir_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs
@@ -10,7 +10,7 @@ use rustc_hir::{
     AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, Closure, ConstArg, ConstArgKind, Expr,
     ExprField, ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime,
     LifetimeName, Pat, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitBoundModifiers, Ty,
-    TyKind,
+    TyKind, StructTailExpr,
 };
 use rustc_lexer::{TokenKind, tokenize};
 use rustc_lint::LateContext;
@@ -380,7 +380,12 @@ impl HirEqInterExpr<'_, '_, '_> {
             (ExprKind::Ret(l), ExprKind::Ret(r)) => both(l.as_ref(), r.as_ref(), |l, r| self.eq_expr(l, r)),
             (&ExprKind::Struct(l_path, lf, ref lo), &ExprKind::Struct(r_path, rf, ref ro)) => {
                 self.eq_qpath(l_path, r_path)
-                    && both(lo.as_ref(), ro.as_ref(), |l, r| self.eq_expr(l, r))
+                    && match (lo, ro) {
+                        (StructTailExpr::Base(l),StructTailExpr::Base(r)) => self.eq_expr(l, r),
+                        (StructTailExpr::None, StructTailExpr::None) => true,
+                        (StructTailExpr::DefaultFields(_), StructTailExpr::DefaultFields(_)) => true,
+                        _ => false,
+                    }
                     && over(lf, rf, |l, r| self.eq_expr_field(l, r))
             },
             (&ExprKind::Tup(l_tup), &ExprKind::Tup(r_tup)) => self.eq_exprs(l_tup, r_tup),
@@ -539,7 +544,7 @@ impl HirEqInterExpr<'_, '_, '_> {
     fn eq_path_parameters(&mut self, left: &GenericArgs<'_>, right: &GenericArgs<'_>) -> bool {
         if left.parenthesized == right.parenthesized {
             over(left.args, right.args, |l, r| self.eq_generic_arg(l, r)) // FIXME(flip1995): may not work
-                && over(left.constraints, right.constraints, |l, r| self.eq_assoc_type_binding(l, r))
+                && over(left.constraints, right.constraints, |l, r| self.eq_assoc_eq_constraint(l, r))
         } else {
             false
         }
@@ -596,12 +601,13 @@ impl HirEqInterExpr<'_, '_, '_> {
         }
     }
 
-    fn eq_assoc_type_binding(&mut self, left: &AssocItemConstraint<'_>, right: &AssocItemConstraint<'_>) -> bool {
+    /// Checks whether two constraints designate the same equality constraint (same name, and same
+    /// type or const).
+    fn eq_assoc_eq_constraint(&mut self, left: &AssocItemConstraint<'_>, right: &AssocItemConstraint<'_>) -> bool {
+        // TODO: this could be extended to check for identical associated item bound constraints
         left.ident.name == right.ident.name
-            && self.eq_ty(
-                left.ty().expect("expected assoc type binding"),
-                right.ty().expect("expected assoc type binding"),
-            )
+            && (both_some_and(left.ty(), right.ty(), |l, r| self.eq_ty(l, r))
+                || both_some_and(left.ct(), right.ct(), |l, r| self.eq_const_arg(l, r)))
     }
 
     fn check_ctxt(&mut self, left: SyntaxContext, right: SyntaxContext) -> bool {
@@ -721,6 +727,11 @@ pub fn both<X>(l: Option<&X>, r: Option<&X>, mut eq_fn: impl FnMut(&X, &X) -> bo
         .map_or_else(|| r.is_none(), |x| r.as_ref().is_some_and(|y| eq_fn(x, y)))
 }
 
+/// Checks if the two `Option`s are both `Some` and pass the predicate function.
+pub fn both_some_and<X, Y>(l: Option<X>, r: Option<Y>, mut pred: impl FnMut(X, Y) -> bool) -> bool {
+    l.is_some_and(|l| r.is_some_and(|r| pred(l, r)))
+}
+
 /// 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 {
     left.len() == right.len() && left.iter().zip(right).all(|(x, y)| eq_fn(x, y))
@@ -1011,7 +1022,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
                     self.hash_expr(f.expr);
                 }
 
-                if let Some(e) = *expr {
+                if let StructTailExpr::Base(e) = *expr {
                     self.hash_expr(e);
                 }
             },
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index 82699211489..8d48cdd3cbb 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -50,6 +50,7 @@ extern crate rustc_session;
 extern crate rustc_span;
 extern crate rustc_target;
 extern crate rustc_trait_selection;
+extern crate smallvec;
 
 #[macro_use]
 pub mod sym_helper;
@@ -65,6 +66,7 @@ pub mod higher;
 mod hir_utils;
 pub mod macros;
 pub mod mir;
+pub mod msrvs;
 pub mod numeric_literal;
 pub mod paths;
 pub mod ptr;
@@ -89,7 +91,6 @@ use std::hash::BuildHasherDefault;
 use std::iter::{once, repeat};
 use std::sync::{Mutex, MutexGuard, OnceLock};
 
-use clippy_config::types::DisallowedPath;
 use itertools::Itertools;
 use rustc_ast::ast::{self, LitKind, RangeLimits};
 use rustc_data_structures::fx::FxHashMap;
@@ -97,7 +98,7 @@ use rustc_data_structures::packed::Pu128;
 use rustc_data_structures::unhash::UnhashMap;
 use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
 use rustc_hir::def::{DefKind, Res};
-use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LOCAL_CRATE, LocalDefId, LocalModDefId};
+use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalModDefId};
 use rustc_hir::definitions::{DefPath, DefPathData};
 use rustc_hir::hir_id::{HirIdMap, HirIdSet};
 use rustc_hir::intravisit::{FnKind, Visitor, walk_expr};
@@ -116,8 +117,8 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
 use rustc_middle::ty::fast_reject::SimplifiedType;
 use rustc_middle::ty::layout::IntegerExt;
 use rustc_middle::ty::{
-    self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, FloatTy, GenericArgsRef, IntTy,
-    Ty, TyCtxt, TypeVisitableExt, UintTy, UpvarCapture,
+    self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, FloatTy, GenericArgsRef, IntTy, Ty, TyCtxt,
+    TypeVisitableExt, UintTy, UpvarCapture,
 };
 use rustc_span::hygiene::{ExpnKind, MacroKind};
 use rustc_span::source_map::SourceMap;
@@ -748,18 +749,6 @@ pub fn def_path_def_ids(tcx: TyCtxt<'_>, path: &[&str]) -> impl Iterator<Item =
     def_path_res(tcx, path).into_iter().filter_map(|res| res.opt_def_id())
 }
 
-/// Creates a map of disallowed items to the reason they were disallowed.
-pub fn create_disallowed_map(
-    tcx: TyCtxt<'_>,
-    disallowed: &'static [DisallowedPath],
-) -> DefIdMap<(&'static str, Option<&'static str>)> {
-    disallowed
-        .iter()
-        .map(|x| (x.path(), x.path().split("::").collect::<Vec<_>>(), x.reason()))
-        .flat_map(|(name, path, reason)| def_path_def_ids(tcx, &path).map(move |id| (id, (name, reason))))
-        .collect()
-}
-
 /// Convenience function to get the `DefId` of a trait by path.
 /// It could be a trait or trait alias.
 ///
@@ -1581,7 +1570,7 @@ pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
 pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Option<&Path<'_>>) -> bool {
     let ty = cx.typeck_results().expr_ty(expr);
     if let Some(Range { start, end, limits }) = Range::hir(expr) {
-        let start_is_none_or_min = start.map_or(true, |start| {
+        let start_is_none_or_min = start.is_none_or(|start| {
             if let rustc_ty::Adt(_, subst) = ty.kind()
                 && let bnd_ty = subst.type_at(0)
                 && let Some(min_val) = bnd_ty.numeric_min_val(cx.tcx)
@@ -1593,7 +1582,7 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti
                 false
             }
         });
-        let end_is_none_or_max = end.map_or(true, |end| match limits {
+        let end_is_none_or_max = end.is_none_or(|end| match limits {
             RangeLimits::Closed => {
                 if let rustc_ty::Adt(_, subst) = ty.kind()
                     && let bnd_ty = subst.type_at(0)
@@ -2715,10 +2704,10 @@ pub enum DefinedTy<'tcx> {
     /// Used for function signatures, and constant and static values. The type is
     /// in the context of its definition site. We also track the `def_id` of its
     /// definition site.
-    /// 
+    ///
     /// WARNING: As the `ty` in in the scope of the definition, not of the function
     /// using it, you must be very careful with how you use it. Using it in the wrong
-    /// scope easily results in ICEs. 
+    /// scope easily results in ICEs.
     Mir {
         def_site_def_id: Option<DefId>,
         ty: Binder<'tcx, Ty<'tcx>>,
@@ -2844,7 +2833,7 @@ impl<'tcx> ExprUseNode<'tcx> {
             Self::ConstStatic(id) => Some(DefinedTy::Mir {
                 def_site_def_id: Some(id.def_id.to_def_id()),
                 ty: Binder::dummy(cx.tcx.type_of(id).instantiate_identity()),
-        }),
+            }),
             Self::Return(id) => {
                 if let Node::Expr(Expr {
                     kind: ExprKind::Closure(c),
@@ -2857,7 +2846,10 @@ impl<'tcx> ExprUseNode<'tcx> {
                     }
                 } else {
                     let ty = cx.tcx.fn_sig(id).instantiate_identity().output();
-                    Some(DefinedTy::Mir { def_site_def_id: Some(id.def_id.to_def_id()), ty })
+                    Some(DefinedTy::Mir {
+                        def_site_def_id: Some(id.def_id.to_def_id()),
+                        ty,
+                    })
                 }
             },
             Self::Field(field) => match get_parent_expr_for_hir(cx, field.hir_id) {
@@ -2874,8 +2866,8 @@ impl<'tcx> ExprUseNode<'tcx> {
                             .map(|f| (adt, f))
                     })
                     .map(|(adt, field_def)| DefinedTy::Mir {
-                            def_site_def_id: Some(adt.did()),
-                            ty: Binder::dummy(cx.tcx.type_of(field_def.did).instantiate_identity()),
+                        def_site_def_id: Some(adt.did()),
+                        ty: Binder::dummy(cx.tcx.type_of(field_def.did).instantiate_identity()),
                     }),
                 _ => None,
             },
@@ -2885,9 +2877,9 @@ impl<'tcx> ExprUseNode<'tcx> {
                 Some(match hir_ty {
                     Some(hir_ty) => DefinedTy::Hir(hir_ty),
                     None => DefinedTy::Mir {
-                        def_site_def_id:  sig.predicates_id(),
+                        def_site_def_id: sig.predicates_id(),
                         ty,
-                    }
+                    },
                 })
             },
             Self::MethodArg(id, _, i) => {
diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs
index 9c4d19ac1f1..45beb146eb6 100644
--- a/src/tools/clippy/clippy_utils/src/macros.rs
+++ b/src/tools/clippy/clippy_utils/src/macros.rs
@@ -1,5 +1,6 @@
 #![allow(clippy::similar_names)] // `expr` and `expn`
 
+use crate::get_unique_attr;
 use crate::visitors::{Descend, for_each_expr_without_closures};
 
 use arrayvec::ArrayVec;
@@ -7,7 +8,7 @@ use rustc_ast::{FormatArgs, FormatArgument, FormatPlaceholder};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_data_structures::sync::{Lrc, OnceLock};
 use rustc_hir::{self as hir, Expr, ExprKind, HirId, Node, QPath};
-use rustc_lint::LateContext;
+use rustc_lint::{LateContext, LintContext};
 use rustc_span::def_id::DefId;
 use rustc_span::hygiene::{self, MacroKind, SyntaxContext};
 use rustc_span::{BytePos, ExpnData, ExpnId, ExpnKind, Span, SpanData, Symbol, sym};
@@ -36,7 +37,9 @@ pub fn is_format_macro(cx: &LateContext<'_>, macro_def_id: DefId) -> bool {
     if let Some(name) = cx.tcx.get_diagnostic_name(macro_def_id) {
         FORMAT_MACRO_DIAG_ITEMS.contains(&name)
     } else {
-        false
+        // Allow users to tag any macro as being format!-like
+        // TODO: consider deleting FORMAT_MACRO_DIAG_ITEMS and using just this method
+        get_unique_attr(cx.sess(), cx.tcx.get_attrs_unchecked(macro_def_id), "format_args").is_some()
     }
 }
 
@@ -93,7 +96,7 @@ pub fn expn_is_local(expn: ExpnId) -> bool {
     std::iter::once((expn, data))
         .chain(backtrace)
         .find_map(|(_, data)| data.macro_def_id)
-        .map_or(true, DefId::is_local)
+        .is_none_or(DefId::is_local)
 }
 
 /// Returns an iterator of macro expansions that created the given span.
diff --git a/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs b/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs
index 6b3078f52af..17e6558a41c 100644
--- a/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs
+++ b/src/tools/clippy/clippy_utils/src/mir/possible_borrower.rs
@@ -2,7 +2,7 @@ use super::possible_origin::PossibleOriginVisitor;
 use super::transitive_relation::TransitiveRelation;
 use crate::ty::is_copy;
 use rustc_data_structures::fx::FxHashMap;
-use rustc_index::bit_set::{BitSet, HybridBitSet};
+use rustc_index::bit_set::BitSet;
 use rustc_lint::LateContext;
 use rustc_middle::mir::visit::Visitor as _;
 use rustc_middle::mir::{self, Mutability};
@@ -21,14 +21,14 @@ struct PossibleBorrowerVisitor<'a, 'b, 'tcx> {
     possible_borrower: TransitiveRelation,
     body: &'b mir::Body<'tcx>,
     cx: &'a LateContext<'tcx>,
-    possible_origin: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
+    possible_origin: FxHashMap<mir::Local, BitSet<mir::Local>>,
 }
 
 impl<'a, 'b, 'tcx> PossibleBorrowerVisitor<'a, 'b, 'tcx> {
     fn new(
         cx: &'a LateContext<'tcx>,
         body: &'b mir::Body<'tcx>,
-        possible_origin: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
+        possible_origin: FxHashMap<mir::Local, BitSet<mir::Local>>,
     ) -> Self {
         Self {
             possible_borrower: TransitiveRelation::default(),
@@ -119,7 +119,7 @@ impl<'tcx> mir::visit::Visitor<'tcx> for PossibleBorrowerVisitor<'_, '_, 'tcx> {
             let mut mutable_variables: Vec<mir::Local> = mutable_borrowers
                 .iter()
                 .filter_map(|r| self.possible_origin.get(r))
-                .flat_map(HybridBitSet::iter)
+                .flat_map(BitSet::iter)
                 .collect();
 
             if ContainsRegion.visit_ty(self.body.local_decls[*dest].ty).is_break() {
@@ -171,7 +171,7 @@ fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) {
 #[allow(clippy::module_name_repetitions)]
 pub struct PossibleBorrowerMap<'b, 'tcx> {
     /// Mapping `Local -> its possible borrowers`
-    pub map: FxHashMap<mir::Local, HybridBitSet<mir::Local>>,
+    pub map: FxHashMap<mir::Local, BitSet<mir::Local>>,
     maybe_live: ResultsCursor<'b, 'tcx, MaybeStorageLive<'tcx>>,
     // Caches to avoid allocation of `BitSet` on every query
     pub bitset: (BitSet<mir::Local>, BitSet<mir::Local>),
diff --git a/src/tools/clippy/clippy_utils/src/mir/possible_origin.rs b/src/tools/clippy/clippy_utils/src/mir/possible_origin.rs
index 4157b3f4930..47b93aad20c 100644
--- a/src/tools/clippy/clippy_utils/src/mir/possible_origin.rs
+++ b/src/tools/clippy/clippy_utils/src/mir/possible_origin.rs
@@ -1,7 +1,7 @@
 use super::transitive_relation::TransitiveRelation;
 use crate::ty::is_copy;
 use rustc_data_structures::fx::FxHashMap;
-use rustc_index::bit_set::HybridBitSet;
+use rustc_index::bit_set::BitSet;
 use rustc_lint::LateContext;
 use rustc_middle::mir;
 
@@ -22,7 +22,7 @@ impl<'a, 'tcx> PossibleOriginVisitor<'a, 'tcx> {
         }
     }
 
-    pub fn into_map(self, cx: &LateContext<'tcx>) -> FxHashMap<mir::Local, HybridBitSet<mir::Local>> {
+    pub fn into_map(self, cx: &LateContext<'tcx>) -> FxHashMap<mir::Local, BitSet<mir::Local>> {
         let mut map = FxHashMap::default();
         for row in (1..self.body.local_decls.len()).map(mir::Local::from_usize) {
             if is_copy(cx, self.body.local_decls[row].ty) {
diff --git a/src/tools/clippy/clippy_utils/src/mir/transitive_relation.rs b/src/tools/clippy/clippy_utils/src/mir/transitive_relation.rs
index 7fe2960739f..74d1f60af71 100644
--- a/src/tools/clippy/clippy_utils/src/mir/transitive_relation.rs
+++ b/src/tools/clippy/clippy_utils/src/mir/transitive_relation.rs
@@ -1,5 +1,5 @@
 use rustc_data_structures::fx::FxHashMap;
-use rustc_index::bit_set::HybridBitSet;
+use rustc_index::bit_set::BitSet;
 use rustc_middle::mir;
 
 #[derive(Default)]
@@ -12,8 +12,8 @@ impl TransitiveRelation {
         self.relations.entry(a).or_default().push(b);
     }
 
-    pub fn reachable_from(&self, a: mir::Local, domain_size: usize) -> HybridBitSet<mir::Local> {
-        let mut seen = HybridBitSet::new_empty(domain_size);
+    pub fn reachable_from(&self, a: mir::Local, domain_size: usize) -> BitSet<mir::Local> {
+        let mut seen = BitSet::new_empty(domain_size);
         let mut stack = vec![a];
         while let Some(u) = stack.pop() {
             if let Some(edges) = self.relations.get(&u) {
diff --git a/src/tools/clippy/clippy_config/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs
index 764ca8fb50a..1eb7d54e133 100644
--- a/src/tools/clippy/clippy_config/src/msrvs.rs
+++ b/src/tools/clippy/clippy_utils/src/msrvs.rs
@@ -121,7 +121,7 @@ impl Msrv {
     }
 
     pub fn meets(&self, required: RustcVersion) -> bool {
-        self.current().map_or(true, |msrv| msrv >= required)
+        self.current().is_none_or(|msrv| msrv >= required)
     }
 
     fn parse_attr(sess: &Session, attrs: &[Attribute]) -> Option<RustcVersion> {
diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs
index 11a98b02f33..bb40a9430a7 100644
--- a/src/tools/clippy/clippy_utils/src/paths.rs
+++ b/src/tools/clippy/clippy_utils/src/paths.rs
@@ -36,7 +36,7 @@ pub const CHILD_KILL: [&str; 4] = ["std", "process", "Child", "kill"];
 pub const PANIC_ANY: [&str; 3] = ["std", "panic", "panic_any"];
 
 // Paths in clippy itself
-pub const MSRV: [&str; 3] = ["clippy_config", "msrvs", "Msrv"];
+pub const MSRV: [&str; 3] = ["clippy_utils", "msrvs", "Msrv"];
 
 // Paths in external crates
 #[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
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 3c9ea4bfaf4..df3f10d6179 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
@@ -3,7 +3,7 @@
 // of terminologies might not be relevant in the context of Clippy. Note that its behavior might
 // differ from the time of `rustc` even if the name stays the same.
 
-use clippy_config::msrvs::{self, Msrv};
+use crate::msrvs::{self, Msrv};
 use hir::LangItem;
 use rustc_attr::StableSince;
 use rustc_const_eval::check_consts::ConstCx;
@@ -409,8 +409,7 @@ fn is_ty_const_destruct<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, body: &Body<'tcx>
             return false;
         }
 
-        let (infcx, param_env) =
-            tcx.infer_ctxt().build_with_typing_env(body.typing_env(tcx));
+        let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(body.typing_env(tcx));
         // FIXME(const_trait_impl) constness
         let obligation = Obligation::new(
             tcx,
diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs
index 03e1a814a86..260d1b801e3 100644
--- a/src/tools/clippy/clippy_utils/src/ty.rs
+++ b/src/tools/clippy/clippy_utils/src/ty.rs
@@ -38,7 +38,7 @@ pub use type_certainty::expr_type_is_certain;
 
 /// Checks if the given type implements copy.
 pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
-    ty.is_copy_modulo_regions(cx.tcx, cx.typing_env())
+    cx.type_is_copy_modulo_regions(ty)
 }
 
 /// This checks whether a given type is known to implement Debug.
@@ -226,7 +226,14 @@ pub fn implements_trait<'tcx>(
     trait_id: DefId,
     args: &[GenericArg<'tcx>],
 ) -> bool {
-    implements_trait_with_env_from_iter(cx.tcx, cx.typing_env(), ty, trait_id, None, args.iter().map(|&x| Some(x)))
+    implements_trait_with_env_from_iter(
+        cx.tcx,
+        cx.typing_env(),
+        ty,
+        trait_id,
+        None,
+        args.iter().map(|&x| Some(x)),
+    )
 }
 
 /// Same as `implements_trait` but allows using a `ParamEnv` different from the lint context.
@@ -1262,7 +1269,8 @@ pub fn make_normalized_projection_with_regions<'tcx>(
         }
         let cause = ObligationCause::dummy();
         let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
-        match infcx.at(&cause, param_env)
+        match infcx
+            .at(&cause, param_env)
             .query_normalize(Ty::new_projection_from_args(tcx, ty.def_id, ty.args))
         {
             Ok(ty) => Some(ty.value),
@@ -1278,7 +1286,10 @@ pub fn make_normalized_projection_with_regions<'tcx>(
 pub fn normalize_with_regions<'tcx>(tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
     let cause = ObligationCause::dummy();
     let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
-    infcx.at(&cause, param_env).query_normalize(ty).map_or(ty, |ty| ty.value)
+    infcx
+        .at(&cause, param_env)
+        .query_normalize(ty)
+        .map_or(ty, |ty| ty.value)
 }
 
 /// Checks if the type is `core::mem::ManuallyDrop<_>`
diff --git a/src/tools/clippy/clippy_utils/src/usage.rs b/src/tools/clippy/clippy_utils/src/usage.rs
index c8c25456f69..37f72966892 100644
--- a/src/tools/clippy/clippy_utils/src/usage.rs
+++ b/src/tools/clippy/clippy_utils/src/usage.rs
@@ -27,7 +27,7 @@ pub fn mutated_variables<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) ->
 }
 
 pub fn is_potentially_mutated<'tcx>(variable: HirId, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool {
-    mutated_variables(expr, cx).map_or(true, |mutated| mutated.contains(&variable))
+    mutated_variables(expr, cx).is_none_or(|mutated| mutated.contains(&variable))
 }
 
 pub fn is_potentially_local_place(local_id: HirId, place: &Place<'_>) -> bool {
diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs
index a79be5ca7d4..351e619d7b1 100644
--- a/src/tools/clippy/clippy_utils/src/visitors.rs
+++ b/src/tools/clippy/clippy_utils/src/visitors.rs
@@ -7,7 +7,7 @@ use rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::intravisit::{self, Visitor, walk_block, walk_expr};
 use rustc_hir::{
     AnonConst, Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, LetExpr, Pat, QPath,
-    Safety, Stmt, UnOp, UnsafeSource,
+    Safety, Stmt, UnOp, UnsafeSource, StructTailExpr,
 };
 use rustc_lint::LateContext;
 use rustc_middle::hir::nested_filter;
@@ -663,7 +663,7 @@ pub fn for_each_unconsumed_temporary<'tcx, B>(
                 for field in fields {
                     helper(typeck, true, field.expr, f)?;
                 }
-                if let Some(default) = default {
+                if let StructTailExpr::Base(default) = default {
                     helper(typeck, false, default, f)?;
                 }
             },
diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain
index e32e0cb3604..fb159ca2ae0 100644
--- a/src/tools/clippy/rust-toolchain
+++ b/src/tools/clippy/rust-toolchain
@@ -1,4 +1,6 @@
 [toolchain]
-channel = "nightly-2024-11-14"
+# begin autogenerated nightly
+channel = "nightly-2024-11-28"
+# 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 c66837dc998..32ee668cda1 100644
--- a/src/tools/clippy/src/driver.rs
+++ b/src/tools/clippy/src/driver.rs
@@ -236,7 +236,8 @@ pub fn main() {
             let mut args: Vec<String> = orig_args.clone();
             pass_sysroot_env_if_given(&mut args, sys_root_env);
 
-            return rustc_driver::RunCompiler::new(&args, &mut DefaultCallbacks).run();
+            rustc_driver::RunCompiler::new(&args, &mut DefaultCallbacks).run();
+            return Ok(());
         }
 
         if orig_args.iter().any(|a| a == "--version" || a == "-V") {
@@ -296,12 +297,13 @@ pub fn main() {
             args.extend(clippy_args);
             rustc_driver::RunCompiler::new(&args, &mut ClippyCallbacks { clippy_args_var })
                 .set_using_internal_features(using_internal_features)
-                .run()
+                .run();
         } else {
             rustc_driver::RunCompiler::new(&args, &mut RustcCallbacks { clippy_args_var })
                 .set_using_internal_features(using_internal_features)
-                .run()
+                .run();
         }
+        return Ok(());
     }))
 }
 
diff --git a/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.fixed b/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.fixed
index 9b5bf736f13..928596d0809 100644
--- a/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.fixed
+++ b/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.fixed
@@ -8,8 +8,8 @@ extern crate rustc_lint;
 extern crate rustc_middle;
 #[macro_use]
 extern crate rustc_session;
-use clippy_config::msrvs::Msrv;
 use clippy_utils::extract_msrv_attr;
+use clippy_utils::msrvs::Msrv;
 use rustc_hir::Expr;
 use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
 
diff --git a/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.rs b/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.rs
index c5bde47e4ce..50b28648ccc 100644
--- a/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.rs
+++ b/src/tools/clippy/tests/ui-internal/invalid_msrv_attr_impl.rs
@@ -8,8 +8,8 @@ extern crate rustc_lint;
 extern crate rustc_middle;
 #[macro_use]
 extern crate rustc_session;
-use clippy_config::msrvs::Msrv;
 use clippy_utils::extract_msrv_attr;
+use clippy_utils::msrvs::Msrv;
 use rustc_hir::Expr;
 use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
 
diff --git a/src/tools/clippy/tests/ui/crashes/ice-10645.rs b/src/tools/clippy/tests/ui/crashes/ice-10645.rs
deleted file mode 100644
index 6e126aff751..00000000000
--- a/src/tools/clippy/tests/ui/crashes/ice-10645.rs
+++ /dev/null
@@ -1,7 +0,0 @@
-//@compile-flags: --cap-lints=warn
-// https://github.com/rust-lang/rust-clippy/issues/10645
-
-#![warn(clippy::future_not_send)]
-pub async fn bar<'a, T: 'a>(_: T) {}
-
-fn main() {}
diff --git a/src/tools/clippy/tests/ui/crashes/ice-10645.stderr b/src/tools/clippy/tests/ui/crashes/ice-10645.stderr
deleted file mode 100644
index 0269072b88b..00000000000
--- a/src/tools/clippy/tests/ui/crashes/ice-10645.stderr
+++ /dev/null
@@ -1,17 +0,0 @@
-warning: future cannot be sent between threads safely
-  --> tests/ui/crashes/ice-10645.rs:5:1
-   |
-LL | pub async fn bar<'a, T: 'a>(_: T) {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `bar` is not `Send`
-   |
-note: captured value is not `Send`
-  --> tests/ui/crashes/ice-10645.rs:5:29
-   |
-LL | pub async fn bar<'a, T: 'a>(_: T) {}
-   |                             ^ has type `T` which is not `Send`
-   = note: `T` doesn't implement `std::marker::Send`
-   = note: `-D clippy::future-not-send` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::future_not_send)]`
-
-warning: 1 warning emitted
-
diff --git a/src/tools/clippy/tests/ui/derivable_impls.fixed b/src/tools/clippy/tests/ui/derivable_impls.fixed
new file mode 100644
index 00000000000..c85f384fd6e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/derivable_impls.fixed
@@ -0,0 +1,295 @@
+#![allow(dead_code)]
+
+use std::collections::HashMap;
+
+#[derive(Default)]
+struct FooDefault<'a> {
+    a: bool,
+    b: i32,
+    c: u64,
+    d: Vec<i32>,
+    e: FooND1,
+    f: FooND2,
+    g: HashMap<i32, i32>,
+    h: (i32, Vec<i32>),
+    i: [Vec<i32>; 3],
+    j: [i32; 5],
+    k: Option<i32>,
+    l: &'a [i32],
+}
+
+
+#[derive(Default)]
+struct TupleDefault(bool, i32, u64);
+
+
+struct FooND1 {
+    a: bool,
+}
+
+impl std::default::Default for FooND1 {
+    fn default() -> Self {
+        Self { a: true }
+    }
+}
+
+struct FooND2 {
+    a: i32,
+}
+
+impl std::default::Default for FooND2 {
+    fn default() -> Self {
+        Self { a: 5 }
+    }
+}
+
+struct FooNDNew {
+    a: bool,
+}
+
+impl FooNDNew {
+    fn new() -> Self {
+        Self { a: true }
+    }
+}
+
+impl Default for FooNDNew {
+    fn default() -> Self {
+        Self::new()
+    }
+}
+
+struct FooNDVec(Vec<i32>);
+
+impl Default for FooNDVec {
+    fn default() -> Self {
+        Self(vec![5, 12])
+    }
+}
+
+#[derive(Default)]
+struct StrDefault<'a>(&'a str);
+
+
+#[derive(Default)]
+struct AlreadyDerived(i32, bool);
+
+macro_rules! mac {
+    () => {
+        0
+    };
+    ($e:expr) => {
+        struct X(u32);
+        impl Default for X {
+            fn default() -> Self {
+                Self($e)
+            }
+        }
+    };
+}
+
+mac!(0);
+
+#[derive(Default)]
+struct Y(u32);
+
+struct RustIssue26925<T> {
+    a: Option<T>,
+}
+
+// We should watch out for cases where a manual impl is needed because a
+// derive adds different type bounds (https://github.com/rust-lang/rust/issues/26925).
+// For example, a struct with Option<T> does not require T: Default, but a derive adds
+// that type bound anyways. So until #26925 get fixed we should disable lint
+// for the following case
+impl<T> Default for RustIssue26925<T> {
+    fn default() -> Self {
+        Self { a: None }
+    }
+}
+
+struct SpecializedImpl<A, B> {
+    a: A,
+    b: B,
+}
+
+impl<T: Default> Default for SpecializedImpl<T, T> {
+    fn default() -> Self {
+        Self {
+            a: T::default(),
+            b: T::default(),
+        }
+    }
+}
+
+#[derive(Default)]
+struct WithoutSelfCurly {
+    a: bool,
+}
+
+
+#[derive(Default)]
+struct WithoutSelfParan(bool);
+
+
+// https://github.com/rust-lang/rust-clippy/issues/7655
+
+pub struct SpecializedImpl2<T> {
+    v: Vec<T>,
+}
+
+impl Default for SpecializedImpl2<String> {
+    fn default() -> Self {
+        Self { v: Vec::new() }
+    }
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/7654
+
+pub struct Color {
+    pub r: u8,
+    pub g: u8,
+    pub b: u8,
+}
+
+/// `#000000`
+impl Default for Color {
+    fn default() -> Self {
+        Color { r: 0, g: 0, b: 0 }
+    }
+}
+
+pub struct Color2 {
+    pub r: u8,
+    pub g: u8,
+    pub b: u8,
+}
+
+impl Default for Color2 {
+    /// `#000000`
+    fn default() -> Self {
+        Self { r: 0, g: 0, b: 0 }
+    }
+}
+
+#[derive(Default)]
+pub struct RepeatDefault1 {
+    a: [i8; 32],
+}
+
+
+pub struct RepeatDefault2 {
+    a: [i8; 33],
+}
+
+impl Default for RepeatDefault2 {
+    fn default() -> Self {
+        RepeatDefault2 { a: [0; 33] }
+    }
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/7753
+
+pub enum IntOrString {
+    Int(i32),
+    String(String),
+}
+
+impl Default for IntOrString {
+    fn default() -> Self {
+        IntOrString::Int(0)
+    }
+}
+
+#[derive(Default)]
+pub enum SimpleEnum {
+    Foo,
+    #[default]
+    Bar,
+}
+
+
+pub enum NonExhaustiveEnum {
+    Foo,
+    #[non_exhaustive]
+    Bar,
+}
+
+impl Default for NonExhaustiveEnum {
+    fn default() -> Self {
+        NonExhaustiveEnum::Bar
+    }
+}
+
+// https://github.com/rust-lang/rust-clippy/issues/10396
+
+#[derive(Default)]
+struct DefaultType;
+
+struct GenericType<T = DefaultType> {
+    t: T,
+}
+
+impl Default for GenericType {
+    fn default() -> Self {
+        Self { t: Default::default() }
+    }
+}
+
+struct InnerGenericType<T> {
+    t: T,
+}
+
+impl Default for InnerGenericType<DefaultType> {
+    fn default() -> Self {
+        Self { t: Default::default() }
+    }
+}
+
+struct OtherGenericType<T = DefaultType> {
+    inner: InnerGenericType<T>,
+}
+
+impl Default for OtherGenericType {
+    fn default() -> Self {
+        Self {
+            inner: Default::default(),
+        }
+    }
+}
+
+mod issue10158 {
+    pub trait T {}
+
+    #[derive(Default)]
+    pub struct S {}
+    impl T for S {}
+
+    pub struct Outer {
+        pub inner: Box<dyn T>,
+    }
+
+    impl Default for Outer {
+        fn default() -> Self {
+            Outer {
+                // Box::<S>::default() adjusts to Box<dyn T>
+                inner: Box::<S>::default(),
+            }
+        }
+    }
+}
+
+mod issue11368 {
+    pub struct A {
+        a: u32,
+    }
+
+    impl Default for A {
+        #[track_caller]
+        fn default() -> Self {
+            Self { a: 0 }
+        }
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/derivable_impls.rs b/src/tools/clippy/tests/ui/derivable_impls.rs
index 58f7771b627..21d73ba8b77 100644
--- a/src/tools/clippy/tests/ui/derivable_impls.rs
+++ b/src/tools/clippy/tests/ui/derivable_impls.rs
@@ -1,7 +1,5 @@
 #![allow(dead_code)]
 
-//@no-rustfix: need to change the suggestion to a multipart suggestion
-
 use std::collections::HashMap;
 
 struct FooDefault<'a> {
diff --git a/src/tools/clippy/tests/ui/derivable_impls.stderr b/src/tools/clippy/tests/ui/derivable_impls.stderr
index d3adfa60e10..c22569145bd 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:22:1
+  --> tests/ui/derivable_impls.rs:20:1
    |
 LL | / impl std::default::Default for FooDefault<'_> {
 LL | |     fn default() -> Self {
@@ -12,15 +12,14 @@ LL | | }
    |
    = note: `-D clippy::derivable-impls` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::derivable_impls)]`
-   = help: remove the manual implementation...
-help: ...and instead derive it
+help: replace the manual implementation with a derive attribute
    |
 LL + #[derive(Default)]
-LL | struct FooDefault<'a> {
+LL ~ struct FooDefault<'a> {
    |
 
 error: this `impl` can be derived
-  --> tests/ui/derivable_impls.rs:43:1
+  --> tests/ui/derivable_impls.rs:41:1
    |
 LL | / impl std::default::Default for TupleDefault {
 LL | |     fn default() -> Self {
@@ -29,15 +28,14 @@ LL | |     }
 LL | | }
    | |_^
    |
-   = help: remove the manual implementation...
-help: ...and instead derive it
+help: replace the manual implementation with a derive attribute
    |
 LL + #[derive(Default)]
-LL | struct TupleDefault(bool, i32, u64);
+LL ~ struct TupleDefault(bool, i32, u64);
    |
 
 error: this `impl` can be derived
-  --> tests/ui/derivable_impls.rs:95:1
+  --> tests/ui/derivable_impls.rs:93:1
    |
 LL | / impl Default for StrDefault<'_> {
 LL | |     fn default() -> Self {
@@ -46,15 +44,14 @@ LL | |     }
 LL | | }
    | |_^
    |
-   = help: remove the manual implementation...
-help: ...and instead derive it
+help: replace the manual implementation with a derive attribute
    |
 LL + #[derive(Default)]
-LL | struct StrDefault<'a>(&'a str);
+LL ~ struct StrDefault<'a>(&'a str);
    |
 
 error: this `impl` can be derived
-  --> tests/ui/derivable_impls.rs:121:1
+  --> tests/ui/derivable_impls.rs:119:1
    |
 LL | / impl Default for Y {
 LL | |     fn default() -> Self {
@@ -63,15 +60,14 @@ LL | |     }
 LL | | }
    | |_^
    |
-   = help: remove the manual implementation...
-help: ...and instead derive it
+help: replace the manual implementation with a derive attribute
    |
 LL + #[derive(Default)]
-LL | struct Y(u32);
+LL ~ struct Y(u32);
    |
 
 error: this `impl` can be derived
-  --> tests/ui/derivable_impls.rs:160:1
+  --> tests/ui/derivable_impls.rs:158:1
    |
 LL | / impl Default for WithoutSelfCurly {
 LL | |     fn default() -> Self {
@@ -80,15 +76,14 @@ LL | |     }
 LL | | }
    | |_^
    |
-   = help: remove the manual implementation...
-help: ...and instead derive it
+help: replace the manual implementation with a derive attribute
    |
 LL + #[derive(Default)]
-LL | struct WithoutSelfCurly {
+LL ~ struct WithoutSelfCurly {
    |
 
 error: this `impl` can be derived
-  --> tests/ui/derivable_impls.rs:168:1
+  --> tests/ui/derivable_impls.rs:166:1
    |
 LL | / impl Default for WithoutSelfParan {
 LL | |     fn default() -> Self {
@@ -97,15 +92,14 @@ LL | |     }
 LL | | }
    | |_^
    |
-   = help: remove the manual implementation...
-help: ...and instead derive it
+help: replace the manual implementation with a derive attribute
    |
 LL + #[derive(Default)]
-LL | struct WithoutSelfParan(bool);
+LL ~ struct WithoutSelfParan(bool);
    |
 
 error: this `impl` can be derived
-  --> tests/ui/derivable_impls.rs:218:1
+  --> tests/ui/derivable_impls.rs:216:1
    |
 LL | / impl Default for RepeatDefault1 {
 LL | |     fn default() -> Self {
@@ -114,15 +108,14 @@ LL | |     }
 LL | | }
    | |_^
    |
-   = help: remove the manual implementation...
-help: ...and instead derive it
+help: replace the manual implementation with a derive attribute
    |
 LL + #[derive(Default)]
-LL | pub struct RepeatDefault1 {
+LL ~ pub struct RepeatDefault1 {
    |
 
 error: this `impl` can be derived
-  --> tests/ui/derivable_impls.rs:252:1
+  --> tests/ui/derivable_impls.rs:250:1
    |
 LL | / impl Default for SimpleEnum {
 LL | |     fn default() -> Self {
@@ -131,14 +124,11 @@ LL | |     }
 LL | | }
    | |_^
    |
-   = help: remove the manual implementation...
-help: ...and instead derive it...
+help: replace the manual implementation with a derive attribute and mark the default variant
    |
 LL + #[derive(Default)]
-LL | pub enum SimpleEnum {
-   |
-help: ...and mark the default variant
-   |
+LL ~ pub enum SimpleEnum {
+LL |     Foo,
 LL ~     #[default]
 LL ~     Bar,
    |
diff --git a/src/tools/clippy/tests/ui/doc/doc_include_without_cfg.fixed b/src/tools/clippy/tests/ui/doc/doc_include_without_cfg.fixed
new file mode 100644
index 00000000000..d4ae810d738
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc/doc_include_without_cfg.fixed
@@ -0,0 +1,40 @@
+#![warn(clippy::doc_include_without_cfg)]
+// Should not lint.
+#![doc(html_playground_url = "https://playground.example.com/")]
+#![cfg_attr(doc, doc = include_str!("../approx_const.rs"))] //~ doc_include_without_cfg
+// Should not lint.
+#![cfg_attr(feature = "whatever", doc = include_str!("../approx_const.rs"))]
+#![cfg_attr(doc, doc = include_str!("../approx_const.rs"))]
+#![doc = "some doc"]
+//! more doc
+
+macro_rules! man_link {
+    ($a:literal, $b:literal) => {
+        concat!($a, $b)
+    };
+}
+
+// Should not lint!
+macro_rules! tst {
+    ($(#[$attr:meta])*) => {
+        $(#[$attr])*
+        fn blue() {
+            println!("Hello, world!");
+        }
+    }
+}
+
+tst! {
+    /// This is a test with no included file
+}
+
+#[cfg_attr(doc, doc = include_str!("../approx_const.rs"))] //~ doc_include_without_cfg
+// Should not lint.
+#[doc = man_link!("bla", "blob")]
+#[cfg_attr(feature = "whatever", doc = include_str!("../approx_const.rs"))]
+#[cfg_attr(doc, doc = include_str!("../approx_const.rs"))]
+#[doc = "some doc"]
+/// more doc
+fn main() {
+    // test code goes here
+}
diff --git a/src/tools/clippy/tests/ui/doc/doc_include_without_cfg.rs b/src/tools/clippy/tests/ui/doc/doc_include_without_cfg.rs
new file mode 100644
index 00000000000..c82f6bf2035
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc/doc_include_without_cfg.rs
@@ -0,0 +1,40 @@
+#![warn(clippy::doc_include_without_cfg)]
+// Should not lint.
+#![doc(html_playground_url = "https://playground.example.com/")]
+#![doc = include_str!("../approx_const.rs")] //~ doc_include_without_cfg
+// Should not lint.
+#![cfg_attr(feature = "whatever", doc = include_str!("../approx_const.rs"))]
+#![cfg_attr(doc, doc = include_str!("../approx_const.rs"))]
+#![doc = "some doc"]
+//! more doc
+
+macro_rules! man_link {
+    ($a:literal, $b:literal) => {
+        concat!($a, $b)
+    };
+}
+
+// Should not lint!
+macro_rules! tst {
+    ($(#[$attr:meta])*) => {
+        $(#[$attr])*
+        fn blue() {
+            println!("Hello, world!");
+        }
+    }
+}
+
+tst! {
+    /// This is a test with no included file
+}
+
+#[doc = include_str!("../approx_const.rs")] //~ doc_include_without_cfg
+// Should not lint.
+#[doc = man_link!("bla", "blob")]
+#[cfg_attr(feature = "whatever", doc = include_str!("../approx_const.rs"))]
+#[cfg_attr(doc, doc = include_str!("../approx_const.rs"))]
+#[doc = "some doc"]
+/// more doc
+fn main() {
+    // test code goes here
+}
diff --git a/src/tools/clippy/tests/ui/doc/doc_include_without_cfg.stderr b/src/tools/clippy/tests/ui/doc/doc_include_without_cfg.stderr
new file mode 100644
index 00000000000..17ea53c7c31
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc/doc_include_without_cfg.stderr
@@ -0,0 +1,17 @@
+error: included a file in documentation unconditionally
+  --> tests/ui/doc/doc_include_without_cfg.rs:4:1
+   |
+LL | #![doc = include_str!("../approx_const.rs")]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `cfg_attr(doc, doc = "...")`: `#![cfg_attr(doc, doc = include_str!("../approx_const.rs"))]`
+   |
+   = note: `-D clippy::doc-include-without-cfg` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::doc_include_without_cfg)]`
+
+error: included a file in documentation unconditionally
+  --> tests/ui/doc/doc_include_without_cfg.rs:31:1
+   |
+LL | #[doc = include_str!("../approx_const.rs")]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `cfg_attr(doc, doc = "...")`: `#[cfg_attr(doc, doc = include_str!("../approx_const.rs"))]`
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs b/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs
index 17d2ed9f50c..aa964af3fc2 100644
--- a/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs
+++ b/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs
@@ -134,4 +134,11 @@ struct Human<'a> {
     pub name: &'a str,
 }
 
+// https://github.com/rust-lang/rust-clippy/issues/13578
+mod issue_13578 {
+    pub trait Foo {}
+
+    impl<'a, T: 'a> Foo for Option<T> where &'a T: Foo {}
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/fn_address_comparisons.rs b/src/tools/clippy/tests/ui/fn_address_comparisons.rs
deleted file mode 100644
index 35535bd4fdd..00000000000
--- a/src/tools/clippy/tests/ui/fn_address_comparisons.rs
+++ /dev/null
@@ -1,23 +0,0 @@
-use std::fmt::Debug;
-use std::ptr;
-use std::rc::Rc;
-use std::sync::Arc;
-
-fn a() {}
-
-#[warn(clippy::fn_address_comparisons)]
-fn main() {
-    type F = fn();
-    let f: F = a;
-    let g: F = f;
-
-    // These should fail:
-    let _ = f == a;
-    //~^ ERROR: comparing with a non-unique address of a function item
-    //~| NOTE: `-D clippy::fn-address-comparisons` implied by `-D warnings`
-    let _ = f != a;
-    //~^ ERROR: comparing with a non-unique address of a function item
-
-    // These should be fine:
-    let _ = f == g;
-}
diff --git a/src/tools/clippy/tests/ui/fn_address_comparisons.stderr b/src/tools/clippy/tests/ui/fn_address_comparisons.stderr
deleted file mode 100644
index e6de7e35807..00000000000
--- a/src/tools/clippy/tests/ui/fn_address_comparisons.stderr
+++ /dev/null
@@ -1,17 +0,0 @@
-error: comparing with a non-unique address of a function item
-  --> tests/ui/fn_address_comparisons.rs:15:13
-   |
-LL |     let _ = f == a;
-   |             ^^^^^^
-   |
-   = note: `-D clippy::fn-address-comparisons` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::fn_address_comparisons)]`
-
-error: comparing with a non-unique address of a function item
-  --> tests/ui/fn_address_comparisons.rs:18:13
-   |
-LL |     let _ = f != a;
-   |             ^^^^^^
-
-error: aborting due to 2 previous errors
-
diff --git a/src/tools/clippy/tests/ui/format_args_unfixable.rs b/src/tools/clippy/tests/ui/format_args_unfixable.rs
index f04715f4f01..7590de3751a 100644
--- a/src/tools/clippy/tests/ui/format_args_unfixable.rs
+++ b/src/tools/clippy/tests/ui/format_args_unfixable.rs
@@ -119,3 +119,32 @@ fn test2() {
         format!("something failed at {}", Location::caller())
     );
 }
+
+#[clippy::format_args]
+macro_rules! usr_println {
+    ($target:expr, $($args:tt)*) => {{
+        if $target {
+            println!($($args)*)
+        }
+    }};
+}
+
+fn user_format() {
+    let error = Error::new(ErrorKind::Other, "bad thing");
+    let x = 'x';
+
+    usr_println!(true, "error: {}", format!("boom at {}", Location::caller()));
+    //~^ ERROR: `format!` in `usr_println!` args
+    usr_println!(true, "{}: {}", error, format!("boom at {}", Location::caller()));
+    //~^ ERROR: `format!` in `usr_println!` args
+    usr_println!(true, "{:?}: {}", error, format!("boom at {}", Location::caller()));
+    //~^ ERROR: `format!` in `usr_println!` args
+    usr_println!(true, "{{}}: {}", format!("boom at {}", Location::caller()));
+    //~^ ERROR: `format!` in `usr_println!` args
+    usr_println!(true, r#"error: "{}""#, format!("boom at {}", Location::caller()));
+    //~^ ERROR: `format!` in `usr_println!` args
+    usr_println!(true, "error: {}", format!(r#"boom at "{}""#, Location::caller()));
+    //~^ ERROR: `format!` in `usr_println!` args
+    usr_println!(true, "error: {}", format!("boom at {} {0}", Location::caller()));
+    //~^ ERROR: `format!` in `usr_println!` args
+}
diff --git a/src/tools/clippy/tests/ui/format_args_unfixable.stderr b/src/tools/clippy/tests/ui/format_args_unfixable.stderr
index 20cd0bb8c55..1b4b683fd6c 100644
--- a/src/tools/clippy/tests/ui/format_args_unfixable.stderr
+++ b/src/tools/clippy/tests/ui/format_args_unfixable.stderr
@@ -174,5 +174,68 @@ LL |     panic!("error: {}", format!("something failed at {}", Location::caller(
    = help: combine the `format!(..)` arguments with the outer `panic!(..)` call
    = help: or consider changing `format!` to `format_args!`
 
-error: aborting due to 18 previous errors
+error: `format!` in `usr_println!` args
+  --> tests/ui/format_args_unfixable.rs:136:5
+   |
+LL |     usr_println!(true, "error: {}", format!("boom at {}", Location::caller()));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: combine the `format!(..)` arguments with the outer `usr_println!(..)` call
+   = help: or consider changing `format!` to `format_args!`
+
+error: `format!` in `usr_println!` args
+  --> tests/ui/format_args_unfixable.rs:138:5
+   |
+LL |     usr_println!(true, "{}: {}", error, format!("boom at {}", Location::caller()));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: combine the `format!(..)` arguments with the outer `usr_println!(..)` call
+   = help: or consider changing `format!` to `format_args!`
+
+error: `format!` in `usr_println!` args
+  --> tests/ui/format_args_unfixable.rs:140:5
+   |
+LL |     usr_println!(true, "{:?}: {}", error, format!("boom at {}", Location::caller()));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: combine the `format!(..)` arguments with the outer `usr_println!(..)` call
+   = help: or consider changing `format!` to `format_args!`
+
+error: `format!` in `usr_println!` args
+  --> tests/ui/format_args_unfixable.rs:142:5
+   |
+LL |     usr_println!(true, "{{}}: {}", format!("boom at {}", Location::caller()));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: combine the `format!(..)` arguments with the outer `usr_println!(..)` call
+   = help: or consider changing `format!` to `format_args!`
+
+error: `format!` in `usr_println!` args
+  --> tests/ui/format_args_unfixable.rs:144:5
+   |
+LL |     usr_println!(true, r#"error: "{}""#, format!("boom at {}", Location::caller()));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: combine the `format!(..)` arguments with the outer `usr_println!(..)` call
+   = help: or consider changing `format!` to `format_args!`
+
+error: `format!` in `usr_println!` args
+  --> tests/ui/format_args_unfixable.rs:146:5
+   |
+LL |     usr_println!(true, "error: {}", format!(r#"boom at "{}""#, Location::caller()));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: combine the `format!(..)` arguments with the outer `usr_println!(..)` call
+   = help: or consider changing `format!` to `format_args!`
+
+error: `format!` in `usr_println!` args
+  --> tests/ui/format_args_unfixable.rs:148:5
+   |
+LL |     usr_println!(true, "error: {}", format!("boom at {} {0}", Location::caller()));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: combine the `format!(..)` arguments with the outer `usr_println!(..)` call
+   = help: or consider changing `format!` to `format_args!`
+
+error: aborting due to 25 previous errors
 
diff --git a/src/tools/clippy/tests/ui/future_not_send.rs b/src/tools/clippy/tests/ui/future_not_send.rs
index 9274340b5ca..626ee6de9e4 100644
--- a/src/tools/clippy/tests/ui/future_not_send.rs
+++ b/src/tools/clippy/tests/ui/future_not_send.rs
@@ -1,6 +1,7 @@
 #![warn(clippy::future_not_send)]
 
 use std::cell::Cell;
+use std::future::Future;
 use std::rc::Rc;
 use std::sync::Arc;
 
@@ -63,6 +64,22 @@ where
     t
 }
 
+async fn maybe_send_generic_future<T>(t: T) -> T {
+    async { true }.await;
+    t
+}
+
+async fn maybe_send_generic_future2<F: Fn() -> Fut, Fut: Future>(f: F) {
+    async { true }.await;
+    let res = f();
+    async { true }.await;
+}
+
+async fn generic_future_always_unsend<T>(_: Rc<T>) {
+    //~^ ERROR: future cannot be sent between threads safely
+    async { true }.await;
+}
+
 async fn generic_future_send<T>(t: T)
 where
     T: Send,
@@ -71,7 +88,6 @@ where
 }
 
 async fn unclear_future<T>(t: T) {}
-//~^ ERROR: future cannot be sent between threads safely
 
 fn main() {
     let rc = Rc::new([1, 2, 3]);
diff --git a/src/tools/clippy/tests/ui/future_not_send.stderr b/src/tools/clippy/tests/ui/future_not_send.stderr
index 67677d6367a..3807c747013 100644
--- a/src/tools/clippy/tests/ui/future_not_send.stderr
+++ b/src/tools/clippy/tests/ui/future_not_send.stderr
@@ -1,11 +1,11 @@
 error: future cannot be sent between threads safely
-  --> tests/ui/future_not_send.rs:7:1
+  --> tests/ui/future_not_send.rs:8:1
    |
 LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `private_future` is not `Send`
    |
 note: future is not `Send` as this value is used across an await
-  --> tests/ui/future_not_send.rs:9:20
+  --> tests/ui/future_not_send.rs:10:20
    |
 LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
    |                         -- has type `std::rc::Rc<[u8]>` which is not `Send`
@@ -14,7 +14,7 @@ LL |     async { true }.await
    |                    ^^^^^ await occurs here, with `rc` maybe used later
    = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send`
 note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync`
-  --> tests/ui/future_not_send.rs:7:39
+  --> tests/ui/future_not_send.rs:8:39
    |
 LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
    |                                       ^^^^ has type `&std::cell::Cell<usize>` which is not `Send`, because `std::cell::Cell<usize>` is not `Sync`
@@ -23,13 +23,13 @@ LL | async fn private_future(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
    = help: to override `-D warnings` add `#[allow(clippy::future_not_send)]`
 
 error: future cannot be sent between threads safely
-  --> tests/ui/future_not_send.rs:12:1
+  --> tests/ui/future_not_send.rs:13:1
    |
 LL | pub async fn public_future(rc: Rc<[u8]>) {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `public_future` is not `Send`
    |
 note: future is not `Send` as this value is used across an await
-  --> tests/ui/future_not_send.rs:14:20
+  --> tests/ui/future_not_send.rs:15:20
    |
 LL | pub async fn public_future(rc: Rc<[u8]>) {
    |                            -- has type `std::rc::Rc<[u8]>` which is not `Send`
@@ -39,45 +39,45 @@ LL |     async { true }.await;
    = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send`
 
 error: future cannot be sent between threads safely
-  --> tests/ui/future_not_send.rs:21:1
+  --> tests/ui/future_not_send.rs:22:1
    |
 LL | async fn private_future2(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `private_future2` is not `Send`
    |
 note: captured value is not `Send`
-  --> tests/ui/future_not_send.rs:21:26
+  --> tests/ui/future_not_send.rs:22:26
    |
 LL | async fn private_future2(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
    |                          ^^ has type `std::rc::Rc<[u8]>` which is not `Send`
    = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send`
 note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync`
-  --> tests/ui/future_not_send.rs:21:40
+  --> tests/ui/future_not_send.rs:22:40
    |
 LL | async fn private_future2(rc: Rc<[u8]>, cell: &Cell<usize>) -> bool {
    |                                        ^^^^ has type `&std::cell::Cell<usize>` which is not `Send`, because `std::cell::Cell<usize>` is not `Sync`
    = note: `std::cell::Cell<usize>` doesn't implement `std::marker::Sync`
 
 error: future cannot be sent between threads safely
-  --> tests/ui/future_not_send.rs:26:1
+  --> tests/ui/future_not_send.rs:27:1
    |
 LL | pub async fn public_future2(rc: Rc<[u8]>) {}
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `public_future2` is not `Send`
    |
 note: captured value is not `Send`
-  --> tests/ui/future_not_send.rs:26:29
+  --> tests/ui/future_not_send.rs:27:29
    |
 LL | pub async fn public_future2(rc: Rc<[u8]>) {}
    |                             ^^ has type `std::rc::Rc<[u8]>` which is not `Send`
    = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Send`
 
 error: future cannot be sent between threads safely
-  --> tests/ui/future_not_send.rs:38:5
+  --> tests/ui/future_not_send.rs:39:5
    |
 LL |     async fn private_future(&self) -> usize {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `private_future` is not `Send`
    |
 note: future is not `Send` as this value is used across an await
-  --> tests/ui/future_not_send.rs:40:24
+  --> tests/ui/future_not_send.rs:41:24
    |
 LL |     async fn private_future(&self) -> usize {
    |                             ----- has type `&Dummy` which is not `Send`
@@ -87,20 +87,20 @@ LL |         async { true }.await;
    = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Sync`
 
 error: future cannot be sent between threads safely
-  --> tests/ui/future_not_send.rs:44:5
+  --> tests/ui/future_not_send.rs:45:5
    |
 LL |     pub async fn public_future(&self) {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `public_future` is not `Send`
    |
 note: captured value is not `Send` because `&` references cannot be sent unless their referent is `Sync`
-  --> tests/ui/future_not_send.rs:44:32
+  --> tests/ui/future_not_send.rs:45:32
    |
 LL |     pub async fn public_future(&self) {
    |                                ^^^^^ has type `&Dummy` which is not `Send`, because `Dummy` is not `Sync`
    = note: `std::rc::Rc<[u8]>` doesn't implement `std::marker::Sync`
 
 error: future cannot be sent between threads safely
-  --> tests/ui/future_not_send.rs:55:1
+  --> tests/ui/future_not_send.rs:56:1
    |
 LL | / async fn generic_future<T>(t: T) -> T
 LL | |
@@ -109,7 +109,7 @@ LL | |     T: Send,
    | |____________^ future returned by `generic_future` is not `Send`
    |
 note: future is not `Send` as this value is used across an await
-  --> tests/ui/future_not_send.rs:61:20
+  --> tests/ui/future_not_send.rs:62:20
    |
 LL |     let rt = &t;
    |         -- has type `&T` which is not `Send`
@@ -118,17 +118,20 @@ LL |     async { true }.await;
    = note: `T` doesn't implement `std::marker::Sync`
 
 error: future cannot be sent between threads safely
-  --> tests/ui/future_not_send.rs:73:1
+  --> tests/ui/future_not_send.rs:78:1
    |
-LL | async fn unclear_future<T>(t: T) {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `unclear_future` is not `Send`
+LL | async fn generic_future_always_unsend<T>(_: Rc<T>) {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future returned by `generic_future_always_unsend` is not `Send`
    |
-note: captured value is not `Send`
-  --> tests/ui/future_not_send.rs:73:28
+note: future is not `Send` as this value is used across an await
+  --> tests/ui/future_not_send.rs:80:20
    |
-LL | async fn unclear_future<T>(t: T) {}
-   |                            ^ has type `T` which is not `Send`
-   = note: `T` doesn't implement `std::marker::Send`
+LL | async fn generic_future_always_unsend<T>(_: Rc<T>) {
+   |                                          - has type `std::rc::Rc<T>` which is not `Send`
+LL |
+LL |     async { true }.await;
+   |                    ^^^^^ await occurs here, with `_` maybe used later
+   = note: `std::rc::Rc<T>` doesn't implement `std::marker::Send`
 
 error: aborting due to 8 previous errors
 
diff --git a/src/tools/clippy/tests/ui/if_let_mutex.stderr b/src/tools/clippy/tests/ui/if_let_mutex.edition2021.stderr
index 45df4ac4d67..984d6adbb2a 100644
--- a/src/tools/clippy/tests/ui/if_let_mutex.stderr
+++ b/src/tools/clippy/tests/ui/if_let_mutex.edition2021.stderr
@@ -1,5 +1,5 @@
 error: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock
-  --> tests/ui/if_let_mutex.rs:11:5
+  --> tests/ui/if_let_mutex.rs:16:5
    |
 LL |       if let Err(locked) = m.lock() {
    |       ^                    - this Mutex will remain locked for the entire `if let`-block...
@@ -19,7 +19,7 @@ LL | |     };
    = help: to override `-D warnings` add `#[allow(clippy::if_let_mutex)]`
 
 error: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock
-  --> tests/ui/if_let_mutex.rs:24:5
+  --> tests/ui/if_let_mutex.rs:29:5
    |
 LL |       if let Some(locked) = m.lock().unwrap().deref() {
    |       ^                     - this Mutex will remain locked for the entire `if let`-block...
@@ -37,7 +37,7 @@ LL | |     };
    = help: move the lock call outside of the `if let ...` expression
 
 error: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock
-  --> tests/ui/if_let_mutex.rs:46:5
+  --> tests/ui/if_let_mutex.rs:51:5
    |
 LL |       if let Ok(i) = mutex.lock() {
    |       ^              ----- this Mutex will remain locked for the entire `if let`-block...
@@ -54,7 +54,7 @@ LL | |     };
    = help: move the lock call outside of the `if let ...` expression
 
 error: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock
-  --> tests/ui/if_let_mutex.rs:55:5
+  --> tests/ui/if_let_mutex.rs:60:5
    |
 LL |       if let Ok(_) = m1.lock() {
    |       ^              -- this Mutex will remain locked for the entire `if let`-block...
diff --git a/src/tools/clippy/tests/ui/if_let_mutex.rs b/src/tools/clippy/tests/ui/if_let_mutex.rs
index bb0eadfca1c..80eee293989 100644
--- a/src/tools/clippy/tests/ui/if_let_mutex.rs
+++ b/src/tools/clippy/tests/ui/if_let_mutex.rs
@@ -1,3 +1,8 @@
+//@ compile-flags: -Zunstable-options
+
+//@revisions: edition2021 edition2024
+//@[edition2021] edition:2021
+//@[edition2024] edition:2024
 #![warn(clippy::if_let_mutex)]
 #![allow(clippy::redundant_pattern_matching)]
 
@@ -9,7 +14,7 @@ fn do_stuff<T>(_: T) {}
 fn if_let() {
     let m = Mutex::new(1_u8);
     if let Err(locked) = m.lock() {
-        //~^ ERROR: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a d
+        //~[edition2021]^ if_let_mutex
         do_stuff(locked);
     } else {
         let lock = m.lock().unwrap();
@@ -22,7 +27,7 @@ fn if_let() {
 fn if_let_option() {
     let m = Mutex::new(Some(0_u8));
     if let Some(locked) = m.lock().unwrap().deref() {
-        //~^ ERROR: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a d
+        //~[edition2021]^ if_let_mutex
         do_stuff(locked);
     } else {
         let lock = m.lock().unwrap();
@@ -44,7 +49,7 @@ fn if_let_different_mutex() {
 
 fn mutex_ref(mutex: &Mutex<i32>) {
     if let Ok(i) = mutex.lock() {
-        //~^ ERROR: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a d
+        //~[edition2021]^ if_let_mutex
         do_stuff(i);
     } else {
         let _x = mutex.lock();
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed
index 754fe061c4a..014fbb85c7a 100644
--- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed
@@ -1,6 +1,6 @@
 #![warn(clippy::missing_const_for_fn)]
 #![allow(incomplete_features, clippy::let_and_return, clippy::missing_transmute_annotations)]
-#![feature(const_trait_impl, abi_vectorcall)]
+#![feature(const_trait_impl)]
 
 use std::mem::transmute;
 
@@ -211,8 +211,4 @@ mod extern_fn {
     //~^ ERROR: this could be a `const fn`
     const extern "system-unwind" fn system_unwind() {}
     //~^ ERROR: this could be a `const fn`
-    pub const extern "vectorcall" fn std_call() {}
-    //~^ ERROR: this could be a `const fn`
-    pub const extern "vectorcall-unwind" fn std_call_unwind() {}
-    //~^ ERROR: this could be a `const fn`
 }
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs
index 460be0733e0..4f7c2cbcf0b 100644
--- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs
@@ -1,6 +1,6 @@
 #![warn(clippy::missing_const_for_fn)]
 #![allow(incomplete_features, clippy::let_and_return, clippy::missing_transmute_annotations)]
-#![feature(const_trait_impl, abi_vectorcall)]
+#![feature(const_trait_impl)]
 
 use std::mem::transmute;
 
@@ -211,8 +211,4 @@ mod extern_fn {
     //~^ ERROR: this could be a `const fn`
     extern "system-unwind" fn system_unwind() {}
     //~^ ERROR: this could be a `const fn`
-    pub extern "vectorcall" fn std_call() {}
-    //~^ ERROR: this could be a `const fn`
-    pub extern "vectorcall-unwind" fn std_call_unwind() {}
-    //~^ ERROR: this could be a `const fn`
 }
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr
index d553c522556..cc7dfd0888d 100644
--- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr
@@ -316,27 +316,5 @@ help: make the function `const`
 LL |     const extern "system-unwind" fn system_unwind() {}
    |     +++++
 
-error: this could be a `const fn`
-  --> tests/ui/missing_const_for_fn/could_be_const.rs:214:5
-   |
-LL |     pub extern "vectorcall" fn std_call() {}
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: make the function `const`
-   |
-LL |     pub const extern "vectorcall" fn std_call() {}
-   |         +++++
-
-error: this could be a `const fn`
-  --> tests/ui/missing_const_for_fn/could_be_const.rs:216:5
-   |
-LL |     pub extern "vectorcall-unwind" fn std_call_unwind() {}
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-help: make the function `const`
-   |
-LL |     pub const extern "vectorcall-unwind" fn std_call_unwind() {}
-   |         +++++
-
-error: aborting due to 26 previous errors
+error: aborting due to 24 previous errors
 
diff --git a/src/tools/clippy/tests/ui/missing_doc_crate.rs b/src/tools/clippy/tests/ui/missing_doc_crate.rs
index e00c7fbfed1..fdb23af279d 100644
--- a/src/tools/clippy/tests/ui/missing_doc_crate.rs
+++ b/src/tools/clippy/tests/ui/missing_doc_crate.rs
@@ -1,4 +1,5 @@
 #![warn(clippy::missing_docs_in_private_items)]
+#![allow(clippy::doc_include_without_cfg)]
 #![doc = include_str!("../../README.md")]
 
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/redundant_guards.fixed b/src/tools/clippy/tests/ui/redundant_guards.fixed
index ed4b1c21915..ff7b233f004 100644
--- a/src/tools/clippy/tests/ui/redundant_guards.fixed
+++ b/src/tools/clippy/tests/ui/redundant_guards.fixed
@@ -1,6 +1,6 @@
 //@aux-build:proc_macros.rs
 #![feature(if_let_guard)]
-#![allow(clippy::no_effect, unused, clippy::single_match)]
+#![allow(clippy::no_effect, unused, clippy::single_match, invalid_nan_comparisons)]
 #![warn(clippy::redundant_guards)]
 
 #[macro_use]
@@ -19,15 +19,24 @@ struct FloatWrapper(f32);
 
 fn issue11304() {
     match 0.1 {
-        x if x == 0.0 => todo!(),
+        0.0 => todo!(),
+        // Pattern matching NAN is illegal
+        x if x == f64::NAN => todo!(),
         _ => todo!(),
     }
     match FloatWrapper(0.1) {
-        x if x == FloatWrapper(0.0) => todo!(),
+        FloatWrapper(0.0) => todo!(),
         _ => todo!(),
     }
 }
 
+fn issue13681() {
+    match c"hi" {
+        x if x == c"hi" => (),
+        _ => (),
+    }
+}
+
 fn main() {
     let c = C(1, 2);
     match c {
diff --git a/src/tools/clippy/tests/ui/redundant_guards.rs b/src/tools/clippy/tests/ui/redundant_guards.rs
index adbc4ed16cd..b4d4ef5b170 100644
--- a/src/tools/clippy/tests/ui/redundant_guards.rs
+++ b/src/tools/clippy/tests/ui/redundant_guards.rs
@@ -1,6 +1,6 @@
 //@aux-build:proc_macros.rs
 #![feature(if_let_guard)]
-#![allow(clippy::no_effect, unused, clippy::single_match)]
+#![allow(clippy::no_effect, unused, clippy::single_match, invalid_nan_comparisons)]
 #![warn(clippy::redundant_guards)]
 
 #[macro_use]
@@ -20,6 +20,8 @@ struct FloatWrapper(f32);
 fn issue11304() {
     match 0.1 {
         x if x == 0.0 => todo!(),
+        // Pattern matching NAN is illegal
+        x if x == f64::NAN => todo!(),
         _ => todo!(),
     }
     match FloatWrapper(0.1) {
@@ -28,6 +30,13 @@ fn issue11304() {
     }
 }
 
+fn issue13681() {
+    match c"hi" {
+        x if x == c"hi" => (),
+        _ => (),
+    }
+}
+
 fn main() {
     let c = C(1, 2);
     match c {
diff --git a/src/tools/clippy/tests/ui/redundant_guards.stderr b/src/tools/clippy/tests/ui/redundant_guards.stderr
index fd12e083282..7512546450b 100644
--- a/src/tools/clippy/tests/ui/redundant_guards.stderr
+++ b/src/tools/clippy/tests/ui/redundant_guards.stderr
@@ -1,19 +1,43 @@
 error: redundant guard
-  --> tests/ui/redundant_guards.rs:34:20
+  --> tests/ui/redundant_guards.rs:22:14
    |
-LL |         C(x, y) if let 1 = y => ..,
-   |                    ^^^^^^^^^
+LL |         x if x == 0.0 => todo!(),
+   |              ^^^^^^^^
    |
    = note: `-D clippy::redundant-guards` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::redundant_guards)]`
 help: try
    |
+LL -         x if x == 0.0 => todo!(),
+LL +         0.0 => todo!(),
+   |
+
+error: redundant guard
+  --> tests/ui/redundant_guards.rs:28:14
+   |
+LL |         x if x == FloatWrapper(0.0) => todo!(),
+   |              ^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL -         x if x == FloatWrapper(0.0) => todo!(),
+LL +         FloatWrapper(0.0) => todo!(),
+   |
+
+error: redundant guard
+  --> tests/ui/redundant_guards.rs:43:20
+   |
+LL |         C(x, y) if let 1 = y => ..,
+   |                    ^^^^^^^^^
+   |
+help: try
+   |
 LL -         C(x, y) if let 1 = y => ..,
 LL +         C(x, 1) => ..,
    |
 
 error: redundant guard
-  --> tests/ui/redundant_guards.rs:40:20
+  --> tests/ui/redundant_guards.rs:49:20
    |
 LL |         Some(x) if matches!(x, Some(1) if true) => ..,
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -24,7 +48,7 @@ LL |         Some(Some(1)) if true => ..,
    |              ~~~~~~~  ~~~~~~~
 
 error: redundant guard
-  --> tests/ui/redundant_guards.rs:41:20
+  --> tests/ui/redundant_guards.rs:50:20
    |
 LL |         Some(x) if matches!(x, Some(1)) => {
    |                    ^^^^^^^^^^^^^^^^^^^^
@@ -36,7 +60,7 @@ LL +         Some(Some(1)) => {
    |
 
 error: redundant guard
-  --> tests/ui/redundant_guards.rs:45:20
+  --> tests/ui/redundant_guards.rs:54:20
    |
 LL |         Some(x) if let Some(1) = x => ..,
    |                    ^^^^^^^^^^^^^^^
@@ -48,7 +72,7 @@ LL +         Some(Some(1)) => ..,
    |
 
 error: redundant guard
-  --> tests/ui/redundant_guards.rs:46:20
+  --> tests/ui/redundant_guards.rs:55:20
    |
 LL |         Some(x) if x == Some(2) => ..,
    |                    ^^^^^^^^^^^^
@@ -60,7 +84,7 @@ LL +         Some(Some(2)) => ..,
    |
 
 error: redundant guard
-  --> tests/ui/redundant_guards.rs:47:20
+  --> tests/ui/redundant_guards.rs:56:20
    |
 LL |         Some(x) if Some(2) == x => ..,
    |                    ^^^^^^^^^^^^
@@ -72,7 +96,7 @@ LL +         Some(Some(2)) => ..,
    |
 
 error: redundant guard
-  --> tests/ui/redundant_guards.rs:72:20
+  --> tests/ui/redundant_guards.rs:81:20
    |
 LL |         B { e } if matches!(e, Some(A(2))) => ..,
    |                    ^^^^^^^^^^^^^^^^^^^^^^^
@@ -84,7 +108,7 @@ LL +         B { e: Some(A(2)) } => ..,
    |
 
 error: redundant guard
-  --> tests/ui/redundant_guards.rs:109:20
+  --> tests/ui/redundant_guards.rs:118:20
    |
 LL |         E::A(y) if y == "not from an or pattern" => {},
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -96,7 +120,7 @@ LL +         E::A("not from an or pattern") => {},
    |
 
 error: redundant guard
-  --> tests/ui/redundant_guards.rs:116:14
+  --> tests/ui/redundant_guards.rs:125:14
    |
 LL |         x if matches!(x, Some(0)) => ..,
    |              ^^^^^^^^^^^^^^^^^^^^
@@ -108,7 +132,7 @@ LL +         Some(0) => ..,
    |
 
 error: redundant guard
-  --> tests/ui/redundant_guards.rs:123:14
+  --> tests/ui/redundant_guards.rs:132:14
    |
 LL |         i if i == -1 => {},
    |              ^^^^^^^
@@ -120,7 +144,7 @@ LL +         -1 => {},
    |
 
 error: redundant guard
-  --> tests/ui/redundant_guards.rs:124:14
+  --> tests/ui/redundant_guards.rs:133:14
    |
 LL |         i if i == 1 => {},
    |              ^^^^^^
@@ -132,7 +156,7 @@ LL +         1 => {},
    |
 
 error: redundant guard
-  --> tests/ui/redundant_guards.rs:186:28
+  --> tests/ui/redundant_guards.rs:195:28
    |
 LL |             Some(ref x) if x == &1 => {},
    |                            ^^^^^^^
@@ -144,7 +168,7 @@ LL +             Some(1) => {},
    |
 
 error: redundant guard
-  --> tests/ui/redundant_guards.rs:187:28
+  --> tests/ui/redundant_guards.rs:196:28
    |
 LL |             Some(ref x) if &1 == x => {},
    |                            ^^^^^^^
@@ -156,7 +180,7 @@ LL +             Some(1) => {},
    |
 
 error: redundant guard
-  --> tests/ui/redundant_guards.rs:188:28
+  --> tests/ui/redundant_guards.rs:197:28
    |
 LL |             Some(ref x) if let &2 = x => {},
    |                            ^^^^^^^^^^
@@ -168,7 +192,7 @@ LL +             Some(2) => {},
    |
 
 error: redundant guard
-  --> tests/ui/redundant_guards.rs:189:28
+  --> tests/ui/redundant_guards.rs:198:28
    |
 LL |             Some(ref x) if matches!(x, &3) => {},
    |                            ^^^^^^^^^^^^^^^
@@ -180,7 +204,7 @@ LL +             Some(3) => {},
    |
 
 error: redundant guard
-  --> tests/ui/redundant_guards.rs:209:32
+  --> tests/ui/redundant_guards.rs:218:32
    |
 LL |             B { ref c, .. } if c == &1 => {},
    |                                ^^^^^^^
@@ -192,7 +216,7 @@ LL +             B { c: 1, .. } => {},
    |
 
 error: redundant guard
-  --> tests/ui/redundant_guards.rs:210:32
+  --> tests/ui/redundant_guards.rs:219:32
    |
 LL |             B { ref c, .. } if &1 == c => {},
    |                                ^^^^^^^
@@ -204,7 +228,7 @@ LL +             B { c: 1, .. } => {},
    |
 
 error: redundant guard
-  --> tests/ui/redundant_guards.rs:211:32
+  --> tests/ui/redundant_guards.rs:220:32
    |
 LL |             B { ref c, .. } if let &1 = c => {},
    |                                ^^^^^^^^^^
@@ -216,7 +240,7 @@ LL +             B { c: 1, .. } => {},
    |
 
 error: redundant guard
-  --> tests/ui/redundant_guards.rs:212:32
+  --> tests/ui/redundant_guards.rs:221:32
    |
 LL |             B { ref c, .. } if matches!(c, &1) => {},
    |                                ^^^^^^^^^^^^^^^
@@ -228,7 +252,7 @@ LL +             B { c: 1, .. } => {},
    |
 
 error: redundant guard
-  --> tests/ui/redundant_guards.rs:222:26
+  --> tests/ui/redundant_guards.rs:231:26
    |
 LL |         Some(Some(x)) if x.is_empty() => {},
    |                          ^^^^^^^^^^^^
@@ -240,7 +264,7 @@ LL +         Some(Some("")) => {},
    |
 
 error: redundant guard
-  --> tests/ui/redundant_guards.rs:233:26
+  --> tests/ui/redundant_guards.rs:242:26
    |
 LL |         Some(Some(x)) if x.is_empty() => {},
    |                          ^^^^^^^^^^^^
@@ -252,7 +276,7 @@ LL +         Some(Some([])) => {},
    |
 
 error: redundant guard
-  --> tests/ui/redundant_guards.rs:238:26
+  --> tests/ui/redundant_guards.rs:247:26
    |
 LL |         Some(Some(x)) if x.is_empty() => {},
    |                          ^^^^^^^^^^^^
@@ -264,7 +288,7 @@ LL +         Some(Some([])) => {},
    |
 
 error: redundant guard
-  --> tests/ui/redundant_guards.rs:249:26
+  --> tests/ui/redundant_guards.rs:258:26
    |
 LL |         Some(Some(x)) if x.starts_with(&[]) => {},
    |                          ^^^^^^^^^^^^^^^^^^
@@ -276,7 +300,7 @@ LL +         Some(Some([..])) => {},
    |
 
 error: redundant guard
-  --> tests/ui/redundant_guards.rs:254:26
+  --> tests/ui/redundant_guards.rs:263:26
    |
 LL |         Some(Some(x)) if x.starts_with(&[1]) => {},
    |                          ^^^^^^^^^^^^^^^^^^^
@@ -288,7 +312,7 @@ LL +         Some(Some([1, ..])) => {},
    |
 
 error: redundant guard
-  --> tests/ui/redundant_guards.rs:259:26
+  --> tests/ui/redundant_guards.rs:268:26
    |
 LL |         Some(Some(x)) if x.starts_with(&[1, 2]) => {},
    |                          ^^^^^^^^^^^^^^^^^^^^^^
@@ -300,7 +324,7 @@ LL +         Some(Some([1, 2, ..])) => {},
    |
 
 error: redundant guard
-  --> tests/ui/redundant_guards.rs:264:26
+  --> tests/ui/redundant_guards.rs:273:26
    |
 LL |         Some(Some(x)) if x.ends_with(&[1, 2]) => {},
    |                          ^^^^^^^^^^^^^^^^^^^^
@@ -312,7 +336,7 @@ LL +         Some(Some([.., 1, 2])) => {},
    |
 
 error: redundant guard
-  --> tests/ui/redundant_guards.rs:286:18
+  --> tests/ui/redundant_guards.rs:295:18
    |
 LL |             y if y.is_empty() => {},
    |                  ^^^^^^^^^^^^
@@ -324,7 +348,7 @@ LL +             "" => {},
    |
 
 error: redundant guard
-  --> tests/ui/redundant_guards.rs:305:22
+  --> tests/ui/redundant_guards.rs:314:22
    |
 LL |                 y if y.is_empty() => {},
    |                      ^^^^^^^^^^^^
@@ -335,5 +359,5 @@ LL -                 y if y.is_empty() => {},
 LL +                 "" => {},
    |
 
-error: aborting due to 28 previous errors
+error: aborting due to 30 previous errors
 
diff --git a/src/tools/clippy/tests/ui/rename.fixed b/src/tools/clippy/tests/ui/rename.fixed
index 0d6e07aa546..47149622ef7 100644
--- a/src/tools/clippy/tests/ui/rename.fixed
+++ b/src/tools/clippy/tests/ui/rename.fixed
@@ -59,6 +59,7 @@
 #![allow(unknown_lints)]
 #![allow(unused_labels)]
 #![allow(ambiguous_wide_pointer_comparisons)]
+#![allow(unpredictable_function_pointer_comparisons)]
 #![allow(clippy::reversed_empty_ranges)]
 #![warn(clippy::almost_complete_range)] //~ ERROR: lint `clippy::almost_complete_letter_range`
 #![warn(clippy::disallowed_names)] //~ ERROR: lint `clippy::blacklisted_name`
@@ -74,6 +75,7 @@
 #![warn(clippy::mixed_read_write_in_expression)] //~ ERROR: lint `clippy::eval_order_dependence`
 #![warn(clippy::manual_find_map)] //~ ERROR: lint `clippy::find_map`
 #![warn(clippy::manual_filter_map)] //~ ERROR: lint `clippy::filter_map`
+#![warn(unpredictable_function_pointer_comparisons)] //~ ERROR: lint `clippy::fn_address_comparisons`
 #![warn(clippy::useless_conversion)] //~ ERROR: lint `clippy::identity_conversion`
 #![warn(clippy::redundant_pattern_matching)] //~ ERROR: lint `clippy::if_let_redundant_pattern_matching`
 #![warn(clippy::match_result_ok)] //~ ERROR: lint `clippy::if_let_some_result`
diff --git a/src/tools/clippy/tests/ui/rename.rs b/src/tools/clippy/tests/ui/rename.rs
index 2ac59718786..7a78a5d280d 100644
--- a/src/tools/clippy/tests/ui/rename.rs
+++ b/src/tools/clippy/tests/ui/rename.rs
@@ -59,6 +59,7 @@
 #![allow(unknown_lints)]
 #![allow(unused_labels)]
 #![allow(ambiguous_wide_pointer_comparisons)]
+#![allow(unpredictable_function_pointer_comparisons)]
 #![allow(clippy::reversed_empty_ranges)]
 #![warn(clippy::almost_complete_letter_range)] //~ ERROR: lint `clippy::almost_complete_letter_range`
 #![warn(clippy::blacklisted_name)] //~ ERROR: lint `clippy::blacklisted_name`
@@ -74,6 +75,7 @@
 #![warn(clippy::eval_order_dependence)] //~ ERROR: lint `clippy::eval_order_dependence`
 #![warn(clippy::find_map)] //~ ERROR: lint `clippy::find_map`
 #![warn(clippy::filter_map)] //~ ERROR: lint `clippy::filter_map`
+#![warn(clippy::fn_address_comparisons)] //~ ERROR: lint `clippy::fn_address_comparisons`
 #![warn(clippy::identity_conversion)] //~ ERROR: lint `clippy::identity_conversion`
 #![warn(clippy::if_let_redundant_pattern_matching)] //~ ERROR: lint `clippy::if_let_redundant_pattern_matching`
 #![warn(clippy::if_let_some_result)] //~ ERROR: lint `clippy::if_let_some_result`
diff --git a/src/tools/clippy/tests/ui/rename.stderr b/src/tools/clippy/tests/ui/rename.stderr
index a5ae727b7ee..dc24bc16d0e 100644
--- a/src/tools/clippy/tests/ui/rename.stderr
+++ b/src/tools/clippy/tests/ui/rename.stderr
@@ -1,5 +1,5 @@
 error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range`
-  --> tests/ui/rename.rs:63:9
+  --> tests/ui/rename.rs:64:9
    |
 LL | #![warn(clippy::almost_complete_letter_range)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range`
@@ -8,394 +8,400 @@ LL | #![warn(clippy::almost_complete_letter_range)]
    = help: to override `-D warnings` add `#[allow(renamed_and_removed_lints)]`
 
 error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names`
-  --> tests/ui/rename.rs:64:9
+  --> tests/ui/rename.rs:65:9
    |
 LL | #![warn(clippy::blacklisted_name)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names`
 
 error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_conditions`
-  --> tests/ui/rename.rs:65:9
+  --> tests/ui/rename.rs:66:9
    |
 LL | #![warn(clippy::block_in_if_condition_expr)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions`
 
 error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_conditions`
-  --> tests/ui/rename.rs:66:9
+  --> tests/ui/rename.rs:67:9
    |
 LL | #![warn(clippy::block_in_if_condition_stmt)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions`
 
 error: lint `clippy::blocks_in_if_conditions` has been renamed to `clippy::blocks_in_conditions`
-  --> tests/ui/rename.rs:67:9
+  --> tests/ui/rename.rs:68:9
    |
 LL | #![warn(clippy::blocks_in_if_conditions)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions`
 
 error: lint `clippy::box_vec` has been renamed to `clippy::box_collection`
-  --> tests/ui/rename.rs:68:9
+  --> tests/ui/rename.rs:69:9
    |
 LL | #![warn(clippy::box_vec)]
    |         ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection`
 
 error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes`
-  --> tests/ui/rename.rs:69:9
+  --> tests/ui/rename.rs:70:9
    |
 LL | #![warn(clippy::const_static_lifetime)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes`
 
 error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity`
-  --> tests/ui/rename.rs:70:9
+  --> tests/ui/rename.rs:71:9
    |
 LL | #![warn(clippy::cyclomatic_complexity)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity`
 
 error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq`
-  --> tests/ui/rename.rs:71:9
+  --> tests/ui/rename.rs:72:9
    |
 LL | #![warn(clippy::derive_hash_xor_eq)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq`
 
 error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods`
-  --> tests/ui/rename.rs:72:9
+  --> tests/ui/rename.rs:73:9
    |
 LL | #![warn(clippy::disallowed_method)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods`
 
 error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types`
-  --> tests/ui/rename.rs:73:9
+  --> tests/ui/rename.rs:74:9
    |
 LL | #![warn(clippy::disallowed_type)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types`
 
 error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression`
-  --> tests/ui/rename.rs:74:9
+  --> tests/ui/rename.rs:75:9
    |
 LL | #![warn(clippy::eval_order_dependence)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression`
 
 error: lint `clippy::find_map` has been renamed to `clippy::manual_find_map`
-  --> tests/ui/rename.rs:75:9
+  --> tests/ui/rename.rs:76:9
    |
 LL | #![warn(clippy::find_map)]
    |         ^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_find_map`
 
 error: lint `clippy::filter_map` has been renamed to `clippy::manual_filter_map`
-  --> tests/ui/rename.rs:76:9
+  --> tests/ui/rename.rs:77:9
    |
 LL | #![warn(clippy::filter_map)]
    |         ^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_filter_map`
 
+error: lint `clippy::fn_address_comparisons` has been renamed to `unpredictable_function_pointer_comparisons`
+  --> tests/ui/rename.rs:78:9
+   |
+LL | #![warn(clippy::fn_address_comparisons)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unpredictable_function_pointer_comparisons`
+
 error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion`
-  --> tests/ui/rename.rs:77:9
+  --> tests/ui/rename.rs:79:9
    |
 LL | #![warn(clippy::identity_conversion)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion`
 
 error: lint `clippy::if_let_redundant_pattern_matching` has been renamed to `clippy::redundant_pattern_matching`
-  --> tests/ui/rename.rs:78:9
+  --> tests/ui/rename.rs:80:9
    |
 LL | #![warn(clippy::if_let_redundant_pattern_matching)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_pattern_matching`
 
 error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok`
-  --> tests/ui/rename.rs:79:9
+  --> tests/ui/rename.rs:81:9
    |
 LL | #![warn(clippy::if_let_some_result)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok`
 
 error: lint `clippy::incorrect_clone_impl_on_copy_type` has been renamed to `clippy::non_canonical_clone_impl`
-  --> tests/ui/rename.rs:80:9
+  --> tests/ui/rename.rs:82:9
    |
 LL | #![warn(clippy::incorrect_clone_impl_on_copy_type)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_clone_impl`
 
 error: lint `clippy::incorrect_partial_ord_impl_on_ord_type` has been renamed to `clippy::non_canonical_partial_ord_impl`
-  --> tests/ui/rename.rs:81:9
+  --> tests/ui/rename.rs:83:9
    |
 LL | #![warn(clippy::incorrect_partial_ord_impl_on_ord_type)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_partial_ord_impl`
 
 error: lint `clippy::integer_arithmetic` has been renamed to `clippy::arithmetic_side_effects`
-  --> tests/ui/rename.rs:82:9
+  --> tests/ui/rename.rs:84:9
    |
 LL | #![warn(clippy::integer_arithmetic)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects`
 
 error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr`
-  --> tests/ui/rename.rs:83:9
+  --> tests/ui/rename.rs:85:9
    |
 LL | #![warn(clippy::logic_bug)]
    |         ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr`
 
 error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default`
-  --> tests/ui/rename.rs:84:9
+  --> tests/ui/rename.rs:86:9
    |
 LL | #![warn(clippy::new_without_default_derive)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default`
 
 error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map`
-  --> tests/ui/rename.rs:85:9
+  --> tests/ui/rename.rs:87:9
    |
 LL | #![warn(clippy::option_and_then_some)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map`
 
 error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used`
-  --> tests/ui/rename.rs:86:9
+  --> tests/ui/rename.rs:88:9
    |
 LL | #![warn(clippy::option_expect_used)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
 
 error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or`
-  --> tests/ui/rename.rs:87:9
+  --> tests/ui/rename.rs:89:9
    |
 LL | #![warn(clippy::option_map_unwrap_or)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
 
 error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
-  --> tests/ui/rename.rs:88:9
+  --> tests/ui/rename.rs:90:9
    |
 LL | #![warn(clippy::option_map_unwrap_or_else)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
 
 error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used`
-  --> tests/ui/rename.rs:89:9
+  --> tests/ui/rename.rs:91:9
    |
 LL | #![warn(clippy::option_unwrap_used)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
 
 error: lint `clippy::overflow_check_conditional` has been renamed to `clippy::panicking_overflow_checks`
-  --> tests/ui/rename.rs:90:9
+  --> tests/ui/rename.rs:92:9
    |
 LL | #![warn(clippy::overflow_check_conditional)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::panicking_overflow_checks`
 
 error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow`
-  --> tests/ui/rename.rs:91:9
+  --> tests/ui/rename.rs:93:9
    |
 LL | #![warn(clippy::ref_in_deref)]
    |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow`
 
 error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used`
-  --> tests/ui/rename.rs:92:9
+  --> tests/ui/rename.rs:94:9
    |
 LL | #![warn(clippy::result_expect_used)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
 
 error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
-  --> tests/ui/rename.rs:93:9
+  --> tests/ui/rename.rs:95:9
    |
 LL | #![warn(clippy::result_map_unwrap_or_else)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
 
 error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used`
-  --> tests/ui/rename.rs:94:9
+  --> tests/ui/rename.rs:96:9
    |
 LL | #![warn(clippy::result_unwrap_used)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
 
 error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str`
-  --> tests/ui/rename.rs:95:9
+  --> tests/ui/rename.rs:97:9
    |
 LL | #![warn(clippy::single_char_push_str)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str`
 
 error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions`
-  --> tests/ui/rename.rs:96:9
+  --> tests/ui/rename.rs:98:9
    |
 LL | #![warn(clippy::stutter)]
    |         ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions`
 
 error: lint `clippy::thread_local_initializer_can_be_made_const` has been renamed to `clippy::missing_const_for_thread_local`
-  --> tests/ui/rename.rs:97:9
+  --> tests/ui/rename.rs:99:9
    |
 LL | #![warn(clippy::thread_local_initializer_can_be_made_const)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::missing_const_for_thread_local`
 
 error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl`
-  --> tests/ui/rename.rs:98:9
+  --> tests/ui/rename.rs:100:9
    |
 LL | #![warn(clippy::to_string_in_display)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl`
 
 error: lint `clippy::unwrap_or_else_default` has been renamed to `clippy::unwrap_or_default`
-  --> tests/ui/rename.rs:99:9
+  --> tests/ui/rename.rs:101:9
    |
 LL | #![warn(clippy::unwrap_or_else_default)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_or_default`
 
 error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters`
-  --> tests/ui/rename.rs:100:9
+  --> tests/ui/rename.rs:102:9
    |
 LL | #![warn(clippy::zero_width_space)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters`
 
 error: lint `clippy::cast_ref_to_mut` has been renamed to `invalid_reference_casting`
-  --> tests/ui/rename.rs:101:9
+  --> tests/ui/rename.rs:103:9
    |
 LL | #![warn(clippy::cast_ref_to_mut)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_reference_casting`
 
 error: lint `clippy::clone_double_ref` has been renamed to `suspicious_double_ref_op`
-  --> tests/ui/rename.rs:102:9
+  --> tests/ui/rename.rs:104:9
    |
 LL | #![warn(clippy::clone_double_ref)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `suspicious_double_ref_op`
 
 error: lint `clippy::cmp_nan` has been renamed to `invalid_nan_comparisons`
-  --> tests/ui/rename.rs:103:9
+  --> tests/ui/rename.rs:105:9
    |
 LL | #![warn(clippy::cmp_nan)]
    |         ^^^^^^^^^^^^^^^ help: use the new name: `invalid_nan_comparisons`
 
 error: lint `clippy::drop_bounds` has been renamed to `drop_bounds`
-  --> tests/ui/rename.rs:104:9
+  --> tests/ui/rename.rs:106:9
    |
 LL | #![warn(clippy::drop_bounds)]
    |         ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds`
 
 error: lint `clippy::drop_copy` has been renamed to `dropping_copy_types`
-  --> tests/ui/rename.rs:105:9
+  --> tests/ui/rename.rs:107:9
    |
 LL | #![warn(clippy::drop_copy)]
    |         ^^^^^^^^^^^^^^^^^ help: use the new name: `dropping_copy_types`
 
 error: lint `clippy::drop_ref` has been renamed to `dropping_references`
-  --> tests/ui/rename.rs:106:9
+  --> tests/ui/rename.rs:108:9
    |
 LL | #![warn(clippy::drop_ref)]
    |         ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references`
 
 error: lint `clippy::fn_null_check` has been renamed to `useless_ptr_null_checks`
-  --> tests/ui/rename.rs:107:9
+  --> tests/ui/rename.rs:109:9
    |
 LL | #![warn(clippy::fn_null_check)]
    |         ^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `useless_ptr_null_checks`
 
 error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles`
-  --> tests/ui/rename.rs:108:9
+  --> tests/ui/rename.rs:110:9
    |
 LL | #![warn(clippy::for_loop_over_option)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles`
 
 error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles`
-  --> tests/ui/rename.rs:109:9
+  --> tests/ui/rename.rs:111:9
    |
 LL | #![warn(clippy::for_loop_over_result)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles`
 
 error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles`
-  --> tests/ui/rename.rs:110:9
+  --> tests/ui/rename.rs:112:9
    |
 LL | #![warn(clippy::for_loops_over_fallibles)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles`
 
 error: lint `clippy::forget_copy` has been renamed to `forgetting_copy_types`
-  --> tests/ui/rename.rs:111:9
+  --> tests/ui/rename.rs:113:9
    |
 LL | #![warn(clippy::forget_copy)]
    |         ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_copy_types`
 
 error: lint `clippy::forget_ref` has been renamed to `forgetting_references`
-  --> tests/ui/rename.rs:112:9
+  --> tests/ui/rename.rs:114:9
    |
 LL | #![warn(clippy::forget_ref)]
    |         ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references`
 
 error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter`
-  --> tests/ui/rename.rs:113:9
+  --> tests/ui/rename.rs:115:9
    |
 LL | #![warn(clippy::into_iter_on_array)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter`
 
 error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering`
-  --> tests/ui/rename.rs:114:9
+  --> tests/ui/rename.rs:116:9
    |
 LL | #![warn(clippy::invalid_atomic_ordering)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering`
 
 error: lint `clippy::invalid_ref` has been renamed to `invalid_value`
-  --> tests/ui/rename.rs:115:9
+  --> tests/ui/rename.rs:117:9
    |
 LL | #![warn(clippy::invalid_ref)]
    |         ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value`
 
 error: lint `clippy::invalid_utf8_in_unchecked` has been renamed to `invalid_from_utf8_unchecked`
-  --> tests/ui/rename.rs:116:9
+  --> tests/ui/rename.rs:118:9
    |
 LL | #![warn(clippy::invalid_utf8_in_unchecked)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_from_utf8_unchecked`
 
 error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop`
-  --> tests/ui/rename.rs:117:9
+  --> tests/ui/rename.rs:119:9
    |
 LL | #![warn(clippy::let_underscore_drop)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop`
 
 error: lint `clippy::maybe_misused_cfg` has been renamed to `unexpected_cfgs`
-  --> tests/ui/rename.rs:118:9
+  --> tests/ui/rename.rs:120:9
    |
 LL | #![warn(clippy::maybe_misused_cfg)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs`
 
 error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums`
-  --> tests/ui/rename.rs:119:9
+  --> tests/ui/rename.rs:121:9
    |
 LL | #![warn(clippy::mem_discriminant_non_enum)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums`
 
 error: lint `clippy::mismatched_target_os` has been renamed to `unexpected_cfgs`
-  --> tests/ui/rename.rs:120:9
+  --> tests/ui/rename.rs:122:9
    |
 LL | #![warn(clippy::mismatched_target_os)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs`
 
 error: lint `clippy::panic_params` has been renamed to `non_fmt_panics`
-  --> tests/ui/rename.rs:121:9
+  --> tests/ui/rename.rs:123:9
    |
 LL | #![warn(clippy::panic_params)]
    |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics`
 
 error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally`
-  --> tests/ui/rename.rs:122:9
+  --> tests/ui/rename.rs:124:9
    |
 LL | #![warn(clippy::positional_named_format_parameters)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally`
 
 error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `dangling_pointers_from_temporaries`
-  --> tests/ui/rename.rs:123:9
+  --> tests/ui/rename.rs:125:9
    |
 LL | #![warn(clippy::temporary_cstring_as_ptr)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `dangling_pointers_from_temporaries`
 
 error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops`
-  --> tests/ui/rename.rs:124:9
+  --> tests/ui/rename.rs:126:9
    |
 LL | #![warn(clippy::undropped_manually_drops)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `undropped_manually_drops`
 
 error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints`
-  --> tests/ui/rename.rs:125:9
+  --> tests/ui/rename.rs:127:9
    |
 LL | #![warn(clippy::unknown_clippy_lints)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints`
 
 error: lint `clippy::unused_label` has been renamed to `unused_labels`
-  --> tests/ui/rename.rs:126:9
+  --> tests/ui/rename.rs:128:9
    |
 LL | #![warn(clippy::unused_label)]
    |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels`
 
 error: lint `clippy::vtable_address_comparisons` has been renamed to `ambiguous_wide_pointer_comparisons`
-  --> tests/ui/rename.rs:127:9
+  --> tests/ui/rename.rs:129:9
    |
 LL | #![warn(clippy::vtable_address_comparisons)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `ambiguous_wide_pointer_comparisons`
 
 error: lint `clippy::reverse_range_loop` has been renamed to `clippy::reversed_empty_ranges`
-  --> tests/ui/rename.rs:128:9
+  --> tests/ui/rename.rs:130:9
    |
 LL | #![warn(clippy::reverse_range_loop)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::reversed_empty_ranges`
 
-error: aborting due to 66 previous errors
+error: aborting due to 67 previous errors
 
diff --git a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.fixed b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.fixed
index 779431303ae..708512793d5 100644
--- a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.fixed
+++ b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.fixed
@@ -1,6 +1,6 @@
 #![deny(clippy::trait_duplication_in_bounds)]
 #![allow(unused)]
-#![feature(const_trait_impl)]
+#![feature(associated_const_equality, const_trait_impl)]
 
 use std::any::Any;
 
@@ -179,3 +179,19 @@ fn main() {
     let _x: fn(_) = f::<()>;
     let _x: fn(_) = f::<i32>;
 }
+
+// #13706
+fn assoc_tys_bounds<T>()
+where
+    T: Iterator<Item: Clone> + Iterator<Item: Clone>,
+{
+}
+trait AssocConstTrait {
+    const ASSOC: usize;
+}
+fn assoc_const_args<T>()
+where
+    T: AssocConstTrait<ASSOC = 0>,
+    //~^ trait_duplication_in_bounds
+{
+}
diff --git a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs
index 3e974dc0a8f..12db6b65a7a 100644
--- a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs
+++ b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs
@@ -1,6 +1,6 @@
 #![deny(clippy::trait_duplication_in_bounds)]
 #![allow(unused)]
-#![feature(const_trait_impl)]
+#![feature(associated_const_equality, const_trait_impl)]
 
 use std::any::Any;
 
@@ -179,3 +179,19 @@ fn main() {
     let _x: fn(_) = f::<()>;
     let _x: fn(_) = f::<i32>;
 }
+
+// #13706
+fn assoc_tys_bounds<T>()
+where
+    T: Iterator<Item: Clone> + Iterator<Item: Clone>,
+{
+}
+trait AssocConstTrait {
+    const ASSOC: usize;
+}
+fn assoc_const_args<T>()
+where
+    T: AssocConstTrait<ASSOC = 0> + AssocConstTrait<ASSOC = 0>,
+    //~^ trait_duplication_in_bounds
+{
+}
diff --git a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr
index 0dd508e4745..83c06eaccd4 100644
--- a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr
+++ b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr
@@ -70,5 +70,11 @@ error: these where clauses contain repeated elements
 LL |     T: IntoIterator<Item = U::Owned> + IntoIterator<Item = U::Owned>,
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `IntoIterator<Item = U::Owned>`
 
-error: aborting due to 11 previous errors
+error: these where clauses contain repeated elements
+  --> tests/ui/trait_duplication_in_bounds.rs:194:8
+   |
+LL |     T: AssocConstTrait<ASSOC = 0> + AssocConstTrait<ASSOC = 0>,
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `AssocConstTrait<ASSOC = 0>`
+
+error: aborting due to 12 previous errors
 
diff --git a/src/tools/clippy/tests/ui/uninlined_format_args.fixed b/src/tools/clippy/tests/ui/uninlined_format_args.fixed
index 3f5b0e52ece..111a2e1987c 100644
--- a/src/tools/clippy/tests/ui/uninlined_format_args.fixed
+++ b/src/tools/clippy/tests/ui/uninlined_format_args.fixed
@@ -257,8 +257,6 @@ fn tester2() {
     my_concat!("{}", local_i32);
     my_good_macro!("{}", local_i32);
     my_good_macro!("{}", local_i32,);
-
-    // FIXME: Broken false positives, currently unhandled
     my_bad_macro!("{}", local_i32);
     my_bad_macro2!("{}", local_i32);
     used_twice! {
@@ -267,3 +265,22 @@ fn tester2() {
         local_i32,
     };
 }
+
+#[clippy::format_args]
+macro_rules! usr_println {
+    ($target:expr, $($args:tt)*) => {{
+        if $target {
+            println!($($args)*)
+        }
+    }};
+}
+
+fn user_format() {
+    let local_i32 = 1;
+    let local_f64 = 2.0;
+
+    usr_println!(true, "val='{local_i32}'");
+    usr_println!(true, "{local_i32}");
+    usr_println!(true, "{local_i32:#010x}");
+    usr_println!(true, "{local_f64:.1}");
+}
diff --git a/src/tools/clippy/tests/ui/uninlined_format_args.rs b/src/tools/clippy/tests/ui/uninlined_format_args.rs
index b311aa4912c..81fe2476567 100644
--- a/src/tools/clippy/tests/ui/uninlined_format_args.rs
+++ b/src/tools/clippy/tests/ui/uninlined_format_args.rs
@@ -262,8 +262,6 @@ fn tester2() {
     my_concat!("{}", local_i32);
     my_good_macro!("{}", local_i32);
     my_good_macro!("{}", local_i32,);
-
-    // FIXME: Broken false positives, currently unhandled
     my_bad_macro!("{}", local_i32);
     my_bad_macro2!("{}", local_i32);
     used_twice! {
@@ -272,3 +270,22 @@ fn tester2() {
         local_i32,
     };
 }
+
+#[clippy::format_args]
+macro_rules! usr_println {
+    ($target:expr, $($args:tt)*) => {{
+        if $target {
+            println!($($args)*)
+        }
+    }};
+}
+
+fn user_format() {
+    let local_i32 = 1;
+    let local_f64 = 2.0;
+
+    usr_println!(true, "val='{}'", local_i32);
+    usr_println!(true, "{}", local_i32);
+    usr_println!(true, "{:#010x}", local_i32);
+    usr_println!(true, "{:.1}", local_f64);
+}
diff --git a/src/tools/clippy/tests/ui/uninlined_format_args.stderr b/src/tools/clippy/tests/ui/uninlined_format_args.stderr
index 5a7ff3bc4f5..77961fea2c5 100644
--- a/src/tools/clippy/tests/ui/uninlined_format_args.stderr
+++ b/src/tools/clippy/tests/ui/uninlined_format_args.stderr
@@ -845,5 +845,53 @@ LL -     println!("expand='{}'", local_i32);
 LL +     println!("expand='{local_i32}'");
    |
 
-error: aborting due to 71 previous errors
+error: variables can be used directly in the `format!` string
+  --> tests/ui/uninlined_format_args.rs:287:5
+   |
+LL |     usr_println!(true, "val='{}'", local_i32);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: change this to
+   |
+LL -     usr_println!(true, "val='{}'", local_i32);
+LL +     usr_println!(true, "val='{local_i32}'");
+   |
+
+error: variables can be used directly in the `format!` string
+  --> tests/ui/uninlined_format_args.rs:288:5
+   |
+LL |     usr_println!(true, "{}", local_i32);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: change this to
+   |
+LL -     usr_println!(true, "{}", local_i32);
+LL +     usr_println!(true, "{local_i32}");
+   |
+
+error: variables can be used directly in the `format!` string
+  --> tests/ui/uninlined_format_args.rs:289:5
+   |
+LL |     usr_println!(true, "{:#010x}", local_i32);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: change this to
+   |
+LL -     usr_println!(true, "{:#010x}", local_i32);
+LL +     usr_println!(true, "{local_i32:#010x}");
+   |
+
+error: variables can be used directly in the `format!` string
+  --> tests/ui/uninlined_format_args.rs:290:5
+   |
+LL |     usr_println!(true, "{:.1}", local_f64);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: change this to
+   |
+LL -     usr_println!(true, "{:.1}", local_f64);
+LL +     usr_println!(true, "{local_f64:.1}");
+   |
+
+error: aborting due to 75 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unnecessary_map_or.fixed b/src/tools/clippy/tests/ui/unnecessary_map_or.fixed
index 2d932a70e9d..70b78ceca50 100644
--- a/src/tools/clippy/tests/ui/unnecessary_map_or.fixed
+++ b/src/tools/clippy/tests/ui/unnecessary_map_or.fixed
@@ -23,10 +23,9 @@ fn main() {
     let _ = Ok::<Vec<i32>, i32>(vec![5]).is_ok_and(|n| n == [5]);
     let _ = (Ok::<i32, i32>(5) == Ok(5));
     let _ = (Some(5) == Some(5)).then(|| 1);
+    let _ = Some(5).is_none_or(|n| n == 5);
+    let _ = Some(5).is_none_or(|n| 5 == n);
 
-    // shouldnt trigger
-    let _ = Some(5).map_or(true, |n| n == 5);
-    let _ = Some(5).map_or(true, |n| 5 == n);
     macro_rules! x {
         () => {
             Some(1)
@@ -51,10 +50,21 @@ fn main() {
     let r: Result<i32, S> = Ok(3);
     let _ = r.is_ok_and(|x| x == 7);
 
+    // lint constructs that are not comparaisons as well
+    let func = |_x| true;
+    let r: Result<i32, S> = Ok(3);
+    let _ = r.is_ok_and(func);
+    let _ = Some(5).is_some_and(func);
+    let _ = Some(5).is_none_or(func);
+
     #[derive(PartialEq)]
     struct S2;
     let r: Result<i32, S2> = Ok(4);
     let _ = (r == Ok(8));
+
+    // do not lint `Result::map_or(true, …)`
+    let r: Result<i32, S2> = Ok(4);
+    let _ = r.map_or(true, |x| x == 8);
 }
 
 #[clippy::msrv = "1.69.0"]
@@ -62,3 +72,9 @@ fn msrv_1_69() {
     // is_some_and added in 1.70.0
     let _ = Some(5).map_or(false, |n| n == if 2 > 1 { n } else { 0 });
 }
+
+#[clippy::msrv = "1.81.0"]
+fn msrv_1_81() {
+    // is_none_or added in 1.82.0
+    let _ = Some(5).map_or(true, |n| n == if 2 > 1 { n } else { 0 });
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_map_or.rs b/src/tools/clippy/tests/ui/unnecessary_map_or.rs
index 4a9d69be1e9..50757715977 100644
--- a/src/tools/clippy/tests/ui/unnecessary_map_or.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_map_or.rs
@@ -26,10 +26,9 @@ fn main() {
     let _ = Ok::<Vec<i32>, i32>(vec![5]).map_or(false, |n| n == [5]);
     let _ = Ok::<i32, i32>(5).map_or(false, |n| n == 5);
     let _ = Some(5).map_or(false, |n| n == 5).then(|| 1);
-
-    // shouldnt trigger
     let _ = Some(5).map_or(true, |n| n == 5);
     let _ = Some(5).map_or(true, |n| 5 == n);
+
     macro_rules! x {
         () => {
             Some(1)
@@ -54,10 +53,21 @@ fn main() {
     let r: Result<i32, S> = Ok(3);
     let _ = r.map_or(false, |x| x == 7);
 
+    // lint constructs that are not comparaisons as well
+    let func = |_x| true;
+    let r: Result<i32, S> = Ok(3);
+    let _ = r.map_or(false, func);
+    let _ = Some(5).map_or(false, func);
+    let _ = Some(5).map_or(true, func);
+
     #[derive(PartialEq)]
     struct S2;
     let r: Result<i32, S2> = Ok(4);
     let _ = r.map_or(false, |x| x == 8);
+
+    // do not lint `Result::map_or(true, …)`
+    let r: Result<i32, S2> = Ok(4);
+    let _ = r.map_or(true, |x| x == 8);
 }
 
 #[clippy::msrv = "1.69.0"]
@@ -65,3 +75,9 @@ fn msrv_1_69() {
     // is_some_and added in 1.70.0
     let _ = Some(5).map_or(false, |n| n == if 2 > 1 { n } else { 0 });
 }
+
+#[clippy::msrv = "1.81.0"]
+fn msrv_1_81() {
+    // is_none_or added in 1.82.0
+    let _ = Some(5).map_or(true, |n| n == if 2 > 1 { n } else { 0 });
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_map_or.stderr b/src/tools/clippy/tests/ui/unnecessary_map_or.stderr
index 299a4e5da7a..890abb01228 100644
--- a/src/tools/clippy/tests/ui/unnecessary_map_or.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_map_or.stderr
@@ -1,4 +1,4 @@
-error: this `map_or` is redundant
+error: this `map_or` can be simplified
   --> tests/ui/unnecessary_map_or.rs:12:13
    |
 LL |     let _ = Some(5).map_or(false, |n| n == 5);
@@ -7,13 +7,13 @@ LL |     let _ = Some(5).map_or(false, |n| n == 5);
    = note: `-D clippy::unnecessary-map-or` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::unnecessary_map_or)]`
 
-error: this `map_or` is redundant
+error: this `map_or` can be simplified
   --> tests/ui/unnecessary_map_or.rs:13:13
    |
 LL |     let _ = Some(5).map_or(true, |n| n != 5);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) != Some(5))`
 
-error: this `map_or` is redundant
+error: this `map_or` can be simplified
   --> tests/ui/unnecessary_map_or.rs:14:13
    |
 LL |       let _ = Some(5).map_or(false, |n| {
@@ -23,7 +23,7 @@ LL | |         n == 5
 LL | |     });
    | |______^ help: use a standard comparison instead: `(Some(5) == Some(5))`
 
-error: this `map_or` is redundant
+error: this `map_or` can be simplified
   --> tests/ui/unnecessary_map_or.rs:18:13
    |
 LL |       let _ = Some(5).map_or(false, |n| {
@@ -41,59 +41,89 @@ LL +         6 >= 5
 LL ~     });
    |
 
-error: this `map_or` is redundant
+error: this `map_or` can be simplified
   --> tests/ui/unnecessary_map_or.rs:22:13
    |
 LL |     let _ = Some(vec![5]).map_or(false, |n| n == [5]);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(vec![5]).is_some_and(|n| n == [5])`
 
-error: this `map_or` is redundant
+error: this `map_or` can be simplified
   --> tests/ui/unnecessary_map_or.rs:23:13
    |
 LL |     let _ = Some(vec![1]).map_or(false, |n| vec![2] == n);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(vec![1]).is_some_and(|n| vec![2] == n)`
 
-error: this `map_or` is redundant
+error: this `map_or` can be simplified
   --> tests/ui/unnecessary_map_or.rs:24:13
    |
 LL |     let _ = Some(5).map_or(false, |n| n == n);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(5).is_some_and(|n| n == n)`
 
-error: this `map_or` is redundant
+error: this `map_or` can be simplified
   --> tests/ui/unnecessary_map_or.rs:25:13
    |
 LL |     let _ = Some(5).map_or(false, |n| n == if 2 > 1 { n } else { 0 });
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(5).is_some_and(|n| n == if 2 > 1 { n } else { 0 })`
 
-error: this `map_or` is redundant
+error: this `map_or` can be simplified
   --> tests/ui/unnecessary_map_or.rs:26:13
    |
 LL |     let _ = Ok::<Vec<i32>, i32>(vec![5]).map_or(false, |n| n == [5]);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_ok_and instead: `Ok::<Vec<i32>, i32>(vec![5]).is_ok_and(|n| n == [5])`
 
-error: this `map_or` is redundant
+error: this `map_or` can be simplified
   --> tests/ui/unnecessary_map_or.rs:27:13
    |
 LL |     let _ = Ok::<i32, i32>(5).map_or(false, |n| n == 5);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Ok::<i32, i32>(5) == Ok(5))`
 
-error: this `map_or` is redundant
+error: this `map_or` can be simplified
   --> tests/ui/unnecessary_map_or.rs:28:13
    |
 LL |     let _ = Some(5).map_or(false, |n| n == 5).then(|| 1);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) == Some(5))`
 
-error: this `map_or` is redundant
-  --> tests/ui/unnecessary_map_or.rs:55:13
+error: this `map_or` can be simplified
+  --> tests/ui/unnecessary_map_or.rs:29:13
+   |
+LL |     let _ = Some(5).map_or(true, |n| n == 5);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_none_or instead: `Some(5).is_none_or(|n| n == 5)`
+
+error: this `map_or` can be simplified
+  --> tests/ui/unnecessary_map_or.rs:30:13
+   |
+LL |     let _ = Some(5).map_or(true, |n| 5 == n);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_none_or instead: `Some(5).is_none_or(|n| 5 == n)`
+
+error: this `map_or` can be simplified
+  --> tests/ui/unnecessary_map_or.rs:54:13
    |
 LL |     let _ = r.map_or(false, |x| x == 7);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_ok_and instead: `r.is_ok_and(|x| x == 7)`
 
-error: this `map_or` is redundant
+error: this `map_or` can be simplified
+  --> tests/ui/unnecessary_map_or.rs:59:13
+   |
+LL |     let _ = r.map_or(false, func);
+   |             ^^^^^^^^^^^^^^^^^^^^^ help: use is_ok_and instead: `r.is_ok_and(func)`
+
+error: this `map_or` can be simplified
   --> tests/ui/unnecessary_map_or.rs:60:13
    |
+LL |     let _ = Some(5).map_or(false, func);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(5).is_some_and(func)`
+
+error: this `map_or` can be simplified
+  --> tests/ui/unnecessary_map_or.rs:61:13
+   |
+LL |     let _ = Some(5).map_or(true, func);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_none_or instead: `Some(5).is_none_or(func)`
+
+error: this `map_or` can be simplified
+  --> tests/ui/unnecessary_map_or.rs:66:13
+   |
 LL |     let _ = r.map_or(false, |x| x == 8);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(r == Ok(8))`
 
-error: aborting due to 13 previous errors
+error: aborting due to 18 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unused_format_specs.1.fixed b/src/tools/clippy/tests/ui/unused_format_specs.1.fixed
index b7d1cce2870..157c2b08d3c 100644
--- a/src/tools/clippy/tests/ui/unused_format_specs.1.fixed
+++ b/src/tools/clippy/tests/ui/unused_format_specs.1.fixed
@@ -33,3 +33,38 @@ fn should_not_lint() {
     let args = format_args!("");
     println!("{args}");
 }
+
+#[clippy::format_args]
+macro_rules! usr_println {
+    ($target:expr, $($args:tt)*) => {{
+        if $target {
+            println!($($args)*)
+        }
+    }};
+}
+
+fn should_lint_user() {
+    // prints `.`, not `     .`
+    usr_println!(true, "{:5}.", format!(""));
+    //~^ ERROR: format specifiers have no effect on `format_args!()`
+    //prints `abcde`, not `abc`
+    usr_println!(true, "{:.3}", format!("abcde"));
+    //~^ ERROR: format specifiers have no effect on `format_args!()`
+
+    usr_println!(true, "{}.", format_args_from_macro!());
+    //~^ ERROR: format specifiers have no effect on `format_args!()`
+
+    let args = format_args!("");
+    usr_println!(true, "{args}");
+    //~^ ERROR: format specifiers have no effect on `format_args!()`
+}
+
+fn should_not_lint_user() {
+    usr_println!(true, "{}", format_args!(""));
+    // Technically the same as `{}`, but the `format_args` docs specifically mention that you can use
+    // debug formatting so allow it
+    usr_println!(true, "{:?}", format_args!(""));
+
+    let args = format_args!("");
+    usr_println!(true, "{args}");
+}
diff --git a/src/tools/clippy/tests/ui/unused_format_specs.2.fixed b/src/tools/clippy/tests/ui/unused_format_specs.2.fixed
index 94bb6b7036b..92c7b951f3c 100644
--- a/src/tools/clippy/tests/ui/unused_format_specs.2.fixed
+++ b/src/tools/clippy/tests/ui/unused_format_specs.2.fixed
@@ -33,3 +33,38 @@ fn should_not_lint() {
     let args = format_args!("");
     println!("{args}");
 }
+
+#[clippy::format_args]
+macro_rules! usr_println {
+    ($target:expr, $($args:tt)*) => {{
+        if $target {
+            println!($($args)*)
+        }
+    }};
+}
+
+fn should_lint_user() {
+    // prints `.`, not `     .`
+    usr_println!(true, "{}.", format_args!(""));
+    //~^ ERROR: format specifiers have no effect on `format_args!()`
+    //prints `abcde`, not `abc`
+    usr_println!(true, "{}", format_args!("abcde"));
+    //~^ ERROR: format specifiers have no effect on `format_args!()`
+
+    usr_println!(true, "{}.", format_args_from_macro!());
+    //~^ ERROR: format specifiers have no effect on `format_args!()`
+
+    let args = format_args!("");
+    usr_println!(true, "{args}");
+    //~^ ERROR: format specifiers have no effect on `format_args!()`
+}
+
+fn should_not_lint_user() {
+    usr_println!(true, "{}", format_args!(""));
+    // Technically the same as `{}`, but the `format_args` docs specifically mention that you can use
+    // debug formatting so allow it
+    usr_println!(true, "{:?}", format_args!(""));
+
+    let args = format_args!("");
+    usr_println!(true, "{args}");
+}
diff --git a/src/tools/clippy/tests/ui/unused_format_specs.rs b/src/tools/clippy/tests/ui/unused_format_specs.rs
index 2c85e371149..a5df4d8a866 100644
--- a/src/tools/clippy/tests/ui/unused_format_specs.rs
+++ b/src/tools/clippy/tests/ui/unused_format_specs.rs
@@ -33,3 +33,38 @@ fn should_not_lint() {
     let args = format_args!("");
     println!("{args}");
 }
+
+#[clippy::format_args]
+macro_rules! usr_println {
+    ($target:expr, $($args:tt)*) => {{
+        if $target {
+            println!($($args)*)
+        }
+    }};
+}
+
+fn should_lint_user() {
+    // prints `.`, not `     .`
+    usr_println!(true, "{:5}.", format_args!(""));
+    //~^ ERROR: format specifiers have no effect on `format_args!()`
+    //prints `abcde`, not `abc`
+    usr_println!(true, "{:.3}", format_args!("abcde"));
+    //~^ ERROR: format specifiers have no effect on `format_args!()`
+
+    usr_println!(true, "{:5}.", format_args_from_macro!());
+    //~^ ERROR: format specifiers have no effect on `format_args!()`
+
+    let args = format_args!("");
+    usr_println!(true, "{args:5}");
+    //~^ ERROR: format specifiers have no effect on `format_args!()`
+}
+
+fn should_not_lint_user() {
+    usr_println!(true, "{}", format_args!(""));
+    // Technically the same as `{}`, but the `format_args` docs specifically mention that you can use
+    // debug formatting so allow it
+    usr_println!(true, "{:?}", format_args!(""));
+
+    let args = format_args!("");
+    usr_println!(true, "{args}");
+}
diff --git a/src/tools/clippy/tests/ui/unused_format_specs.stderr b/src/tools/clippy/tests/ui/unused_format_specs.stderr
index 2b5c81c63d6..df61d59130e 100644
--- a/src/tools/clippy/tests/ui/unused_format_specs.stderr
+++ b/src/tools/clippy/tests/ui/unused_format_specs.stderr
@@ -58,5 +58,63 @@ LL -     println!("{args:5}");
 LL +     println!("{args}");
    |
 
-error: aborting due to 4 previous errors
+error: format specifiers have no effect on `format_args!()`
+  --> tests/ui/unused_format_specs.rs:48:25
+   |
+LL |     usr_println!(true, "{:5}.", format_args!(""));
+   |                         ^^^^
+   |
+help: for the width to apply consider using `format!()`
+   |
+LL |     usr_println!(true, "{:5}.", format!(""));
+   |                                 ~~~~~~
+help: if the current behavior is intentional, remove the format specifiers
+   |
+LL -     usr_println!(true, "{:5}.", format_args!(""));
+LL +     usr_println!(true, "{}.", format_args!(""));
+   |
+
+error: format specifiers have no effect on `format_args!()`
+  --> tests/ui/unused_format_specs.rs:51:25
+   |
+LL |     usr_println!(true, "{:.3}", format_args!("abcde"));
+   |                         ^^^^^
+   |
+help: for the precision to apply consider using `format!()`
+   |
+LL |     usr_println!(true, "{:.3}", format!("abcde"));
+   |                                 ~~~~~~
+help: if the current behavior is intentional, remove the format specifiers
+   |
+LL -     usr_println!(true, "{:.3}", format_args!("abcde"));
+LL +     usr_println!(true, "{}", format_args!("abcde"));
+   |
+
+error: format specifiers have no effect on `format_args!()`
+  --> tests/ui/unused_format_specs.rs:54:25
+   |
+LL |     usr_println!(true, "{:5}.", format_args_from_macro!());
+   |                         ^^^^
+   |
+   = help: for the width to apply consider using `format!()`
+help: if the current behavior is intentional, remove the format specifiers
+   |
+LL -     usr_println!(true, "{:5}.", format_args_from_macro!());
+LL +     usr_println!(true, "{}.", format_args_from_macro!());
+   |
+
+error: format specifiers have no effect on `format_args!()`
+  --> tests/ui/unused_format_specs.rs:58:25
+   |
+LL |     usr_println!(true, "{args:5}");
+   |                         ^^^^^^^^
+   |
+   = help: for the width to apply consider using `format!()`
+help: if the current behavior is intentional, remove the format specifiers
+   |
+LL -     usr_println!(true, "{args:5}");
+LL +     usr_println!(true, "{args}");
+   |
+
+error: aborting due to 8 previous errors
 
diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs
index e6fbba943a4..36f876bcdc6 100644
--- a/src/tools/compiletest/src/common.rs
+++ b/src/tools/compiletest/src/common.rs
@@ -1,4 +1,4 @@
-use std::collections::{HashMap, HashSet};
+use std::collections::{BTreeSet, HashMap, HashSet};
 use std::ffi::OsString;
 use std::path::{Path, PathBuf};
 use std::process::Command;
@@ -486,6 +486,9 @@ impl Config {
     }
 }
 
+/// Known widths of `target_has_atomic`.
+pub const KNOWN_TARGET_HAS_ATOMIC_WIDTHS: &[&str] = &["8", "16", "32", "64", "128", "ptr"];
+
 #[derive(Debug, Clone)]
 pub struct TargetCfgs {
     pub current: TargetCfg,
@@ -611,6 +614,17 @@ impl TargetCfgs {
                 ("panic", Some("abort")) => cfg.panic = PanicStrategy::Abort,
                 ("panic", Some("unwind")) => cfg.panic = PanicStrategy::Unwind,
                 ("panic", other) => panic!("unexpected value for panic cfg: {other:?}"),
+
+                ("target_has_atomic", Some(width))
+                    if KNOWN_TARGET_HAS_ATOMIC_WIDTHS.contains(&width) =>
+                {
+                    cfg.target_has_atomic.insert(width.to_string());
+                }
+                ("target_has_atomic", Some(other)) => {
+                    panic!("unexpected value for `target_has_atomic` cfg: {other:?}")
+                }
+                // Nightly-only std-internal impl detail.
+                ("target_has_atomic", None) => {}
                 _ => {}
             }
         }
@@ -645,6 +659,12 @@ pub struct TargetCfg {
     pub(crate) xray: bool,
     #[serde(default = "default_reloc_model")]
     pub(crate) relocation_model: String,
+
+    // Not present in target cfg json output, additional derived information.
+    #[serde(skip)]
+    /// Supported target atomic widths: e.g. `8` to `128` or `ptr`. This is derived from the builtin
+    /// `target_has_atomic` `cfg`s e.g. `target_has_atomic="8"`.
+    pub(crate) target_has_atomic: BTreeSet<String>,
 }
 
 impl TargetCfg {
diff --git a/src/tools/compiletest/src/compute_diff.rs b/src/tools/compiletest/src/compute_diff.rs
index 92c80c27de0..3ace6c5b6d7 100644
--- a/src/tools/compiletest/src/compute_diff.rs
+++ b/src/tools/compiletest/src/compute_diff.rs
@@ -144,7 +144,7 @@ where
     }
 
     if !wrote_data {
-        println!("note: diff is identical to nightly rustdoc");
+        eprintln!("note: diff is identical to nightly rustdoc");
         assert!(diff_output.metadata().unwrap().len() == 0);
         return false;
     } else if verbose {
diff --git a/src/tools/compiletest/src/debuggers.rs b/src/tools/compiletest/src/debuggers.rs
index b605bc813f1..e75c8a5993e 100644
--- a/src/tools/compiletest/src/debuggers.rs
+++ b/src/tools/compiletest/src/debuggers.rs
@@ -20,7 +20,7 @@ pub(crate) fn configure_gdb(config: &Config) -> Option<Arc<Config>> {
     }
 
     if config.remote_test_client.is_some() && !config.target.contains("android") {
-        println!(
+        eprintln!(
             "WARNING: debuginfo tests are not available when \
              testing with remote"
         );
@@ -28,7 +28,7 @@ pub(crate) fn configure_gdb(config: &Config) -> Option<Arc<Config>> {
     }
 
     if config.target.contains("android") {
-        println!(
+        eprintln!(
             "{} debug-info test uses tcp 5039 port.\
              please reserve it",
             config.target
@@ -50,7 +50,7 @@ pub(crate) fn configure_lldb(config: &Config) -> Option<Arc<Config>> {
     config.lldb_python_dir.as_ref()?;
 
     if let Some(350) = config.lldb_version {
-        println!(
+        eprintln!(
             "WARNING: The used version of LLDB (350) has a \
              known issue that breaks debuginfo tests. See \
              issue #32520 for more information. Skipping all \
diff --git a/src/tools/compiletest/src/directive-list.rs b/src/tools/compiletest/src/directive-list.rs
index 0c47ef871d2..5638d471890 100644
--- a/src/tools/compiletest/src/directive-list.rs
+++ b/src/tools/compiletest/src/directive-list.rs
@@ -17,7 +17,6 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "check-run-results",
     "check-stdout",
     "check-test-line-numbers-match",
-    "compare-output-lines-by-subset",
     "compile-flags",
     "doc-flags",
     "dont-check-compiler-stderr",
@@ -154,6 +153,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "needs-sanitizer-thread",
     "needs-std-debug-assertions",
     "needs-symlink",
+    "needs-target-has-atomic",
     "needs-threads",
     "needs-unwind",
     "needs-wasmtime",
@@ -215,6 +215,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "pp-exact",
     "pretty-compare-only",
     "pretty-mode",
+    "proc-macro",
     "reference",
     "regex-error-pattern",
     "remap-src-base",
diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs
index e945797647e..91558d0c898 100644
--- a/src/tools/compiletest/src/header.rs
+++ b/src/tools/compiletest/src/header.rs
@@ -115,9 +115,6 @@ pub struct TestProps {
     pub dont_check_compiler_stdout: bool,
     // For UI tests, allows compiler to generate arbitrary output to stderr
     pub dont_check_compiler_stderr: bool,
-    // When checking the output of stdout or stderr check
-    // that the lines of expected output are a subset of the actual output.
-    pub compare_output_lines_by_subset: bool,
     // Don't force a --crate-type=dylib flag on the command line
     //
     // Set this for example if you have an auxiliary test file that contains
@@ -221,6 +218,7 @@ mod directives {
     pub const AUX_BIN: &'static str = "aux-bin";
     pub const AUX_BUILD: &'static str = "aux-build";
     pub const AUX_CRATE: &'static str = "aux-crate";
+    pub const PROC_MACRO: &'static str = "proc-macro";
     pub const AUX_CODEGEN_BACKEND: &'static str = "aux-codegen-backend";
     pub const EXEC_ENV: &'static str = "exec-env";
     pub const RUSTC_ENV: &'static str = "rustc-env";
@@ -239,7 +237,6 @@ mod directives {
     pub const KNOWN_BUG: &'static str = "known-bug";
     pub const TEST_MIR_PASS: &'static str = "test-mir-pass";
     pub const REMAP_SRC_BASE: &'static str = "remap-src-base";
-    pub const COMPARE_OUTPUT_LINES_BY_SUBSET: &'static str = "compare-output-lines-by-subset";
     pub const LLVM_COV_FLAGS: &'static str = "llvm-cov-flags";
     pub const FILECHECK_FLAGS: &'static str = "filecheck-flags";
     pub const NO_AUTO_CHECK_CFG: &'static str = "no-auto-check-cfg";
@@ -273,7 +270,6 @@ impl TestProps {
             check_run_results: false,
             dont_check_compiler_stdout: false,
             dont_check_compiler_stderr: false,
-            compare_output_lines_by_subset: false,
             no_prefer_dynamic: false,
             pretty_mode: "normal".to_string(),
             pretty_compare_only: false,
@@ -549,11 +545,6 @@ impl TestProps {
                         |s| s.trim().to_string(),
                     );
                     config.set_name_directive(ln, REMAP_SRC_BASE, &mut self.remap_src_base);
-                    config.set_name_directive(
-                        ln,
-                        COMPARE_OUTPUT_LINES_BY_SUBSET,
-                        &mut self.compare_output_lines_by_subset,
-                    );
 
                     if let Some(flags) = config.parse_name_value_directive(ln, LLVM_COV_FLAGS) {
                         self.llvm_cov_flags.extend(split_flags(&flags));
@@ -1134,6 +1125,8 @@ fn parse_normalize_rule(header: &str) -> Option<(String, String)> {
     .captures(header)?;
     let regex = captures["regex"].to_owned();
     let replacement = captures["replacement"].to_owned();
+    // FIXME: Support escaped new-line in strings.
+    let replacement = replacement.replace("\\n", "\n");
     Some((regex, replacement))
 }
 
diff --git a/src/tools/compiletest/src/header/auxiliary.rs b/src/tools/compiletest/src/header/auxiliary.rs
index 6f6538ce196..0e1f3a785f8 100644
--- a/src/tools/compiletest/src/header/auxiliary.rs
+++ b/src/tools/compiletest/src/header/auxiliary.rs
@@ -4,7 +4,7 @@
 use std::iter;
 
 use crate::common::Config;
-use crate::header::directives::{AUX_BIN, AUX_BUILD, AUX_CODEGEN_BACKEND, AUX_CRATE};
+use crate::header::directives::{AUX_BIN, AUX_BUILD, AUX_CODEGEN_BACKEND, AUX_CRATE, PROC_MACRO};
 
 /// Properties parsed from `aux-*` test directives.
 #[derive(Clone, Debug, Default)]
@@ -17,6 +17,8 @@ pub(crate) struct AuxProps {
     /// Similar to `builds`, but a list of NAME=somelib.rs of dependencies
     /// to build and pass with the `--extern` flag.
     pub(crate) crates: Vec<(String, String)>,
+    /// Same as `builds`, but for proc-macros.
+    pub(crate) proc_macros: Vec<String>,
     /// Similar to `builds`, but also uses the resulting dylib as a
     /// `-Zcodegen-backend` when compiling the test file.
     pub(crate) codegen_backend: Option<String>,
@@ -26,12 +28,13 @@ impl AuxProps {
     /// Yields all of the paths (relative to `./auxiliary/`) that have been
     /// specified in `aux-*` directives for this test.
     pub(crate) fn all_aux_path_strings(&self) -> impl Iterator<Item = &str> {
-        let Self { builds, bins, crates, codegen_backend } = self;
+        let Self { builds, bins, crates, proc_macros, codegen_backend } = self;
 
         iter::empty()
             .chain(builds.iter().map(String::as_str))
             .chain(bins.iter().map(String::as_str))
             .chain(crates.iter().map(|(_, path)| path.as_str()))
+            .chain(proc_macros.iter().map(String::as_str))
             .chain(codegen_backend.iter().map(String::as_str))
     }
 }
@@ -39,13 +42,15 @@ impl AuxProps {
 /// If the given test directive line contains an `aux-*` directive, parse it
 /// and update [`AuxProps`] accordingly.
 pub(super) fn parse_and_update_aux(config: &Config, ln: &str, aux: &mut AuxProps) {
-    if !ln.starts_with("aux-") {
+    if !(ln.starts_with("aux-") || ln.starts_with("proc-macro")) {
         return;
     }
 
     config.push_name_value_directive(ln, AUX_BUILD, &mut aux.builds, |r| r.trim().to_string());
     config.push_name_value_directive(ln, AUX_BIN, &mut aux.bins, |r| r.trim().to_string());
     config.push_name_value_directive(ln, AUX_CRATE, &mut aux.crates, parse_aux_crate);
+    config
+        .push_name_value_directive(ln, PROC_MACRO, &mut aux.proc_macros, |r| r.trim().to_string());
     if let Some(r) = config.parse_name_value_directive(ln, AUX_CODEGEN_BACKEND) {
         aux.codegen_backend = Some(r.trim().to_owned());
     }
diff --git a/src/tools/compiletest/src/header/needs.rs b/src/tools/compiletest/src/header/needs.rs
index 77570c58db5..e19dcd992fc 100644
--- a/src/tools/compiletest/src/header/needs.rs
+++ b/src/tools/compiletest/src/header/needs.rs
@@ -1,4 +1,4 @@
-use crate::common::{Config, Sanitizer};
+use crate::common::{Config, KNOWN_TARGET_HAS_ATOMIC_WIDTHS, Sanitizer};
 use crate::header::{IgnoreDecision, llvm_has_libzstd};
 
 pub(super) fn handle_needs(
@@ -171,11 +171,54 @@ pub(super) fn handle_needs(
         },
     ];
 
-    let (name, comment) = match ln.split_once([':', ' ']) {
-        Some((name, comment)) => (name, Some(comment)),
+    let (name, rest) = match ln.split_once([':', ' ']) {
+        Some((name, rest)) => (name, Some(rest)),
         None => (ln, None),
     };
 
+    // FIXME(jieyouxu): tighten up this parsing to reject using both `:` and ` ` as means to
+    // delineate value.
+    if name == "needs-target-has-atomic" {
+        let Some(rest) = rest else {
+            return IgnoreDecision::Error {
+                message: "expected `needs-target-has-atomic` to have a comma-separated list of atomic widths".to_string(),
+            };
+        };
+
+        // Expect directive value to be a list of comma-separated atomic widths.
+        let specified_widths = rest
+            .split(',')
+            .map(|width| width.trim())
+            .map(ToString::to_string)
+            .collect::<Vec<String>>();
+
+        for width in &specified_widths {
+            if !KNOWN_TARGET_HAS_ATOMIC_WIDTHS.contains(&width.as_str()) {
+                return IgnoreDecision::Error {
+                    message: format!(
+                        "unknown width specified in `needs-target-has-atomic`: `{width}` is not a \
+                        known `target_has_atomic_width`, known values are `{:?}`",
+                        KNOWN_TARGET_HAS_ATOMIC_WIDTHS
+                    ),
+                };
+            }
+        }
+
+        let satisfies_all_specified_widths = specified_widths
+            .iter()
+            .all(|specified| config.target_cfg().target_has_atomic.contains(specified));
+        if satisfies_all_specified_widths {
+            return IgnoreDecision::Continue;
+        } else {
+            return IgnoreDecision::Ignore {
+                reason: format!(
+                    "skipping test as target does not support all of the required `target_has_atomic` widths `{:?}`",
+                    specified_widths
+                ),
+            };
+        }
+    }
+
     if !name.starts_with("needs-") {
         return IgnoreDecision::Continue;
     }
@@ -193,7 +236,7 @@ pub(super) fn handle_needs(
                 break;
             } else {
                 return IgnoreDecision::Ignore {
-                    reason: if let Some(comment) = comment {
+                    reason: if let Some(comment) = rest {
                         format!("{} ({})", need.ignore_reason, comment.trim())
                     } else {
                         need.ignore_reason.into()
diff --git a/src/tools/compiletest/src/header/tests.rs b/src/tools/compiletest/src/header/tests.rs
index 4d75c38dd32..cd7c6f8361e 100644
--- a/src/tools/compiletest/src/header/tests.rs
+++ b/src/tools/compiletest/src/header/tests.rs
@@ -787,3 +787,40 @@ fn test_not_trailing_directive() {
     run_path(&mut poisoned, Path::new("a.rs"), b"//@ revisions: incremental");
     assert!(!poisoned);
 }
+
+#[test]
+fn test_needs_target_has_atomic() {
+    use std::collections::BTreeSet;
+
+    // `x86_64-unknown-linux-gnu` supports `["8", "16", "32", "64", "ptr"]` but not `128`.
+
+    let config = cfg().target("x86_64-unknown-linux-gnu").build();
+    // Expectation sanity check.
+    assert_eq!(
+        config.target_cfg().target_has_atomic,
+        BTreeSet::from([
+            "8".to_string(),
+            "16".to_string(),
+            "32".to_string(),
+            "64".to_string(),
+            "ptr".to_string()
+        ]),
+        "expected `x86_64-unknown-linux-gnu` to not have 128-bit atomic support"
+    );
+
+    assert!(!check_ignore(&config, "//@ needs-target-has-atomic: 8"));
+    assert!(!check_ignore(&config, "//@ needs-target-has-atomic: 16"));
+    assert!(!check_ignore(&config, "//@ needs-target-has-atomic: 32"));
+    assert!(!check_ignore(&config, "//@ needs-target-has-atomic: 64"));
+    assert!(!check_ignore(&config, "//@ needs-target-has-atomic: ptr"));
+
+    assert!(check_ignore(&config, "//@ needs-target-has-atomic: 128"));
+
+    assert!(!check_ignore(&config, "//@ needs-target-has-atomic: 8,16,32,64,ptr"));
+
+    assert!(check_ignore(&config, "//@ needs-target-has-atomic: 8,16,32,64,ptr,128"));
+
+    // Check whitespace between widths is permitted.
+    assert!(!check_ignore(&config, "//@ needs-target-has-atomic: 8, ptr"));
+    assert!(check_ignore(&config, "//@ needs-target-has-atomic: 8, ptr, 128"));
+}
diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs
index bf4a3124075..9acb7d393b4 100644
--- a/src/tools/compiletest/src/lib.rs
+++ b/src/tools/compiletest/src/lib.rs
@@ -37,8 +37,8 @@ use walkdir::WalkDir;
 
 use self::header::{EarlyProps, make_test_description};
 use crate::common::{
-    Config, Mode, PassMode, TestPaths, UI_EXTENSIONS, expected_output_path, output_base_dir,
-    output_relative_path,
+    CompareMode, Config, Mode, PassMode, TestPaths, UI_EXTENSIONS, expected_output_path,
+    output_base_dir, output_relative_path,
 };
 use crate::header::HeadersCache;
 use crate::util::logv;
@@ -188,8 +188,8 @@ pub fn parse_config(args: Vec<String>) -> Config {
     let (argv0, args_) = args.split_first().unwrap();
     if args.len() == 1 || args[1] == "-h" || args[1] == "--help" {
         let message = format!("Usage: {} [OPTIONS] [TESTNAME...]", argv0);
-        println!("{}", opts.usage(&message));
-        println!();
+        eprintln!("{}", opts.usage(&message));
+        eprintln!();
         panic!()
     }
 
@@ -200,8 +200,8 @@ pub fn parse_config(args: Vec<String>) -> Config {
 
     if matches.opt_present("h") || matches.opt_present("help") {
         let message = format!("Usage: {} [OPTIONS]  [TESTNAME...]", argv0);
-        println!("{}", opts.usage(&message));
-        println!();
+        eprintln!("{}", opts.usage(&message));
+        eprintln!();
         panic!()
     }
 
@@ -273,6 +273,15 @@ pub fn parse_config(args: Vec<String>) -> Config {
     } else {
         matches.free.clone()
     };
+    let compare_mode = matches.opt_str("compare-mode").map(|s| {
+        s.parse().unwrap_or_else(|_| {
+            let variants: Vec<_> = CompareMode::STR_VARIANTS.iter().copied().collect();
+            panic!(
+                "`{s}` is not a valid value for `--compare-mode`, it should be one of: {}",
+                variants.join(", ")
+            );
+        })
+    });
     Config {
         bless: matches.opt_present("bless"),
         compile_lib_path: make_absolute(opt_path(matches, "compile-lib-path")),
@@ -342,9 +351,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
         only_modified: matches.opt_present("only-modified"),
         color,
         remote_test_client: matches.opt_str("remote-test-client").map(PathBuf::from),
-        compare_mode: matches
-            .opt_str("compare-mode")
-            .map(|s| s.parse().expect("invalid --compare-mode provided")),
+        compare_mode,
         rustfix_coverage: matches.opt_present("rustfix-coverage"),
         has_html_tidy,
         has_enzyme,
@@ -501,7 +508,7 @@ pub fn run_tests(config: Arc<Config>) {
             // easy to miss which tests failed, and as such fail to reproduce
             // the failure locally.
 
-            println!(
+            eprintln!(
                 "Some tests failed in compiletest suite={}{} mode={} host={} target={}",
                 config.suite,
                 config
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index bc80c8246ad..ca746ed8c55 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -22,7 +22,7 @@ use crate::common::{
     UI_STDERR, UI_STDOUT, UI_SVG, UI_WINDOWS_SVG, Ui, expected_output_path, incremental_dir,
     output_base_dir, output_base_name, output_testname_unique,
 };
-use crate::compute_diff::{write_diff, write_filtered_diff};
+use crate::compute_diff::{DiffLine, make_diff, write_diff, write_filtered_diff};
 use crate::errors::{self, Error, ErrorKind};
 use crate::header::TestProps;
 use crate::read2::{Truncated, read2_abbreviated};
@@ -102,7 +102,7 @@ fn get_lib_name(name: &str, aux_type: AuxType) -> Option<String> {
         // In this case, the only path we can pass
         // with '--extern-meta' is the '.rlib' file
         AuxType::Lib => Some(format!("lib{name}.rlib")),
-        AuxType::Dylib => Some(dylib_name(name)),
+        AuxType::Dylib | AuxType::ProcMacro => Some(dylib_name(name)),
     }
 }
 
@@ -131,7 +131,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");
+        eprintln!("\n");
     }
     debug!("running {:?}", testpaths.file.display());
     let mut props = TestProps::from_file(&testpaths.file, revision, &config);
@@ -350,10 +350,13 @@ impl<'test> TestCx<'test> {
             }
         } else {
             if proc_res.status.success() {
-                self.fatal_proc_rec(
-                    &format!("{} test compiled successfully!", self.config.mode)[..],
-                    proc_res,
-                );
+                {
+                    self.error(&format!("{} test did not emit an error", self.config.mode));
+                    if self.config.mode == crate::common::Mode::Ui {
+                        eprintln!("note: by default, ui tests are expected not to compile");
+                    }
+                    proc_res.fatal(None, || ());
+                };
             }
 
             if !self.props.dont_check_failure_status {
@@ -771,20 +774,20 @@ impl<'test> TestCx<'test> {
                 unexpected.len(),
                 not_found.len()
             ));
-            println!("status: {}\ncommand: {}\n", proc_res.status, proc_res.cmdline);
+            eprintln!("status: {}\ncommand: {}\n", proc_res.status, proc_res.cmdline);
             if !unexpected.is_empty() {
-                println!("{}", "--- unexpected errors (from JSON output) ---".green());
+                eprintln!("{}", "--- unexpected errors (from JSON output) ---".green());
                 for error in &unexpected {
-                    println!("{}", error.render_for_expected());
+                    eprintln!("{}", error.render_for_expected());
                 }
-                println!("{}", "---".green());
+                eprintln!("{}", "---".green());
             }
             if !not_found.is_empty() {
-                println!("{}", "--- not found errors (from test file) ---".red());
+                eprintln!("{}", "--- not found errors (from test file) ---".red());
                 for error in &not_found {
-                    println!("{}", error.render_for_expected());
+                    eprintln!("{}", error.render_for_expected());
                 }
-                println!("{}", "---\n".red());
+                eprintln!("{}", "---\n".red());
             }
             panic!("errors differ from expected");
         }
@@ -1097,7 +1100,9 @@ impl<'test> TestCx<'test> {
     }
 
     fn has_aux_dir(&self) -> bool {
-        !self.props.aux.builds.is_empty() || !self.props.aux.crates.is_empty()
+        !self.props.aux.builds.is_empty()
+            || !self.props.aux.crates.is_empty()
+            || !self.props.aux.proc_macros.is_empty()
     }
 
     fn aux_output_dir(&self) -> PathBuf {
@@ -1118,31 +1123,48 @@ impl<'test> TestCx<'test> {
 
     fn build_all_auxiliary(&self, of: &TestPaths, aux_dir: &Path, rustc: &mut Command) {
         for rel_ab in &self.props.aux.builds {
-            self.build_auxiliary(of, rel_ab, &aux_dir, false /* is_bin */);
+            self.build_auxiliary(of, rel_ab, &aux_dir, None);
         }
 
         for rel_ab in &self.props.aux.bins {
-            self.build_auxiliary(of, rel_ab, &aux_dir, true /* is_bin */);
+            self.build_auxiliary(of, rel_ab, &aux_dir, Some(AuxType::Bin));
         }
 
+        let path_to_crate_name = |path: &str| -> String {
+            path.rsplit_once('/')
+                .map_or(path, |(_, tail)| tail)
+                .trim_end_matches(".rs")
+                .replace('-', "_")
+        };
+
+        let add_extern =
+            |rustc: &mut Command, aux_name: &str, aux_path: &str, aux_type: AuxType| {
+                let lib_name = get_lib_name(&path_to_crate_name(aux_path), aux_type);
+                if let Some(lib_name) = lib_name {
+                    rustc.arg("--extern").arg(format!(
+                        "{}={}/{}",
+                        aux_name,
+                        aux_dir.display(),
+                        lib_name
+                    ));
+                }
+            };
+
         for (aux_name, aux_path) in &self.props.aux.crates {
-            let aux_type = self.build_auxiliary(of, &aux_path, &aux_dir, false /* is_bin */);
-            let lib_name =
-                get_lib_name(&aux_path.trim_end_matches(".rs").replace('-', "_"), aux_type);
-            if let Some(lib_name) = lib_name {
-                rustc.arg("--extern").arg(format!(
-                    "{}={}/{}",
-                    aux_name,
-                    aux_dir.display(),
-                    lib_name
-                ));
-            }
+            let aux_type = self.build_auxiliary(of, &aux_path, &aux_dir, None);
+            add_extern(rustc, aux_name, aux_path, aux_type);
+        }
+
+        for proc_macro in &self.props.aux.proc_macros {
+            self.build_auxiliary(of, proc_macro, &aux_dir, Some(AuxType::ProcMacro));
+            let crate_name = path_to_crate_name(proc_macro);
+            add_extern(rustc, &crate_name, proc_macro, AuxType::ProcMacro);
         }
 
         // Build any `//@ aux-codegen-backend`, and pass the resulting library
         // to `-Zcodegen-backend` when compiling the test file.
         if let Some(aux_file) = &self.props.aux.codegen_backend {
-            let aux_type = self.build_auxiliary(of, aux_file, aux_dir, false);
+            let aux_type = self.build_auxiliary(of, aux_file, aux_dir, None);
             if let Some(lib_name) = get_lib_name(aux_file.trim_end_matches(".rs"), aux_type) {
                 let lib_path = aux_dir.join(&lib_name);
                 rustc.arg(format!("-Zcodegen-backend={}", lib_path.display()));
@@ -1209,17 +1231,23 @@ impl<'test> TestCx<'test> {
     }
 
     /// Builds an aux dependency.
+    ///
+    /// If `aux_type` is `None`, then this will determine the aux-type automatically.
     fn build_auxiliary(
         &self,
         of: &TestPaths,
         source_path: &str,
         aux_dir: &Path,
-        is_bin: bool,
+        aux_type: Option<AuxType>,
     ) -> AuxType {
         let aux_testpaths = self.compute_aux_test_paths(of, source_path);
-        let aux_props = self.props.from_aux_file(&aux_testpaths.file, self.revision, self.config);
+        let mut aux_props =
+            self.props.from_aux_file(&aux_testpaths.file, self.revision, self.config);
+        if aux_type == Some(AuxType::ProcMacro) {
+            aux_props.force_host = true;
+        }
         let mut aux_dir = aux_dir.to_path_buf();
-        if is_bin {
+        if aux_type == Some(AuxType::Bin) {
             // On unix, the binary of `auxiliary/foo.rs` will be named
             // `auxiliary/foo` which clashes with the _dir_ `auxiliary/foo`, so
             // put bins in a `bin` subfolder.
@@ -1250,8 +1278,12 @@ impl<'test> TestCx<'test> {
             aux_rustc.env_remove(key);
         }
 
-        let (aux_type, crate_type) = if is_bin {
+        let (aux_type, crate_type) = if aux_type == Some(AuxType::Bin) {
             (AuxType::Bin, Some("bin"))
+        } else if aux_type == Some(AuxType::ProcMacro) {
+            (AuxType::ProcMacro, Some("proc-macro"))
+        } else if aux_type.is_some() {
+            panic!("aux_type {aux_type:?} not expected");
         } else if aux_props.no_prefer_dynamic {
             (AuxType::Dylib, None)
         } else if self.config.target.contains("emscripten")
@@ -1287,6 +1319,11 @@ impl<'test> TestCx<'test> {
             aux_rustc.args(&["--crate-type", crate_type]);
         }
 
+        if aux_type == AuxType::ProcMacro {
+            // For convenience, but this only works on 2018.
+            aux_rustc.args(&["--extern", "proc_macro"]);
+        }
+
         aux_rustc.arg("-L").arg(&aux_dir);
 
         let auxres = aux_cx.compose_and_run(
@@ -1839,18 +1876,18 @@ impl<'test> TestCx<'test> {
 
     fn maybe_dump_to_stdout(&self, out: &str, err: &str) {
         if self.config.verbose {
-            println!("------stdout------------------------------");
-            println!("{}", out);
-            println!("------stderr------------------------------");
-            println!("{}", err);
-            println!("------------------------------------------");
+            eprintln!("------stdout------------------------------");
+            eprintln!("{}", out);
+            eprintln!("------stderr------------------------------");
+            eprintln!("{}", err);
+            eprintln!("------------------------------------------");
         }
     }
 
     fn error(&self, err: &str) {
         match self.revision {
-            Some(rev) => println!("\nerror in revision `{}`: {}", rev, err),
-            None => println!("\nerror: {}", err),
+            Some(rev) => eprintln!("\nerror in revision `{}`: {}", rev, err),
+            None => eprintln!("\nerror: {}", err),
         }
     }
 
@@ -1935,7 +1972,7 @@ impl<'test> TestCx<'test> {
         if !self.config.has_html_tidy {
             return;
         }
-        println!("info: generating a diff against nightly rustdoc");
+        eprintln!("info: generating a diff against nightly rustdoc");
 
         let suffix =
             self.safe_revision().map_or("nightly".into(), |path| path.to_owned() + "-nightly");
@@ -2045,7 +2082,7 @@ impl<'test> TestCx<'test> {
                 .output()
                 .unwrap();
             assert!(output.status.success());
-            println!("{}", String::from_utf8_lossy(&output.stdout));
+            eprintln!("{}", String::from_utf8_lossy(&output.stdout));
             eprintln!("{}", String::from_utf8_lossy(&output.stderr));
         } else {
             use colored::Colorize;
@@ -2258,17 +2295,31 @@ impl<'test> TestCx<'test> {
         match output_kind {
             TestOutput::Compile => {
                 if !self.props.dont_check_compiler_stdout {
-                    errors +=
-                        self.compare_output(stdout_kind, &normalized_stdout, &expected_stdout);
+                    errors += self.compare_output(
+                        stdout_kind,
+                        &normalized_stdout,
+                        &proc_res.stdout,
+                        &expected_stdout,
+                    );
                 }
                 if !self.props.dont_check_compiler_stderr {
-                    errors +=
-                        self.compare_output(stderr_kind, &normalized_stderr, &expected_stderr);
+                    errors += self.compare_output(
+                        stderr_kind,
+                        &normalized_stderr,
+                        &stderr,
+                        &expected_stderr,
+                    );
                 }
             }
             TestOutput::Run => {
-                errors += self.compare_output(stdout_kind, &normalized_stdout, &expected_stdout);
-                errors += self.compare_output(stderr_kind, &normalized_stderr, &expected_stderr);
+                errors += self.compare_output(
+                    stdout_kind,
+                    &normalized_stdout,
+                    &proc_res.stdout,
+                    &expected_stdout,
+                );
+                errors +=
+                    self.compare_output(stderr_kind, &normalized_stderr, &stderr, &expected_stderr);
             }
         }
         errors
@@ -2445,7 +2496,7 @@ impl<'test> TestCx<'test> {
                 )"#
             )
             .replace_all(&output, |caps: &Captures<'_>| {
-                println!("{}", &caps[0]);
+                eprintln!("{}", &caps[0]);
                 caps[0].replace(r"\", "/")
             })
             .replace("\r\n", "\n")
@@ -2496,7 +2547,13 @@ impl<'test> TestCx<'test> {
         }
     }
 
-    fn compare_output(&self, stream: &str, actual: &str, expected: &str) -> usize {
+    fn compare_output(
+        &self,
+        stream: &str,
+        actual: &str,
+        actual_unnormalized: &str,
+        expected: &str,
+    ) -> usize {
         let are_different = match (self.force_color_svg(), expected.find('\n'), actual.find('\n')) {
             // FIXME: We ignore the first line of SVG files
             // because the width parameter is non-deterministic.
@@ -2507,13 +2564,10 @@ impl<'test> TestCx<'test> {
             return 0;
         }
 
-        // If `compare-output-lines-by-subset` is not explicitly enabled then
-        // auto-enable it when a `runner` is in use since wrapper tools 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.props.compare_output_lines_by_subset || self.config.runner.is_some();
+        // 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();
 
         let tmp;
         let (expected, actual): (&str, &str) = if compare_output_by_lines {
@@ -2547,37 +2601,23 @@ 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:?}");
+        eprintln!("Saved the actual {stream} to {actual_path:?}");
 
         let expected_path =
             expected_output_path(self.testpaths, self.revision, &self.config.compare_mode, stream);
 
         if !self.config.bless {
             if expected.is_empty() {
-                println!("normalized {}:\n{}\n", stream, actual);
+                eprintln!("normalized {}:\n{}\n", stream, actual);
             } else {
-                println!("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();
-                    match Command::new(name)
-                        .args(args)
-                        .args([&expected_path, &actual_path])
-                        .output()
-                    {
-                        Err(err) => {
-                            self.fatal(&format!(
-                                "failed to call custom diff command `{diff_command}`: {err}"
-                            ));
-                        }
-                        Ok(output) => {
-                            let output = String::from_utf8_lossy(&output.stdout);
-                            print!("{output}");
-                        }
-                    }
-                } else {
-                    print!("{}", write_diff(expected, actual, 3));
-                }
+                self.show_diff(
+                    stream,
+                    &expected_path,
+                    &actual_path,
+                    expected,
+                    actual,
+                    actual_unnormalized,
+                );
             }
         } else {
             // Delete non-revision .stderr/.stdout file if revisions are used.
@@ -2591,14 +2631,84 @@ 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!("Blessing the {stream} of {test_name} in {expected_path:?}");
+            eprintln!("Blessing the {stream} of {test_name} in {expected_path:?}");
         }
 
-        println!("\nThe actual {0} differed from the expected {0}.", stream);
+        eprintln!("\nThe actual {0} differed from the expected {0}.", stream);
 
         if self.config.bless { 0 } else { 1 }
     }
 
+    /// Returns whether to show the full stderr/stdout.
+    fn show_diff(
+        &self,
+        stream: &str,
+        expected_path: &Path,
+        actual_path: &Path,
+        expected: &str,
+        actual: &str,
+        actual_unnormalized: &str,
+    ) {
+        eprintln!("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();
+            match Command::new(name).args(args).args([expected_path, actual_path]).output() {
+                Err(err) => {
+                    self.fatal(&format!(
+                        "failed to call custom diff command `{diff_command}`: {err}"
+                    ));
+                }
+                Ok(output) => {
+                    let output = String::from_utf8_lossy(&output.stdout);
+                    eprint!("{output}");
+                }
+            }
+        } else {
+            eprint!("{}", 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.
+        let diff_results = make_diff(actual, expected, 0);
+
+        let (mut mismatches_normalized, mut mismatch_line_nos) = (String::new(), vec![]);
+        for hunk in diff_results {
+            let mut line_no = hunk.line_number;
+            for line in hunk.lines {
+                // NOTE: `Expected` is actually correct here, the argument order is reversed so our line numbers match up
+                if let DiffLine::Expected(normalized) = line {
+                    mismatches_normalized += &normalized;
+                    mismatches_normalized += "\n";
+                    mismatch_line_nos.push(line_no);
+                    line_no += 1;
+                }
+            }
+        }
+        let mut mismatches_unnormalized = String::new();
+        let diff_normalized = make_diff(actual, actual_unnormalized, 0);
+        for hunk in diff_normalized {
+            if mismatch_line_nos.contains(&hunk.line_number) {
+                for line in hunk.lines {
+                    if let DiffLine::Resulting(unnormalized) = line {
+                        mismatches_unnormalized += &unnormalized;
+                        mismatches_unnormalized += "\n";
+                    }
+                }
+            }
+        }
+
+        let normalized_diff = make_diff(&mismatches_normalized, &mismatches_unnormalized, 0);
+        // HACK: instead of checking if each hunk is empty, this only checks if the whole input is empty. we should be smarter about this so we don't treat added or removed output as normalized.
+        if !normalized_diff.is_empty()
+            && !mismatches_unnormalized.is_empty()
+            && !mismatches_normalized.is_empty()
+        {
+            eprintln!("Note: some mismatched output was normalized before being compared");
+            // FIXME: respect diff_command
+            eprint!("{}", write_diff(&mismatches_unnormalized, &mismatches_normalized, 0));
+        }
+    }
+
     fn check_and_prune_duplicate_outputs(
         &self,
         proc_res: &ProcRes,
@@ -2673,7 +2783,7 @@ impl<'test> TestCx<'test> {
         fs::create_dir_all(&incremental_dir).unwrap();
 
         if self.config.verbose {
-            println!("init_incremental_test: incremental_dir={}", incremental_dir.display());
+            eprintln!("init_incremental_test: incremental_dir={}", incremental_dir.display());
         }
     }
 
@@ -2731,7 +2841,7 @@ impl ProcRes {
             }
         }
 
-        println!(
+        eprintln!(
             "status: {}\ncommand: {}\n{}\n{}\n",
             self.status,
             self.cmdline,
@@ -2742,7 +2852,7 @@ impl ProcRes {
 
     pub fn fatal(&self, err: Option<&str>, on_failure: impl FnOnce()) -> ! {
         if let Some(e) = err {
-            println!("\nerror: {}", e);
+            eprintln!("\nerror: {}", e);
         }
         self.print_info();
         on_failure();
@@ -2768,8 +2878,10 @@ enum LinkToAux {
     No,
 }
 
+#[derive(Debug, PartialEq)]
 enum AuxType {
     Bin,
     Lib,
     Dylib,
+    ProcMacro,
 }
diff --git a/src/tools/compiletest/src/runtest/codegen_units.rs b/src/tools/compiletest/src/runtest/codegen_units.rs
index 6c866cbef21..6acd140183d 100644
--- a/src/tools/compiletest/src/runtest/codegen_units.rs
+++ b/src/tools/compiletest/src/runtest/codegen_units.rs
@@ -64,13 +64,13 @@ impl TestCx<'_> {
         if !missing.is_empty() {
             missing.sort();
 
-            println!("\nThese items should have been contained but were not:\n");
+            eprintln!("\nThese items should have been contained but were not:\n");
 
             for item in &missing {
-                println!("{}", item);
+                eprintln!("{}", item);
             }
 
-            println!("\n");
+            eprintln!("\n");
         }
 
         if !unexpected.is_empty() {
@@ -80,24 +80,24 @@ impl TestCx<'_> {
                 sorted
             };
 
-            println!("\nThese items were contained but should not have been:\n");
+            eprintln!("\nThese items were contained but should not have been:\n");
 
             for item in sorted {
-                println!("{}", item);
+                eprintln!("{}", item);
             }
 
-            println!("\n");
+            eprintln!("\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");
+            eprintln!("\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!();
+                eprintln!("{}", expected_item.name);
+                eprintln!("  expected: {}", codegen_units_to_str(&expected_item.codegen_units));
+                eprintln!("  actual:   {}", codegen_units_to_str(&actual_item.codegen_units));
+                eprintln!();
             }
         }
 
diff --git a/src/tools/compiletest/src/runtest/coverage.rs b/src/tools/compiletest/src/runtest/coverage.rs
index 961a1602986..030ca5ebb24 100644
--- a/src/tools/compiletest/src/runtest/coverage.rs
+++ b/src/tools/compiletest/src/runtest/coverage.rs
@@ -39,8 +39,12 @@ impl<'test> TestCx<'test> {
         let expected_coverage_dump = self.load_expected_output(kind);
         let actual_coverage_dump = self.normalize_output(&proc_res.stdout, &[]);
 
-        let coverage_dump_errors =
-            self.compare_output(kind, &actual_coverage_dump, &expected_coverage_dump);
+        let coverage_dump_errors = self.compare_output(
+            kind,
+            &actual_coverage_dump,
+            &proc_res.stdout,
+            &expected_coverage_dump,
+        );
 
         if coverage_dump_errors > 0 {
             self.fatal_proc_rec(
@@ -135,8 +139,12 @@ impl<'test> TestCx<'test> {
                 self.fatal_proc_rec(&err, &proc_res);
             });
 
-        let coverage_errors =
-            self.compare_output(kind, &normalized_actual_coverage, &expected_coverage);
+        let coverage_errors = self.compare_output(
+            kind,
+            &normalized_actual_coverage,
+            &proc_res.stdout,
+            &expected_coverage,
+        );
 
         if coverage_errors > 0 {
             self.fatal_proc_rec(
diff --git a/src/tools/compiletest/src/runtest/debuginfo.rs b/src/tools/compiletest/src/runtest/debuginfo.rs
index c621c22ac99..7322e730e53 100644
--- a/src/tools/compiletest/src/runtest/debuginfo.rs
+++ b/src/tools/compiletest/src/runtest/debuginfo.rs
@@ -260,7 +260,7 @@ impl TestCx<'_> {
                 cmdline,
             };
             if adb.kill().is_err() {
-                println!("Adb process is already finished.");
+                eprintln!("Adb process is already finished.");
             }
         } else {
             let rust_src_root =
@@ -275,7 +275,7 @@ impl TestCx<'_> {
 
             match self.config.gdb_version {
                 Some(version) => {
-                    println!("NOTE: compiletest thinks it is using GDB version {}", version);
+                    eprintln!("NOTE: compiletest thinks it is using GDB version {}", version);
 
                     if version > extract_gdb_version("7.4").unwrap() {
                         // Add the directory containing the pretty printers to
@@ -297,7 +297,7 @@ impl TestCx<'_> {
                     }
                 }
                 _ => {
-                    println!(
+                    eprintln!(
                         "NOTE: compiletest does not know which version of \
                          GDB it is using"
                     );
@@ -392,10 +392,10 @@ impl TestCx<'_> {
 
         match self.config.lldb_version {
             Some(ref version) => {
-                println!("NOTE: compiletest thinks it is using LLDB version {}", version);
+                eprintln!("NOTE: compiletest thinks it is using LLDB version {}", version);
             }
             _ => {
-                println!(
+                eprintln!(
                     "NOTE: compiletest does not know which version of \
                      LLDB it is using"
                 );
diff --git a/src/tools/compiletest/src/runtest/incremental.rs b/src/tools/compiletest/src/runtest/incremental.rs
index 591aff0defe..4f26786129b 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);
+            eprint!("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 d1ec0035744..64a2addaa28 100644
--- a/src/tools/compiletest/src/runtest/mir_opt.rs
+++ b/src/tools/compiletest/src/runtest/mir_opt.rs
@@ -89,7 +89,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));
+                    eprint!("{}", 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/rustdoc_json.rs b/src/tools/compiletest/src/runtest/rustdoc_json.rs
index 31fdb0a5d13..84376d346af 100644
--- a/src/tools/compiletest/src/runtest/rustdoc_json.rs
+++ b/src/tools/compiletest/src/runtest/rustdoc_json.rs
@@ -29,7 +29,7 @@ impl TestCx<'_> {
 
         if !res.status.success() {
             self.fatal_proc_rec_with_ctx("jsondocck failed!", &res, |_| {
-                println!("Rustdoc Output:");
+                eprintln!("Rustdoc Output:");
                 proc_res.print_info();
             })
         }
diff --git a/src/tools/compiletest/src/runtest/ui.rs b/src/tools/compiletest/src/runtest/ui.rs
index bb747c68029..bc860bf18c7 100644
--- a/src/tools/compiletest/src/runtest/ui.rs
+++ b/src/tools/compiletest/src/runtest/ui.rs
@@ -100,7 +100,7 @@ impl TestCx<'_> {
                 )
             });
 
-            errors += self.compare_output("fixed", &fixed_code, &expected_fixed);
+            errors += self.compare_output("fixed", &fixed_code, &fixed_code, &expected_fixed);
         } else if !expected_fixed.is_empty() {
             panic!(
                 "the `//@ run-rustfix` directive wasn't found but a `*.fixed` \
@@ -109,10 +109,10 @@ impl TestCx<'_> {
         }
 
         if errors > 0 {
-            println!("To update references, rerun the tests and pass the `--bless` flag");
+            eprintln!("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!(
+            eprintln!(
                 "To only update this specific test, also pass `--test-args {}`",
                 relative_path_to_file.display(),
             );
diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs
index bff02f1db9f..7b50e62c29b 100644
--- a/src/tools/compiletest/src/util.rs
+++ b/src/tools/compiletest/src/util.rs
@@ -30,7 +30,7 @@ fn path_div() -> &'static str {
 pub fn logv(config: &Config, s: String) {
     debug!("{}", s);
     if config.verbose {
-        println!("{}", s);
+        eprintln!("{}", s);
     }
 }
 
diff --git a/src/tools/jsondocck/src/cache.rs b/src/tools/jsondocck/src/cache.rs
index 9f95f9fb408..47512039740 100644
--- a/src/tools/jsondocck/src/cache.rs
+++ b/src/tools/jsondocck/src/cache.rs
@@ -28,7 +28,8 @@ impl Cache {
         }
     }
 
-    pub fn value(&self) -> &Value {
-        &self.value
+    // FIXME: Make this failible, so jsonpath syntax error has line number.
+    pub fn select(&self, path: &str) -> Vec<&Value> {
+        jsonpath_lib::select(&self.value, path).unwrap()
     }
 }
diff --git a/src/tools/jsondocck/src/error.rs b/src/tools/jsondocck/src/error.rs
index c4cd79a64fd..0a3e085b405 100644
--- a/src/tools/jsondocck/src/error.rs
+++ b/src/tools/jsondocck/src/error.rs
@@ -1,29 +1,7 @@
-use std::error::Error;
-use std::fmt;
-
 use crate::Command;
 
 #[derive(Debug)]
-pub enum CkError {
-    /// A check failed. File didn't exist or failed to match the command
-    FailedCheck(String, Command),
-    /// An error triggered by some other error
-    Induced(Box<dyn Error>),
-}
-
-impl fmt::Display for CkError {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            CkError::FailedCheck(msg, cmd) => {
-                write!(f, "Failed check: {} on line {}", msg, cmd.lineno)
-            }
-            CkError::Induced(err) => write!(f, "Check failed: {}", err),
-        }
-    }
-}
-
-impl<T: Error + 'static> From<T> for CkError {
-    fn from(err: T) -> CkError {
-        CkError::Induced(Box::new(err))
-    }
+pub struct CkError {
+    pub message: String,
+    pub command: Command,
 }
diff --git a/src/tools/jsondocck/src/main.rs b/src/tools/jsondocck/src/main.rs
index c08bbbc769a..b6a1d7dfa7a 100644
--- a/src/tools/jsondocck/src/main.rs
+++ b/src/tools/jsondocck/src/main.rs
@@ -1,8 +1,8 @@
 use std::borrow::Cow;
+use std::process::ExitCode;
 use std::sync::OnceLock;
-use std::{env, fmt, fs};
+use std::{env, fs};
 
-use jsonpath_lib::select;
 use regex::{Regex, RegexBuilder};
 use serde_json::Value;
 
@@ -14,90 +14,134 @@ use cache::Cache;
 use config::parse_config;
 use error::CkError;
 
-fn main() -> Result<(), String> {
+fn main() -> ExitCode {
     let config = parse_config(env::args().collect());
 
     let mut failed = Vec::new();
     let mut cache = Cache::new(&config);
-    let commands = get_commands(&config.template)
-        .map_err(|_| format!("Jsondocck failed for {}", &config.template))?;
+    let Ok(commands) = get_commands(&config.template) else {
+        eprintln!("Jsondocck failed for {}", &config.template);
+        return ExitCode::FAILURE;
+    };
 
     for command in commands {
-        if let Err(e) = check_command(command, &mut cache) {
-            failed.push(e);
+        if let Err(message) = check_command(&command, &mut cache) {
+            failed.push(CkError { command, message });
         }
     }
 
     if failed.is_empty() {
-        Ok(())
+        ExitCode::SUCCESS
     } else {
         for i in failed {
-            eprintln!("{}", i);
+            eprintln!("{}:{}, command failed", config.template, i.command.lineno);
+            eprintln!("{}", i.message)
         }
-        Err(format!("Jsondocck failed for {}", &config.template))
+        ExitCode::FAILURE
     }
 }
 
 #[derive(Debug)]
 pub struct Command {
-    negated: bool,
     kind: CommandKind,
-    args: Vec<String>,
+    path: String,
     lineno: usize,
 }
 
 #[derive(Debug)]
-pub enum CommandKind {
-    Has,
-    Count,
-    Is,
-    IsMany,
-    Set,
+enum CommandKind {
+    /// `//@ has <path>`
+    ///
+    /// Checks the path exists.
+    HasPath,
+
+    /// `//@ has <path> <value>`
+    ///
+    /// Check one thing at the path  is equal to the value.
+    HasValue { value: String },
+
+    /// `//@ !has <path>`
+    ///
+    /// Checks the path doesn't exist.
+    HasNotPath,
+
+    /// `//@ is <path> <value>`
+    ///
+    /// Check the path is the given value.
+    Is { value: String },
+
+    /// `//@ is <path> <value> <value>...`
+    ///
+    /// Check that the path matches to exactly every given value.
+    IsMany { values: Vec<String> },
+
+    /// `//@ !is <path> <value>`
+    ///
+    /// Check the path isn't the given value.
+    IsNot { value: String },
+
+    /// `//@ count <path> <value>`
+    ///
+    /// Check the path has the expected number of matches.
+    CountIs { expected: usize },
+
+    /// `//@ set <name> = <path>`
+    Set { variable: String },
 }
 
 impl CommandKind {
-    fn validate(&self, args: &[String], lineno: usize) -> bool {
-        // FIXME(adotinthevoid): We should "parse, don't validate" here, so we avoid ad-hoc
-        // indexing in check_command.
-        let count = match self {
-            CommandKind::Has => (1..=2).contains(&args.len()),
-            CommandKind::IsMany => args.len() >= 2,
-            CommandKind::Count | CommandKind::Is => 2 == args.len(),
-            CommandKind::Set => 3 == args.len(),
-        };
+    /// Returns both the kind and the path.
+    ///
+    /// Returns `None` if the command isn't from jsondocck (e.g. from compiletest).
+    fn parse<'a>(command_name: &str, negated: bool, args: &'a [String]) -> Option<(Self, &'a str)> {
+        let kind = match (command_name, negated) {
+            ("count", false) => {
+                assert_eq!(args.len(), 2);
+                let expected = args[1].parse().expect("invalid number for `count`");
+                Self::CountIs { expected }
+            }
 
-        if !count {
-            print_err(&format!("Incorrect number of arguments to `{}`", self), lineno);
-            return false;
-        }
+            ("ismany", false) => {
+                // FIXME: Make this >= 3, and migrate len(values)==1 cases to @is
+                assert!(args.len() >= 2, "Not enough args to `ismany`");
+                let values = args[1..].to_owned();
+                Self::IsMany { values }
+            }
 
-        if let CommandKind::Count = self {
-            if args[1].parse::<usize>().is_err() {
-                print_err(
-                    &format!(
-                        "Second argument to `count` must be a valid usize (got `{}`)",
-                        args[1]
-                    ),
-                    lineno,
-                );
-                return false;
+            ("is", false) => {
+                assert_eq!(args.len(), 2);
+                Self::Is { value: args[1].clone() }
+            }
+            ("is", true) => {
+                assert_eq!(args.len(), 2);
+                Self::IsNot { value: args[1].clone() }
             }
-        }
 
-        true
-    }
-}
+            ("set", false) => {
+                assert_eq!(args.len(), 3);
+                assert_eq!(args[1], "=");
+                return Some((Self::Set { variable: args[0].clone() }, &args[2]));
+            }
 
-impl fmt::Display for CommandKind {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        let text = match self {
-            CommandKind::Has => "has",
-            CommandKind::IsMany => "ismany",
-            CommandKind::Count => "count",
-            CommandKind::Is => "is",
-            CommandKind::Set => "set",
+            ("has", false) => match args {
+                [_path] => Self::HasPath,
+                [_path, value] => Self::HasValue { value: value.clone() },
+                _ => panic!("`//@ has` must have 2 or 3 arguments, but got {args:?}"),
+            },
+            ("has", true) => {
+                assert_eq!(args.len(), 1, "args={args:?}");
+                Self::HasNotPath
+            }
+
+            (_, false) if KNOWN_DIRECTIVE_NAMES.contains(&command_name) => {
+                return None;
+            }
+            _ => {
+                panic!("Invalid command `//@ {}{command_name}`", if negated { "!" } else { "" })
+            }
         };
-        write!(f, "{}", text)
+
+        Some((kind, &args[0]))
     }
 }
 
@@ -125,8 +169,7 @@ fn print_err(msg: &str, lineno: usize) {
 //        See <https://github.com/rust-lang/rust/issues/125813#issuecomment-2141953780>.
 include!(concat!(env!("CARGO_MANIFEST_DIR"), "/../compiletest/src/directive-list.rs"));
 
-/// Get a list of commands from a file. Does the work of ensuring the commands
-/// are syntactically valid.
+/// Get a list of commands from a file.
 fn get_commands(template: &str) -> Result<Vec<Command>, ()> {
     let mut commands = Vec::new();
     let mut errors = false;
@@ -142,217 +185,102 @@ fn get_commands(template: &str) -> Result<Vec<Command>, ()> {
 
         let negated = cap.name("negated").unwrap().as_str() == "!";
 
-        let cmd = match cap.name("cmd").unwrap().as_str() {
-            "has" => CommandKind::Has,
-            "count" => CommandKind::Count,
-            "is" => CommandKind::Is,
-            "ismany" => CommandKind::IsMany,
-            "set" => CommandKind::Set,
-            // FIXME: See the comment above the `include!(...)`.
-            cmd if KNOWN_DIRECTIVE_NAMES.contains(&cmd) => continue,
-            cmd => {
-                print_err(&format!("Unrecognized command name `{cmd}`"), lineno);
-                errors = true;
-                continue;
-            }
-        };
-
-        let args = cap.name("args").map_or(Some(vec![]), |m| shlex::split(m.as_str()));
-
-        let args = match args {
+        let args_str = &cap["args"];
+        let args = match shlex::split(args_str) {
             Some(args) => args,
             None => {
-                print_err(
-                    &format!(
-                        "Invalid arguments to shlex::split: `{}`",
-                        cap.name("args").unwrap().as_str()
-                    ),
-                    lineno,
-                );
+                print_err(&format!("Invalid arguments to shlex::split: `{args_str}`",), lineno);
                 errors = true;
                 continue;
             }
         };
 
-        if !cmd.validate(&args, lineno) {
-            errors = true;
-            continue;
+        if let Some((kind, path)) = CommandKind::parse(&cap["cmd"], negated, &args) {
+            commands.push(Command { kind, lineno, path: path.to_owned() })
         }
-
-        commands.push(Command { negated, kind: cmd, args, lineno })
     }
 
     if !errors { Ok(commands) } else { Err(()) }
 }
 
-/// Performs the actual work of ensuring a command passes. Generally assumes the command
-/// is syntactically valid.
-fn check_command(command: Command, cache: &mut Cache) -> Result<(), CkError> {
-    // FIXME: Be more granular about why, (e.g. syntax error, count not equal)
-    let result = match command.kind {
-        CommandKind::Has => {
-            match command.args.len() {
-                // `has <jsonpath>`: Check that `jsonpath` exists.
-                1 => {
-                    let val = cache.value();
-                    let results = select(val, &command.args[0]).unwrap();
-                    !results.is_empty()
-                }
-                // `has <jsonpath> <value>`: Check *any* item matched by `jsonpath` equals `value`.
-                2 => {
-                    let val = cache.value().clone();
-                    let results = select(&val, &command.args[0]).unwrap();
-                    let pat = string_to_value(&command.args[1], cache);
-                    let has = results.contains(&pat.as_ref());
-                    // Give better error for when `has` check fails.
-                    if !command.negated && !has {
-                        return Err(CkError::FailedCheck(
-                            format!(
-                                "{} matched to {:?} but didn't have {:?}",
-                                &command.args[0],
-                                results,
-                                pat.as_ref()
-                            ),
-                            command,
-                        ));
-                    } else {
-                        has
-                    }
-                }
-                _ => unreachable!(),
+/// Performs the actual work of ensuring a command passes.
+fn check_command(command: &Command, cache: &mut Cache) -> Result<(), String> {
+    let matches = cache.select(&command.path);
+    match &command.kind {
+        CommandKind::HasPath => {
+            if matches.is_empty() {
+                return Err("matched to no values".to_owned());
+            }
+        }
+        CommandKind::HasNotPath => {
+            if !matches.is_empty() {
+                return Err(format!("matched to {matches:?}, but wanted no matches"));
+            }
+        }
+        CommandKind::HasValue { value } => {
+            let want_value = string_to_value(value, cache);
+            if !matches.contains(&want_value.as_ref()) {
+                return Err(format!("matched to {matches:?}, which didn't contain {want_value:?}"));
+            }
+        }
+        CommandKind::Is { value } => {
+            let want_value = string_to_value(value, cache);
+            let matched = get_one(&matches)?;
+            if matched != want_value.as_ref() {
+                return Err(format!("matched to {matched:?} but want {want_value:?}"));
+            }
+        }
+        CommandKind::IsNot { value } => {
+            let wantnt_value = string_to_value(value, cache);
+            let matched = get_one(&matches)?;
+            if matched == wantnt_value.as_ref() {
+                return Err(format!("got value {wantnt_value:?}, but want anything else"));
             }
         }
-        // `ismany <path> <jsonpath> <value...>`
-        CommandKind::IsMany => {
-            assert!(!command.negated, "`ismany` may not be negated");
-            let (query, values) = if let [query, values @ ..] = &command.args[..] {
-                (query, values)
-            } else {
-                unreachable!("Checked in CommandKind::validate")
-            };
-            let val = cache.value();
-            let got_values = select(val, &query).unwrap();
 
+        CommandKind::IsMany { values } => {
             // Serde json doesn't implement Ord or Hash for Value, so we must
             // use a Vec here. While in theory that makes setwize equality
             // O(n^2), in practice n will never be large enough to matter.
             let expected_values =
                 values.iter().map(|v| string_to_value(v, cache)).collect::<Vec<_>>();
-            if expected_values.len() != got_values.len() {
-                return Err(CkError::FailedCheck(
-                    format!(
-                        "Expected {} values, but `{}` matched to {} values ({:?})",
-                        expected_values.len(),
-                        query,
-                        got_values.len(),
-                        got_values
-                    ),
-                    command,
+            if expected_values.len() != matches.len() {
+                return Err(format!(
+                    "Expected {} values, but matched to {} values ({:?})",
+                    expected_values.len(),
+                    matches.len(),
+                    matches
                 ));
             };
-            for got_value in got_values {
+            for got_value in matches {
                 if !expected_values.iter().any(|exp| &**exp == got_value) {
-                    return Err(CkError::FailedCheck(
-                        format!("`{}` has match {:?}, which was not expected", query, got_value),
-                        command,
-                    ));
+                    return Err(format!("has match {got_value:?}, which was not expected",));
                 }
             }
-            true
-        }
-        // `count <jsonpath> <count>`: Check that `jsonpath` matches exactly `count` times.
-        CommandKind::Count => {
-            assert_eq!(command.args.len(), 2);
-            let expected: usize = command.args[1].parse().unwrap();
-            let val = cache.value();
-            let results = select(val, &command.args[0]).unwrap();
-            let eq = results.len() == expected;
-            if !command.negated && !eq {
-                return Err(CkError::FailedCheck(
-                    format!(
-                        "`{}` matched to `{:?}` with length {}, but expected length {}",
-                        &command.args[0],
-                        results,
-                        results.len(),
-                        expected
-                    ),
-                    command,
-                ));
-            } else {
-                eq
-            }
         }
-        // `has <jsonpath> <value>`: Check` *exactly one* item matched by `jsonpath`, and it equals `value`.
-        CommandKind::Is => {
-            assert_eq!(command.args.len(), 2);
-            let val = cache.value().clone();
-            let results = select(&val, &command.args[0]).unwrap();
-            let pat = string_to_value(&command.args[1], cache);
-            let is = results.len() == 1 && results[0] == pat.as_ref();
-            if !command.negated && !is {
-                return Err(CkError::FailedCheck(
-                    format!(
-                        "{} matched to {:?}, but expected {:?}",
-                        &command.args[0],
-                        results,
-                        pat.as_ref()
-                    ),
-                    command,
+        CommandKind::CountIs { expected } => {
+            if *expected != matches.len() {
+                return Err(format!(
+                    "matched to `{matches:?}` with length {}, but expected length {expected}",
+                    matches.len(),
                 ));
-            } else {
-                is
             }
         }
-        // `set <name> = <jsonpath>`
-        CommandKind::Set => {
-            assert!(!command.negated, "`set` may not be negated");
-            assert_eq!(command.args.len(), 3);
-            assert_eq!(command.args[1], "=", "Expected an `=`");
-            let val = cache.value().clone();
-            let results = select(&val, &command.args[2]).unwrap();
-            assert_eq!(
-                results.len(),
-                1,
-                "Expected 1 match for `{}` (because of `set`): matched to {:?}",
-                command.args[2],
-                results
-            );
-            match results.len() {
-                0 => false,
-                1 => {
-                    let r = cache.variables.insert(command.args[0].clone(), results[0].clone());
-                    assert!(r.is_none(), "Name collision: {} is duplicated", command.args[0]);
-                    true
-                }
-                _ => {
-                    panic!(
-                        "Got multiple results in `set` for `{}`: {:?}",
-                        &command.args[2], results,
-                    );
-                }
-            }
+        CommandKind::Set { variable } => {
+            let value = get_one(&matches)?;
+            let r = cache.variables.insert(variable.to_owned(), value.clone());
+            assert!(r.is_none(), "name collision: {variable:?} is duplicated");
         }
-    };
+    }
 
-    if result == command.negated {
-        if command.negated {
-            Err(CkError::FailedCheck(
-                format!("`!{} {}` matched when it shouldn't", command.kind, command.args.join(" ")),
-                command,
-            ))
-        } else {
-            // FIXME: In the future, try 'peeling back' each step, and see at what level the match failed
-            Err(CkError::FailedCheck(
-                format!(
-                    "`{} {}` didn't match when it should",
-                    command.kind,
-                    command.args.join(" ")
-                ),
-                command,
-            ))
-        }
-    } else {
-        Ok(())
+    Ok(())
+}
+
+fn get_one<'a>(matches: &[&'a Value]) -> Result<&'a Value, String> {
+    match matches {
+        [] => Err("matched to no values".to_owned()),
+        [matched] => Ok(matched),
+        _ => Err(format!("matched to multiple values {matches:?}, but want exactly 1")),
     }
 }
 
diff --git a/src/tools/miri/Cargo.lock b/src/tools/miri/Cargo.lock
index 05d1c2d1eb3..363b96fdff1 100644
--- a/src/tools/miri/Cargo.lock
+++ b/src/tools/miri/Cargo.lock
@@ -1,6 +1,6 @@
 # This file is automatically @generated by Cargo.
 # It is not intended for manual editing.
-version = 3
+version = 4
 
 [[package]]
 name = "addr2line"
@@ -151,12 +151,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
 
 [[package]]
-name = "cfg_aliases"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e"
-
-[[package]]
 name = "chrono"
 version = "0.4.38"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -287,16 +281,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "ctrlc"
-version = "3.4.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "672465ae37dc1bc6380a6547a8883d5dd397b0f1faaad4f265726cc7042a5345"
-dependencies = [
- "nix",
- "windows-sys 0.52.0",
-]
-
-[[package]]
 name = "directories"
 version = "5.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -420,16 +404,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
 
 [[package]]
-name = "jemalloc-sys"
-version = "0.5.4+5.3.0-patched"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac6c1946e1cea1788cbfde01c993b52a10e2da07f4bac608228d1bed20bfebf2"
-dependencies = [
- "cc",
- "libc",
-]
-
-[[package]]
 name = "lazy_static"
 version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -554,10 +528,8 @@ dependencies = [
  "chrono",
  "chrono-tz",
  "colored",
- "ctrlc",
  "directories",
  "getrandom",
- "jemalloc-sys",
  "libc",
  "libffi",
  "libloading",
@@ -567,23 +539,12 @@ dependencies = [
  "rustc_version",
  "smallvec",
  "tempfile",
+ "tikv-jemalloc-sys",
  "ui_test",
  "windows-sys 0.52.0",
 ]
 
 [[package]]
-name = "nix"
-version = "0.28.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4"
-dependencies = [
- "bitflags",
- "cfg-if",
- "cfg_aliases",
- "libc",
-]
-
-[[package]]
 name = "num-traits"
 version = "0.2.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1032,6 +993,16 @@ dependencies = [
 ]
 
 [[package]]
+name = "tikv-jemalloc-sys"
+version = "0.6.0+5.3.0-1-ge13ca993e8ccb9ba9847cc330696e02839f328f7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cd3c60906412afa9c2b5b5a48ca6a5abe5736aec9eb48ad05037a677e52e4e2d"
+dependencies = [
+ "cc",
+ "libc",
+]
+
+[[package]]
 name = "tracing"
 version = "0.1.40"
 source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/src/tools/miri/Cargo.toml b/src/tools/miri/Cargo.toml
index cb02914fd93..6e8e270985e 100644
--- a/src/tools/miri/Cargo.toml
+++ b/src/tools/miri/Cargo.toml
@@ -23,7 +23,6 @@ rand = "0.8"
 smallvec = { version = "1.7", features = ["drain_filter"] }
 aes = { version = "0.8.3", features = ["hazmat"] }
 measureme = "11"
-ctrlc = "3.2.5"
 chrono = { version = "0.4.38", default-features = false }
 chrono-tz = "0.10"
 directories = "5"
@@ -31,8 +30,8 @@ directories = "5"
 # Copied from `compiler/rustc/Cargo.toml`.
 # But only for some targets, it fails for others. Rustc configures this in its CI, but we can't
 # easily use that since we support of-tree builds.
-[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies.jemalloc-sys]
-version = "0.5.0"
+[target.'cfg(any(target_os = "linux", target_os = "macos"))'.dependencies.tikv-jemalloc-sys]
+version = "0.6.0"
 features = ['unprefixed_malloc_on_supported_platforms']
 
 [target.'cfg(unix)'.dependencies]
diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md
index 9a683ae68fd..4e30dea18ff 100644
--- a/src/tools/miri/README.md
+++ b/src/tools/miri/README.md
@@ -68,9 +68,10 @@ Further caveats that Miri users should be aware of:
   not support networking. System API support varies between targets; if you run
   on Windows it is a good idea to use `--target x86_64-unknown-linux-gnu` to get
   better support.
-* Weak memory emulation may [produce weak behaviors](https://github.com/rust-lang/miri/issues/2301)
-  when `SeqCst` fences are used that are not actually permitted by the Rust memory model, and it
-  cannot produce all behaviors possibly observable on real hardware.
+* Weak memory emulation is not complete: there are legal behaviors that Miri will never produce.
+  However, Miri produces many behaviors that are hard to observe on real hardware, so it can help
+  quite a bit in finding weak memory concurrency bugs. To be really sure about complicated atomic
+  code, use specialized tools such as [loom](https://github.com/tokio-rs/loom).
 
 Moreover, Miri fundamentally cannot ensure that your code is *sound*. [Soundness] is the property of
 never causing undefined behavior when invoked from arbitrary safe code, even in combination with
diff --git a/src/tools/miri/bench-cargo-miri/string-replace/Cargo.lock b/src/tools/miri/bench-cargo-miri/string-replace/Cargo.lock
new file mode 100644
index 00000000000..443115c1265
--- /dev/null
+++ b/src/tools/miri/bench-cargo-miri/string-replace/Cargo.lock
@@ -0,0 +1,7 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 3
+
+[[package]]
+name = "string-replace"
+version = "0.1.0"
diff --git a/src/tools/miri/bench-cargo-miri/string-replace/Cargo.toml b/src/tools/miri/bench-cargo-miri/string-replace/Cargo.toml
new file mode 100644
index 00000000000..f0785cd693e
--- /dev/null
+++ b/src/tools/miri/bench-cargo-miri/string-replace/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "string-replace"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/miri/bench-cargo-miri/string-replace/data.json b/src/tools/miri/bench-cargo-miri/string-replace/data.json
new file mode 100644
index 00000000000..7e074cd6954
--- /dev/null
+++ b/src/tools/miri/bench-cargo-miri/string-replace/data.json
@@ -0,0 +1 @@
+[{"_id":"6724e6fc58417687afba2b85","index":0,"guid":"5c1bd108-2ee2-40bd-bce8-895c206409df","isActive":true,"balance":"$2,927.88","picture":"http://placehold.it/32x32","age":40,"eyeColor":"green","name":"Wynn Bradshaw","gender":"male","company":"ROTODYNE","email":"wynnbradshaw@rotodyne.com","phone":"+1 (904) 559-3130","address":"287 Bergen Avenue, Sperryville, Alaska, 5392","about":"Adipisicing fugiat aute adipisicing qui esse cillum. Lorem consequat consectetur voluptate id pariatur nostrud incididunt aliquip incididunt laboris aliqua. Magna nulla adipisicing cupidatat ea velit aliquip magna duis duis sunt ipsum. Cillum labore mollit fugiat tempor dolor sit.\r\n","registered":"2017-01-26T01:28:10 -01:00","latitude":46.089504,"longitude":51.763723,"greeting":"Hello, Wynn Bradshaw! You have 6 unread messages.","favoriteFruit":"banana"},{"_id":"6724e6fce8619d86c0389ccf","index":1,"guid":"ced7fbb7-3b1a-419b-9fc7-d47582bbb3ea","isActive":true,"balance":"$1,856.38","picture":"http://placehold.it/32x32","age":21,"eyeColor":"brown","name":"Olsen Larsen","gender":"male","company":"VERBUS","email":"olsenlarsen@verbus.com","phone":"+1 (936) 480-3749","address":"370 Losee Terrace, Churchill, Maine, 4040","about":"Consequat Lorem in laboris fugiat veniam tempor eiusmod eu incididunt enim do et qui. Sit commodo eu excepteur cillum ex tempor commodo ex ex laboris esse. Aute aute nulla dolore dolor do. Irure esse proident nostrud non. Incididunt velit reprehenderit incididunt laboris do. Consequat nulla est id ex veniam tempor. Sit Lorem magna cillum aliquip irure quis sit minim anim.\r\n","registered":"2016-07-12T02:08:39 -02:00","latitude":-12.843628,"longitude":-124.143829,"greeting":"Hello, Olsen Larsen! You have 1 unread messages.","favoriteFruit":"apple"},{"_id":"6724e6fc01b471965ea560cf","index":2,"guid":"21fde9a3-13ba-46be-baed-fb503f668b9e","isActive":false,"balance":"$2,025.88","picture":"http://placehold.it/32x32","age":29,"eyeColor":"green","name":"Ramirez Kinney","gender":"male","company":"QUAREX","email":"ramirezkinney@quarex.com","phone":"+1 (852) 447-2930","address":"986 Cornelia Street, Oberlin, Texas, 362","about":"Minim ea proident quis eiusmod aliquip duis excepteur velit minim aute cupidatat. Esse qui ex aliquip laborum id reprehenderit. Anim dolore commodo deserunt laborum nulla duis. Sint quis anim mollit fugiat sit incididunt reprehenderit occaecat aliqua dolor. Ullamco ipsum eiusmod incididunt proident qui exercitation adipisicing voluptate elit aliquip. Tempor duis aute incididunt adipisicing.\r\n","registered":"2016-02-23T05:34:14 -01:00","latitude":-56.21645,"longitude":44.048129,"greeting":"Hello, Ramirez Kinney! You have 9 unread messages.","favoriteFruit":"banana"},{"_id":"6724e6fc3ea8e4182b9e170f","index":3,"guid":"46b20637-eecc-40db-87d7-03da9bcd1cea","isActive":true,"balance":"$3,399.31","picture":"http://placehold.it/32x32","age":39,"eyeColor":"brown","name":"Hansen Kaufman","gender":"male","company":"EVENTAGE","email":"hansenkaufman@eventage.com","phone":"+1 (827) 483-2303","address":"916 Brighton Court, Sunbury, New Mexico, 3804","about":"Nisi in voluptate aute ullamco ipsum proident fugiat veniam anim reprehenderit. In ad irure dolor labore culpa incididunt veniam mollit Lorem deserunt cupidatat incididunt. Aliquip aliquip proident ut culpa.\r\n","registered":"2023-10-18T07:03:48 -02:00","latitude":-40.239135,"longitude":49.802049,"greeting":"Hello, Hansen Kaufman! You have 10 unread messages.","favoriteFruit":"apple"},{"_id":"6724e6fc721f83a10cf2aa37","index":4,"guid":"3d23743b-1e82-474e-8f7a-855fa46170d1","isActive":false,"balance":"$1,967.87","picture":"http://placehold.it/32x32","age":35,"eyeColor":"green","name":"Imelda Stephens","gender":"female","company":"OHMNET","email":"imeldastephens@ohmnet.com","phone":"+1 (893) 523-2400","address":"391 Wilson Street, Glidden, Kansas, 7226","about":"Officia sunt magna adipisicing id exercitation deserunt deserunt aliquip excepteur Lorem enim fugiat. Nulla culpa ut cupidatat excepteur do deserunt labore id eu laboris ullamco adipisicing ad. Et non nisi adipisicing minim aliquip ea ut qui adipisicing do laboris ex dolore duis.\r\n","registered":"2020-10-20T07:03:38 -02:00","latitude":0.348698,"longitude":-157.961956,"greeting":"Hello, Imelda Stephens! You have 2 unread messages.","favoriteFruit":"strawberry"},{"_id":"6724e6fc7ad7274b9f4c406c","index":5,"guid":"626292b1-ae84-4887-9e29-78e548cd24e6","isActive":true,"balance":"$1,577.44","picture":"http://placehold.it/32x32","age":40,"eyeColor":"brown","name":"Lynne Jarvis","gender":"female","company":"CORECOM","email":"lynnejarvis@corecom.com","phone":"+1 (899) 556-3876","address":"465 National Drive, Davenport, Palau, 9786","about":"Aliquip elit dolore sint quis do laboris exercitation elit aliqua eiusmod. Excepteur ad aliqua eiusmod incididunt tempor laboris officia consectetur sit. Cupidatat voluptate deserunt ut consectetur qui laborum duis elit incididunt occaecat laborum. Mollit aute velit officia amet aute minim fugiat sit laborum Lorem deserunt in. Exercitation eu sunt nulla adipisicing quis ea aute est. Lorem ea cillum ad labore quis minim et est laboris deserunt proident. Amet ut tempor laborum occaecat exercitation ullamco laborum adipisicing fugiat ea voluptate quis fugiat.\r\n","registered":"2018-11-03T03:53:15 -01:00","latitude":89.827087,"longitude":-136.882799,"greeting":"Hello, Lynne Jarvis! You have 3 unread messages.","favoriteFruit":"strawberry"},{"_id":"6724e6fcef1a1db2cf170762","index":6,"guid":"b8777c06-b90f-49a4-8737-96712fc504a3","isActive":false,"balance":"$2,285.03","picture":"http://placehold.it/32x32","age":37,"eyeColor":"green","name":"Price Bolton","gender":"male","company":"IMANT","email":"pricebolton@imant.com","phone":"+1 (825) 424-2873","address":"237 Aberdeen Street, Sattley, Montana, 2918","about":"Non cillum irure fugiat consequat ad ex. Magna magna tempor excepteur irure quis. Duis in laboris ipsum adipisicing culpa magna reprehenderit nisi incididunt est veniam quis. Labore culpa ut culpa veniam est est consectetur ipsum ex esse.\r\n","registered":"2014-04-26T01:20:19 -02:00","latitude":70.349258,"longitude":126.810102,"greeting":"Hello, Price Bolton! You have 10 unread messages.","favoriteFruit":"banana"},{"_id":"6724e6fc8bcb952208c159f9","index":7,"guid":"a4e6c6c8-3fe3-42de-ae28-79c16956d309","isActive":false,"balance":"$1,298.07","picture":"http://placehold.it/32x32","age":28,"eyeColor":"blue","name":"Gretchen Wynn","gender":"female","company":"TERASCAPE","email":"gretchenwynn@terascape.com","phone":"+1 (882) 447-2895","address":"973 Suydam Place, Shindler, Nebraska, 8094","about":"Anim mollit labore magna proident ipsum culpa enim deserunt dolore sunt veniam fugiat. Ad fugiat cupidatat nisi commodo dolore duis commodo nostrud est. Enim proident ullamco non adipisicing magna consequat mollit ad reprehenderit laboris. Ex quis duis anim id non commodo amet sunt est magna officia.\r\n","registered":"2021-08-13T08:51:32 -02:00","latitude":14.551848,"longitude":-27.142242,"greeting":"Hello, Gretchen Wynn! You have 7 unread messages.","favoriteFruit":"apple"},{"_id":"6724e6fcc8243c2dfa47f5d4","index":8,"guid":"27df20d5-c1d8-419b-ad38-bdd1e6094775","isActive":true,"balance":"$3,005.40","picture":"http://placehold.it/32x32","age":33,"eyeColor":"blue","name":"Chen Travis","gender":"male","company":"MEMORA","email":"chentravis@memora.com","phone":"+1 (980) 500-2406","address":"182 Dahlgreen Place, Baker, South Carolina, 9817","about":"Ad nisi consequat aliquip eiusmod aute pariatur est sint magna. Ad magna anim esse qui Lorem nulla veniam dolore eiusmod. Cillum consequat sit aliqua est proident exercitation eiusmod irure. Minim eu laboris ad incididunt enim sunt. Sunt in excepteur aute non tempor irure mollit laboris. Eu et duis ullamco dolor sint occaecat officia culpa ipsum anim anim eu veniam aliquip. Exercitation ipsum dolor sint cillum duis incididunt minim quis irure enim reprehenderit do do incididunt.\r\n","registered":"2019-09-01T02:57:37 -02:00","latitude":25.442301,"longitude":48.381036,"greeting":"Hello, Chen Travis! You have 1 unread messages.","favoriteFruit":"banana"}]
\ No newline at end of file
diff --git a/src/tools/miri/bench-cargo-miri/string-replace/src/main.rs b/src/tools/miri/bench-cargo-miri/string-replace/src/main.rs
new file mode 100644
index 00000000000..73bf4a850e4
--- /dev/null
+++ b/src/tools/miri/bench-cargo-miri/string-replace/src/main.rs
@@ -0,0 +1,7 @@
+const TCB_INFO_JSON: &str = include_str!("../data.json");
+
+fn main() {
+    let tcb_json = TCB_INFO_JSON;
+    let bad_tcb_json = tcb_json.replace("female", "male");
+    std::hint::black_box(bad_tcb_json);
+}
diff --git a/src/tools/miri/cargo-miri/src/phases.rs b/src/tools/miri/cargo-miri/src/phases.rs
index f1f76fd338c..7ca4f414c20 100644
--- a/src/tools/miri/cargo-miri/src/phases.rs
+++ b/src/tools/miri/cargo-miri/src/phases.rs
@@ -348,14 +348,17 @@ pub fn phase_rustc(mut args: impl Iterator<Item = String>, phase: RustcPhase) {
             // Create a stub .d file to stop Cargo from "rebuilding" the crate:
             // https://github.com/rust-lang/miri/issues/1724#issuecomment-787115693
             // As we store a JSON file instead of building the crate here, an empty file is fine.
-            let dep_info_name = format!(
-                "{}/{}{}.d",
-                get_arg_flag_value("--out-dir").unwrap(),
+            let mut dep_info_name = PathBuf::from(get_arg_flag_value("--out-dir").unwrap());
+            dep_info_name.push(format!(
+                "{}{}.d",
                 get_arg_flag_value("--crate-name").unwrap(),
                 get_arg_flag_value("extra-filename").unwrap_or_default(),
-            );
+            ));
             if verbose > 0 {
-                eprintln!("[cargo-miri rustc] writing stub dep-info to `{dep_info_name}`");
+                eprintln!(
+                    "[cargo-miri rustc] writing stub dep-info to `{}`",
+                    dep_info_name.display()
+                );
             }
             File::create(dep_info_name).expect("failed to create fake .d file");
         }
diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh
index 0356d7ecf10..8e6e31bee43 100755
--- a/src/tools/miri/ci/ci.sh
+++ b/src/tools/miri/ci/ci.sh
@@ -152,9 +152,9 @@ case $HOST_TARGET in
     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=x86_64-unknown-freebsd run_tests_minimal $BASIC $UNIX time hashmap random threadname pthread fs libc-pipe
     TEST_TARGET=i686-unknown-freebsd   run_tests_minimal $BASIC $UNIX time hashmap random threadname pthread fs libc-pipe
-    TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $BASIC $UNIX time hashmap random thread sync available-parallelism tls libc-pipe
-    TEST_TARGET=x86_64-pc-solaris      run_tests_minimal $BASIC $UNIX time hashmap random thread sync available-parallelism tls libc-pipe
-    TEST_TARGET=aarch64-linux-android  run_tests_minimal $BASIC $UNIX time hashmap random sync threadname pthread
+    TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $BASIC $UNIX time hashmap random thread sync available-parallelism tls libc-pipe fs
+    TEST_TARGET=x86_64-pc-solaris      run_tests_minimal $BASIC $UNIX time hashmap random thread sync available-parallelism tls libc-pipe fs
+    TEST_TARGET=aarch64-linux-android  run_tests_minimal $BASIC $UNIX time hashmap random sync threadname pthread epoll eventfd
     TEST_TARGET=wasm32-wasip2          run_tests_minimal $BASIC wasm
     TEST_TARGET=wasm32-unknown-unknown run_tests_minimal no_std empty_main wasm # this target doesn't really have std
     TEST_TARGET=thumbv7em-none-eabihf  run_tests_minimal no_std
diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version
index effed0cd180..57d0b27dfd3 100644
--- a/src/tools/miri/rust-version
+++ b/src/tools/miri/rust-version
@@ -1 +1 @@
-2d0ea7956c45de6e421fd579e2ded27be405dec6
+728f2daab42ba8f1b3d5caab62495798d1eabfa1
diff --git a/src/tools/miri/src/alloc_addresses/mod.rs b/src/tools/miri/src/alloc_addresses/mod.rs
index fe7d8db245b..f7295fd7d8a 100644
--- a/src/tools/miri/src/alloc_addresses/mod.rs
+++ b/src/tools/miri/src/alloc_addresses/mod.rs
@@ -286,9 +286,9 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
 impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
 pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
-    fn expose_ptr(&mut self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> {
-        let this = self.eval_context_mut();
-        let global_state = this.machine.alloc_addresses.get_mut();
+    fn expose_ptr(&self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> {
+        let this = self.eval_context_ref();
+        let mut global_state = this.machine.alloc_addresses.borrow_mut();
         // In strict mode, we don't need this, so we can save some cycles by not tracking it.
         if global_state.provenance_mode == ProvenanceMode::Strict {
             return interp_ok(());
@@ -299,8 +299,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             return interp_ok(());
         }
         trace!("Exposing allocation id {alloc_id:?}");
-        let global_state = this.machine.alloc_addresses.get_mut();
         global_state.exposed.insert(alloc_id);
+        // Release the global state before we call `expose_tag`, which may call `get_alloc_info_extra`,
+        // which may need access to the global state.
+        drop(global_state);
         if this.machine.borrow_tracker.is_some() {
             this.expose_tag(alloc_id, tag)?;
         }
diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs
index c61c62c73da..5248c9d186b 100644
--- a/src/tools/miri/src/bin/miri.rs
+++ b/src/tools/miri/src/bin/miri.rs
@@ -3,6 +3,7 @@
     clippy::manual_range_contains,
     clippy::useless_format,
     clippy::field_reassign_with_default,
+    clippy::needless_lifetimes,
     rustc::diagnostic_outside_of_impl,
     rustc::untranslatable_diagnostic
 )]
@@ -289,7 +290,8 @@ fn run_compiler(
     let exit_code = rustc_driver::catch_with_exit_code(move || {
         rustc_driver::RunCompiler::new(&args, callbacks)
             .set_using_internal_features(using_internal_features)
-            .run()
+            .run();
+        Ok(())
     });
     std::process::exit(exit_code)
 }
@@ -316,6 +318,8 @@ fn jemalloc_magic() {
     // See there for further comments.
     use std::os::raw::{c_int, c_void};
 
+    use tikv_jemalloc_sys as jemalloc_sys;
+
     #[used]
     static _F1: unsafe extern "C" fn(usize, usize) -> *mut c_void = jemalloc_sys::calloc;
     #[used]
diff --git a/src/tools/miri/src/borrow_tracker/mod.rs b/src/tools/miri/src/borrow_tracker/mod.rs
index 4883613dea5..9808102f4ba 100644
--- a/src/tools/miri/src/borrow_tracker/mod.rs
+++ b/src/tools/miri/src/borrow_tracker/mod.rs
@@ -302,8 +302,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         }
     }
 
-    fn expose_tag(&mut self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> {
-        let this = self.eval_context_mut();
+    fn expose_tag(&self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> {
+        let this = self.eval_context_ref();
         let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method;
         match method {
             BorrowTrackerMethod::StackedBorrows => this.sb_expose_tag(alloc_id, tag),
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 a855603eeb3..ea75131078e 100644
--- a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs
+++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs
@@ -1011,8 +1011,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     }
 
     /// Mark the given tag as exposed. It was found on a pointer with the given AllocId.
-    fn sb_expose_tag(&mut self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> {
-        let this = self.eval_context_mut();
+    fn sb_expose_tag(&self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> {
+        let this = self.eval_context_ref();
 
         // Function pointers and dead objects don't have an alloc_extra so we ignore them.
         // This is okay because accessing them is UB anyway, no need for any Stacked Borrows checks.
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
new file mode 100644
index 00000000000..928b3e6baef
--- /dev/null
+++ b/src/tools/miri/src/borrow_tracker/tree_borrows/foreign_access_skipping.rs
@@ -0,0 +1,108 @@
+use super::AccessKind;
+use super::tree::AccessRelatedness;
+
+/// To speed up tree traversals, we want to skip traversing subtrees when we know the traversal will have no effect.
+/// This is often the case for foreign accesses, since usually foreign accesses happen several times in a row, but also
+/// foreign accesses are idempotent. In particular, see tests `foreign_read_is_noop_after_foreign_write` and `all_transitions_idempotent`.
+/// Thus, for each node we keep track of the "strongest idempotent foreign access" (SIFA), i.e. which foreign access can be skipped.
+/// Note that for correctness, it is not required that this is the strongest access, just any access it is idempotent under. In particular, setting
+/// it to `None` is always correct, but the point of this optimization is to have it be as strong as possible so that more accesses can be skipped.
+/// This enum represents the kinds of values we store:
+/// - `None` means that the node (and its subtrees) are not (guaranteed to be) idempotent under any foreign access.
+/// - `Read` means that the node (and its subtrees) are idempotent under foreign reads, but not (yet / necessarily) under foreign writes.
+/// - `Write` means that the node (and its subtrees) are idempotent under foreign writes. This also implies that it is idempotent under foreign
+///   reads, since reads are stronger than writes (see test `foreign_read_is_noop_after_foreign_write`). In other words, this node can be skipped
+///   for all foreign accesses.
+///
+/// Since a traversal does not just visit a node, but instead the entire subtree, the SIFA field for a given node indicates that the access to
+/// *the entire subtree* rooted at that node can be skipped. In order for this to work, we maintain the global invariant that at
+/// each location, the SIFA at each child must be stronger than that at the parent. For normal reads and writes, this is easily accomplished by
+/// tracking each foreign access as it occurs, so that then the next access can be skipped. This also obviously maintains the invariant, because
+/// if a node undergoes a foreign access, then all its children also see this as a foreign access. However, the invariant is broken during retags,
+/// because retags act across the entire allocation, but only emit a read event across a specific range. This means that for all nodes outside that
+/// range, the invariant is potentially broken, since a new child with a weaker SIFA is inserted. Thus, during retags, special care is taken to
+/// "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.
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Default)]
+pub enum IdempotentForeignAccess {
+    #[default]
+    None,
+    Read,
+    Write,
+}
+
+impl IdempotentForeignAccess {
+    /// Returns true if a node where the strongest idempotent foreign access is `self`
+    /// can skip the access `happening_next`. Note that if this returns
+    /// `true`, then the entire subtree will be skipped.
+    pub fn can_skip_foreign_access(self, happening_next: IdempotentForeignAccess) -> bool {
+        debug_assert!(happening_next.is_foreign());
+        // This ordering is correct. Intuitively, if the last access here was
+        // a foreign write, everything can be skipped, since after a foreign write,
+        // all further foreign accesses are idempotent
+        happening_next <= self
+    }
+
+    /// Updates `self` to account for a foreign access.
+    pub fn record_new(&mut self, just_happened: IdempotentForeignAccess) {
+        if just_happened.is_local() {
+            // If the access is local, reset it.
+            *self = IdempotentForeignAccess::None;
+        } else {
+            // Otherwise, keep it or stengthen it.
+            *self = just_happened.max(*self);
+        }
+    }
+
+    /// Returns true if this access is local.
+    pub fn is_local(self) -> bool {
+        matches!(self, IdempotentForeignAccess::None)
+    }
+
+    /// Returns true if this access is foreign, i.e. not local.
+    pub fn is_foreign(self) -> bool {
+        !self.is_local()
+    }
+
+    /// Constructs a foreign access from an `AccessKind`
+    pub fn from_foreign(acc: AccessKind) -> IdempotentForeignAccess {
+        match acc {
+            AccessKind::Read => Self::Read,
+            AccessKind::Write => Self::Write,
+        }
+    }
+
+    /// Usually, tree traversals have an `AccessKind` and an `AccessRelatedness`.
+    /// This methods converts these into the corresponding `IdempotentForeignAccess`, to be used
+    /// to e.g. invoke `can_skip_foreign_access`.
+    pub fn from_acc_and_rel(acc: AccessKind, rel: AccessRelatedness) -> IdempotentForeignAccess {
+        if rel.is_foreign() { Self::from_foreign(acc) } else { Self::None }
+    }
+
+    /// During retags, the SIFA needs to be weakened to account for children with weaker SIFAs being inserted.
+    /// Thus, this method is called from the bottom up on each parent, until it returns false, which means the
+    /// "children have stronger SIFAs" invariant is restored.
+    pub fn ensure_no_stronger_than(&mut self, strongest_allowed: IdempotentForeignAccess) -> bool {
+        if *self > strongest_allowed {
+            *self = strongest_allowed;
+            true
+        } else {
+            false
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::IdempotentForeignAccess;
+
+    #[test]
+    fn test_order() {
+        // The internal logic relies on this order.
+        // Do not change.
+        assert!(IdempotentForeignAccess::None < IdempotentForeignAccess::Read);
+        assert!(IdempotentForeignAccess::Read < IdempotentForeignAccess::Write);
+    }
+}
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 8469744bbc4..f39a606513d 100644
--- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs
+++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs
@@ -9,6 +9,7 @@ use crate::concurrency::data_race::NaReadType;
 use crate::*;
 
 pub mod diagnostics;
+mod foreign_access_skipping;
 mod perms;
 mod tree;
 mod unimap;
@@ -296,7 +297,14 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             this.machine.current_span(),
         )?;
         // Record the parent-child pair in the tree.
-        tree_borrows.new_child(orig_tag, new_tag, new_perm.initial_state, range, span)?;
+        tree_borrows.new_child(
+            orig_tag,
+            new_tag,
+            new_perm.initial_state,
+            range,
+            span,
+            new_perm.protector.is_some(),
+        )?;
         drop(tree_borrows);
 
         // Also inform the data race model (but only if any bytes are actually affected).
@@ -532,8 +540,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     }
 
     /// Mark the given tag as exposed. It was found on a pointer with the given AllocId.
-    fn tb_expose_tag(&mut self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> {
-        let this = self.eval_context_mut();
+    fn tb_expose_tag(&self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> {
+        let this = self.eval_context_ref();
 
         // Function pointers and dead objects don't have an alloc_extra so we ignore them.
         // This is okay because accessing them is UB anyway, no need for any Tree Borrows checks.
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 28f9dec7bb9..6e157d3fcd3 100644
--- a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs
+++ b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs
@@ -48,6 +48,7 @@ enum PermissionPriv {
     Disabled,
 }
 use self::PermissionPriv::*;
+use super::foreign_access_skipping::IdempotentForeignAccess;
 
 impl PartialOrd for PermissionPriv {
     /// PermissionPriv is ordered by the reflexive transitive closure of
@@ -87,6 +88,28 @@ impl PermissionPriv {
     fn compatible_with_protector(&self) -> bool {
         !matches!(self, ReservedIM)
     }
+
+    /// See `foreign_access_skipping.rs`. Computes the SIFA of a permission.
+    fn strongest_idempotent_foreign_access(&self, prot: bool) -> IdempotentForeignAccess {
+        match self {
+            // A protected non-conflicted Reserved will become conflicted under a foreign read,
+            // and is hence not idempotent under it.
+            ReservedFrz { conflicted } if prot && !conflicted => IdempotentForeignAccess::None,
+            // Otherwise, foreign reads do not affect Reserved
+            ReservedFrz { .. } => IdempotentForeignAccess::Read,
+            // 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,
+            // Frozen survives foreign reads, but not writes.
+            Frozen => IdempotentForeignAccess::Read,
+            // Disabled survives foreign reads and writes. It survives them
+            // even if protected, because a protected `Disabled` is not initialized
+            // and does therefore not trigger UB.
+            Disabled => IdempotentForeignAccess::Write,
+        }
+    }
 }
 
 /// This module controls how each permission individually reacts to an access.
@@ -305,6 +328,13 @@ impl Permission {
             (Disabled, _) => false,
         }
     }
+
+    /// Returns the strongest foreign action this node survives (without change),
+    /// where `prot` indicates if it is protected.
+    /// See `foreign_access_skipping`
+    pub fn strongest_idempotent_foreign_access(&self, prot: bool) -> IdempotentForeignAccess {
+        self.inner.strongest_idempotent_foreign_access(prot)
+    }
 }
 
 impl PermTransition {
@@ -575,7 +605,7 @@ mod propagation_optimization_checks {
     impl Exhaustive for AccessRelatedness {
         fn exhaustive() -> Box<dyn Iterator<Item = Self>> {
             use AccessRelatedness::*;
-            Box::new(vec![This, StrictChildAccess, AncestorAccess, DistantAccess].into_iter())
+            Box::new(vec![This, StrictChildAccess, AncestorAccess, CousinAccess].into_iter())
         }
     }
 
@@ -635,6 +665,35 @@ mod propagation_optimization_checks {
     }
 
     #[test]
+    #[rustfmt::skip]
+    fn permission_sifa_is_correct() {
+        // Tests that `strongest_idempotent_foreign_access` is correct. See `foreign_access_skipping.rs`.
+        for perm in PermissionPriv::exhaustive() {
+            // Assert that adding a protector makes it less idempotent.
+            if perm.compatible_with_protector() {
+                assert!(perm.strongest_idempotent_foreign_access(true) <= perm.strongest_idempotent_foreign_access(false));
+            }
+            for prot in bool::exhaustive() {
+                if prot {
+                    precondition!(perm.compatible_with_protector());
+                }
+                let access = perm.strongest_idempotent_foreign_access(prot);
+                // We now assert it is idempotent, and never causes UB.
+                // First, if the SIFA includes foreign reads, assert it is idempotent under foreign reads.
+                if access >= IdempotentForeignAccess::Read {
+                    // We use `CousinAccess` here. We could also use `AncestorAccess`, since `transition::perform_access` treats these the same.
+                    // The only place they are treated differently is in protector end accesses, but these are not handled here.
+                    assert_eq!(perm, transition::perform_access(AccessKind::Read, AccessRelatedness::CousinAccess, perm, prot).unwrap());
+                }
+                // Then, if the SIFA includes foreign writes, assert it is idempotent under foreign writes.
+                if access >= IdempotentForeignAccess::Write {
+                    assert_eq!(perm, transition::perform_access(AccessKind::Write, AccessRelatedness::CousinAccess, perm, prot).unwrap());
+                }
+            }
+        }
+    }
+
+    #[test]
     // Check that all transitions are consistent with the order on PermissionPriv,
     // i.e. Reserved -> Active -> Frozen -> Disabled
     fn permissionpriv_partialord_is_reachability() {
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 61b3338e6a2..3f524dc589f 100644
--- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs
+++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs
@@ -21,6 +21,7 @@ use crate::borrow_tracker::tree_borrows::Permission;
 use crate::borrow_tracker::tree_borrows::diagnostics::{
     self, NodeDebugInfo, TbError, TransitionError,
 };
+use crate::borrow_tracker::tree_borrows::foreign_access_skipping::IdempotentForeignAccess;
 use crate::borrow_tracker::tree_borrows::perms::PermTransition;
 use crate::borrow_tracker::tree_borrows::unimap::{UniEntry, UniIndex, UniKeyMap, UniValMap};
 use crate::borrow_tracker::{GlobalState, ProtectorKind};
@@ -45,28 +46,28 @@ pub(super) struct LocationState {
     initialized: bool,
     /// This pointer's current permission / future initial permission.
     permission: Permission,
-    /// Strongest foreign access whose effects have already been applied to
-    /// this node and all its children since the last child access.
-    /// This is `None` if the most recent access is a child access,
-    /// `Some(Write)` if at least one foreign write access has been applied
-    /// since the previous child access, and `Some(Read)` if at least one
-    /// foreign read and no foreign write have occurred since the last child access.
-    latest_foreign_access: Option<AccessKind>,
+    /// See `foreign_access_skipping.rs`.
+    /// Stores an idempotent foreign access for this location and its children.
+    /// For correctness, this must not be too strong, and the recorded idempotent foreign access
+    /// of all children must be at least as strong as this. For performance, it should be as strong as possible.
+    idempotent_foreign_access: IdempotentForeignAccess,
 }
 
 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`.
-    fn new_uninit(permission: Permission) -> Self {
+    /// `sifa` is the (strongest) idempotent foreign access, see `foreign_access_skipping.rs`
+    fn new_uninit(permission: Permission, sifa: IdempotentForeignAccess) -> Self {
         assert!(permission.is_initial() || permission.is_disabled());
-        Self { permission, initialized: false, latest_foreign_access: None }
+        Self { permission, initialized: false, idempotent_foreign_access: sifa }
     }
 
     /// Constructs a new initial state. It has not yet been subjected
     /// to any foreign access. However, it is already marked as having been accessed.
-    fn new_init(permission: Permission) -> Self {
-        Self { permission, initialized: true, latest_foreign_access: None }
+    /// `sifa` is the (strongest) idempotent foreign access, see `foreign_access_skipping.rs`
+    fn new_init(permission: Permission, sifa: IdempotentForeignAccess) -> Self {
+        Self { permission, initialized: true, idempotent_foreign_access: sifa }
     }
 
     /// Check if the location has been initialized, i.e. if it has
@@ -143,52 +144,21 @@ impl LocationState {
         }
     }
 
-    // Helper to optimize the tree traversal.
-    // The optimization here consists of observing thanks to the tests
-    // `foreign_read_is_noop_after_foreign_write` and `all_transitions_idempotent`,
-    // that there are actually just three possible sequences of events that can occur
-    // in between two child accesses that produce different results.
-    //
-    // Indeed,
-    // - applying any number of foreign read accesses is the same as applying
-    //   exactly one foreign read,
-    // - applying any number of foreign read or write accesses is the same
-    //   as applying exactly one foreign write.
-    // therefore the three sequences of events that can produce different
-    // outcomes are
-    // - an empty sequence (`self.latest_foreign_access = None`)
-    // - a nonempty read-only sequence (`self.latest_foreign_access = Some(Read)`)
-    // - a nonempty sequence with at least one write (`self.latest_foreign_access = Some(Write)`)
-    //
-    // This function not only determines if skipping the propagation right now
-    // is possible, it also updates the internal state to keep track of whether
-    // the propagation can be skipped next time.
-    // It is a performance loss not to call this function when a foreign access occurs.
-    // FIXME: This optimization is wrong, and is currently disabled (by ignoring the
-    // result returned here). Since we presumably want an optimization like this,
-    // we should add it back. See #3864 for more information.
+    /// Tree traversal optimizations. See `foreign_access_skipping.rs`.
+    /// This checks if such a foreign access can be skipped.
     fn skip_if_known_noop(
         &self,
         access_kind: AccessKind,
         rel_pos: AccessRelatedness,
     ) -> ContinueTraversal {
         if rel_pos.is_foreign() {
-            let new_access_noop = match (self.latest_foreign_access, access_kind) {
-                // Previously applied transition makes the new one a guaranteed
-                // noop in the two following cases:
-                // (1) justified by `foreign_read_is_noop_after_foreign_write`
-                (Some(AccessKind::Write), AccessKind::Read) => true,
-                // (2) justified by `all_transitions_idempotent`
-                (Some(old), new) if old == new => true,
-                // In all other cases there has been a recent enough
-                // child access that the effects of the new foreign access
-                // need to be applied to this subtree.
-                _ => false,
-            };
+            let happening_now = IdempotentForeignAccess::from_foreign(access_kind);
+            let new_access_noop =
+                self.idempotent_foreign_access.can_skip_foreign_access(happening_now);
             if new_access_noop {
-                // Abort traversal if the new transition is indeed guaranteed
+                // Abort traversal if the new access is indeed guaranteed
                 // to be noop.
-                // No need to update `self.latest_foreign_access`,
+                // No need to update `self.idempotent_foreign_access`,
                 // the type of the current streak among nonempty read-only
                 // or nonempty with at least one write has not changed.
                 ContinueTraversal::SkipSelfAndChildren
@@ -207,20 +177,17 @@ impl LocationState {
     }
 
     /// Records a new access, so that future access can potentially be skipped
-    /// by `skip_if_known_noop`.
-    /// The invariants for this function are closely coupled to the function above:
-    /// It MUST be called on child accesses, and on foreign accesses MUST be called
-    /// when `skip_if_know_noop` returns `Recurse`, and MUST NOT be called otherwise.
-    /// FIXME: This optimization is wrong, and is currently disabled (by ignoring the
-    /// result returned here). Since we presumably want an optimization like this,
-    /// we should add it back. See #3864 for more information.
-    #[allow(unused)]
+    /// by `skip_if_known_noop`. This must be called on child accesses, and otherwise
+    /// shoud be called on foreign accesses for increased performance. It should not be called
+    /// when `skip_if_known_noop` indicated skipping, since it then is a no-op.
+    /// See `foreign_access_skipping.rs`
     fn record_new_access(&mut self, access_kind: AccessKind, rel_pos: AccessRelatedness) {
-        if rel_pos.is_foreign() {
-            self.latest_foreign_access = Some(access_kind);
-        } else {
-            self.latest_foreign_access = None;
-        }
+        debug_assert!(matches!(
+            self.skip_if_known_noop(access_kind, rel_pos),
+            ContinueTraversal::Recurse
+        ));
+        self.idempotent_foreign_access
+            .record_new(IdempotentForeignAccess::from_acc_and_rel(access_kind, rel_pos));
     }
 }
 
@@ -277,6 +244,11 @@ pub(super) struct Node {
     /// It is only ever `Disabled` for a tree root, since the root is initialized to `Active` by
     /// its own separate mechanism.
     default_initial_perm: Permission,
+    /// The default initial (strongest) idempotent foreign access.
+    /// This participates in the invariant for `LocationState::idempotent_foreign_access`
+    /// in cases where there is no location state yet. See `foreign_access_skipping.rs`,
+    /// and `LocationState::idempotent_foreign_access` for more information
+    default_initial_idempotent_foreign_access: IdempotentForeignAccess,
     /// Some extra information useful only for debugging purposes
     pub debug_info: NodeDebugInfo,
 }
@@ -430,7 +402,7 @@ where
                 }
                 self.stack.push((
                     child,
-                    AccessRelatedness::DistantAccess,
+                    AccessRelatedness::CousinAccess,
                     RecursionState::BeforeChildren,
                 ));
             }
@@ -591,6 +563,8 @@ impl Tree {
                 parent: None,
                 children: SmallVec::default(),
                 default_initial_perm: root_default_perm,
+                // The root may never be skipped, all accesses will be local.
+                default_initial_idempotent_foreign_access: IdempotentForeignAccess::None,
                 debug_info,
             });
             nodes
@@ -601,7 +575,10 @@ impl Tree {
             // We also ensure that it is initialized, so that no `Active` but
             // not yet initialized nodes exist. Essentially, we pretend there
             // was a write that initialized these to `Active`.
-            perms.insert(root_idx, LocationState::new_init(Permission::new_active()));
+            perms.insert(
+                root_idx,
+                LocationState::new_init(Permission::new_active(), IdempotentForeignAccess::None),
+            );
             RangeMap::new(size, perms)
         };
         Self { root: root_idx, nodes, rperms, tag_mapping }
@@ -617,29 +594,79 @@ impl<'tcx> Tree {
         default_initial_perm: Permission,
         reborrow_range: AllocRange,
         span: Span,
+        prot: bool,
     ) -> InterpResult<'tcx> {
         assert!(!self.tag_mapping.contains_key(&new_tag));
         let idx = self.tag_mapping.insert(new_tag);
         let parent_idx = self.tag_mapping.get(&parent_tag).unwrap();
+        let strongest_idempotent = default_initial_perm.strongest_idempotent_foreign_access(prot);
         // Create the node
         self.nodes.insert(idx, Node {
             tag: new_tag,
             parent: Some(parent_idx),
             children: SmallVec::default(),
             default_initial_perm,
+            default_initial_idempotent_foreign_access: strongest_idempotent,
             debug_info: NodeDebugInfo::new(new_tag, default_initial_perm, span),
         });
         // Register new_tag as a child of parent_tag
         self.nodes.get_mut(parent_idx).unwrap().children.push(idx);
         // Initialize perms
-        let perm = LocationState::new_init(default_initial_perm);
+        let perm = LocationState::new_init(default_initial_perm, strongest_idempotent);
         for (_perms_range, perms) in self.rperms.iter_mut(reborrow_range.start, reborrow_range.size)
         {
             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.
+        self.update_last_accessed_after_retag(parent_idx, strongest_idempotent);
+
         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(
+        &mut self,
+        mut current: UniIndex,
+        strongest_allowed: IdempotentForeignAccess,
+    ) {
+        // We walk the tree upwards, until the invariant is restored
+        loop {
+            let current_node = self.nodes.get_mut(current).unwrap();
+            // Call `ensure_no_stronger_than` on all SIFAs for this node: the per-location SIFA, as well
+            // as the default SIFA for not-yet-initialized locations.
+            // Record whether we did any change; if not, the invariant is restored and we can stop the traversal.
+            let mut any_change = false;
+            for (_, map) in self.rperms.iter_mut_all() {
+                // Check if this node has a state for this location (or range of locations).
+                if let Some(perm) = map.get_mut(current) {
+                    // Update the per-location SIFA, recording if it changed.
+                    any_change |=
+                        perm.idempotent_foreign_access.ensure_no_stronger_than(strongest_allowed);
+                }
+            }
+            // Now update `default_initial_idempotent_foreign_access`, which stores the default SIFA for not-yet-initialized locations.
+            any_change |= current_node
+                .default_initial_idempotent_foreign_access
+                .ensure_no_stronger_than(strongest_allowed);
+
+            if any_change {
+                let Some(next) = self.nodes.get(current).unwrap().parent else {
+                    // We have arrived at the root.
+                    break;
+                };
+                current = next;
+                continue;
+            } else {
+                break;
+            }
+        }
+    }
+
     /// Deallocation requires
     /// - a pointer that permits write accesses
     /// - the absence of Strong Protectors anywhere in the allocation
@@ -731,13 +758,8 @@ impl<'tcx> Tree {
         let node_skipper = |access_kind: AccessKind, args: &NodeAppArgs<'_>| -> ContinueTraversal {
             let NodeAppArgs { node, perm, rel_pos } = args;
 
-            let old_state = perm
-                .get()
-                .copied()
-                .unwrap_or_else(|| LocationState::new_uninit(node.default_initial_perm));
-            // FIXME: See #3684
-            let _would_skip_if_not_for_fixme = old_state.skip_if_known_noop(access_kind, *rel_pos);
-            ContinueTraversal::Recurse
+            let old_state = perm.get().copied().unwrap_or_else(|| node.default_location_state());
+            old_state.skip_if_known_noop(access_kind, *rel_pos)
         };
         let node_app = |perms_range: Range<u64>,
                         access_kind: AccessKind,
@@ -746,10 +768,12 @@ impl<'tcx> Tree {
          -> Result<(), TransitionError> {
             let NodeAppArgs { node, mut perm, rel_pos } = args;
 
-            let old_state = perm.or_insert(LocationState::new_uninit(node.default_initial_perm));
+            let old_state = perm.or_insert(node.default_location_state());
 
-            // FIXME: See #3684
-            // old_state.record_new_access(access_kind, rel_pos);
+            // Call this function now, which ensures it is only called when
+            // `skip_if_known_noop` returns `Recurse`, due to the contract of
+            // `traverse_this_parents_children_other`.
+            old_state.record_new_access(access_kind, rel_pos);
 
             let protected = global.borrow().protected_tags.contains_key(&node.tag);
             let transition = old_state.perform_access(access_kind, rel_pos, protected)?;
@@ -976,6 +1000,15 @@ impl Tree {
     }
 }
 
+impl Node {
+    pub fn default_location_state(&self) -> LocationState {
+        LocationState::new_uninit(
+            self.default_initial_perm,
+            self.default_initial_idempotent_foreign_access,
+        )
+    }
+}
+
 impl VisitProvenance for Tree {
     fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
         // To ensure that the root never gets removed, we visit it
@@ -998,15 +1031,14 @@ pub enum AccessRelatedness {
     AncestorAccess,
     /// The accessed pointer is neither of the above.
     // It's a cousin/uncle/etc., something in a side branch.
-    // FIXME: find a better name ?
-    DistantAccess,
+    CousinAccess,
 }
 
 impl AccessRelatedness {
     /// Check that access is either Ancestor or Distant, i.e. not
     /// a transitive child (initial pointer included).
     pub fn is_foreign(self) -> bool {
-        matches!(self, AccessRelatedness::AncestorAccess | AccessRelatedness::DistantAccess)
+        matches!(self, AccessRelatedness::AncestorAccess | AccessRelatedness::CousinAccess)
     }
 
     /// Given the AccessRelatedness for the parent node, compute the AccessRelatedness
@@ -1016,7 +1048,7 @@ impl AccessRelatedness {
         use AccessRelatedness::*;
         match self {
             AncestorAccess | This => AncestorAccess,
-            StrictChildAccess | DistantAccess => DistantAccess,
+            StrictChildAccess | CousinAccess => CousinAccess,
         }
     }
 }
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 5d51a72852c..a429940748c 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
@@ -10,7 +10,11 @@ impl Exhaustive for LocationState {
     fn exhaustive() -> Box<dyn Iterator<Item = Self>> {
         // We keep `latest_foreign_access` at `None` as that's just a cache.
         Box::new(<(Permission, bool)>::exhaustive().map(|(permission, initialized)| {
-            Self { permission, initialized, latest_foreign_access: None }
+            Self {
+                permission,
+                initialized,
+                idempotent_foreign_access: IdempotentForeignAccess::default(),
+            }
         }))
     }
 }
@@ -260,15 +264,15 @@ mod spurious_read {
             match xy_rel {
                 RelPosXY::MutuallyForeign =>
                     match self {
-                        PtrSelector::X => (This, DistantAccess),
-                        PtrSelector::Y => (DistantAccess, This),
-                        PtrSelector::Other => (DistantAccess, DistantAccess),
+                        PtrSelector::X => (This, CousinAccess),
+                        PtrSelector::Y => (CousinAccess, This),
+                        PtrSelector::Other => (CousinAccess, CousinAccess),
                     },
                 RelPosXY::XChildY =>
                     match self {
                         PtrSelector::X => (This, StrictChildAccess),
                         PtrSelector::Y => (AncestorAccess, This),
-                        PtrSelector::Other => (DistantAccess, DistantAccess),
+                        PtrSelector::Other => (CousinAccess, CousinAccess),
                     },
             }
         }
@@ -598,13 +602,18 @@ mod spurious_read {
         let source = LocStateProtPair {
             xy_rel: RelPosXY::MutuallyForeign,
             x: LocStateProt {
-                state: LocationState::new_init(Permission::new_frozen()),
+                // For the tests, the strongest idempotent foreign access does not matter, so we use `Default::default`
+                state: LocationState::new_init(
+                    Permission::new_frozen(),
+                    IdempotentForeignAccess::default(),
+                ),
                 prot: true,
             },
             y: LocStateProt {
-                state: LocationState::new_uninit(Permission::new_reserved(
-                    /* freeze */ true, /* protected */ true,
-                )),
+                state: LocationState::new_uninit(
+                    Permission::new_reserved(/* freeze */ true, /* protected */ true),
+                    IdempotentForeignAccess::default(),
+                ),
                 prot: true,
             },
         };
diff --git a/src/tools/miri/src/concurrency/data_race.rs b/src/tools/miri/src/concurrency/data_race.rs
index f86d1eb1dcb..4cdc9348dc9 100644
--- a/src/tools/miri/src/concurrency/data_race.rs
+++ b/src/tools/miri/src/concurrency/data_race.rs
@@ -108,19 +108,19 @@ pub(super) struct ThreadClockSet {
     fence_acquire: VClock,
 
     /// The last timestamp of happens-before relations that
-    /// have been released by this thread by a fence.
+    /// have been released by this thread by a release fence.
     fence_release: VClock,
 
-    /// Timestamps of the last SC fence performed by each
-    /// thread, updated when this thread performs an SC fence
-    pub(super) fence_seqcst: VClock,
-
     /// Timestamps of the last SC write performed by each
-    /// thread, updated when this thread performs an SC fence
+    /// thread, updated when this thread performs an SC fence.
+    /// This is never acquired into the thread's clock, it
+    /// just limits which old writes can be seen in weak memory emulation.
     pub(super) write_seqcst: VClock,
 
     /// Timestamps of the last SC fence performed by each
-    /// thread, updated when this thread performs an SC read
+    /// thread, updated when this thread performs an SC read.
+    /// This is never acquired into the thread's clock, it
+    /// just limits which old writes can be seen in weak memory emulation.
     pub(super) read_seqcst: VClock,
 }
 
@@ -256,6 +256,106 @@ enum AccessType {
     AtomicRmw,
 }
 
+/// Per-byte vector clock metadata for data-race detection.
+#[derive(Clone, PartialEq, Eq, Debug)]
+struct MemoryCellClocks {
+    /// The vector-clock timestamp and the thread that did the last non-atomic write. We don't need
+    /// a full `VClock` here, it's always a single thread and nothing synchronizes, so the effective
+    /// clock is all-0 except for the thread that did the write.
+    write: (VectorIdx, VTimestamp),
+
+    /// The type of operation that the write index represents,
+    /// either newly allocated memory, a non-atomic write or
+    /// a deallocation of memory.
+    write_type: NaWriteType,
+
+    /// The vector-clock of all non-atomic reads that happened since the last non-atomic write
+    /// (i.e., we join together the "singleton" clocks corresponding to each read). It is reset to
+    /// zero on each write operation.
+    read: VClock,
+
+    /// Atomic access, acquire, release sequence 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>>,
+}
+
+/// Extra metadata associated with a thread.
+#[derive(Debug, Clone, Default)]
+struct ThreadExtraState {
+    /// The current vector index in use by the
+    /// thread currently, this is set to None
+    /// after the vector index has been re-used
+    /// and hence the value will never need to be
+    /// read during data-race reporting.
+    vector_index: Option<VectorIdx>,
+
+    /// Thread termination vector clock, this
+    /// is set on thread termination and is used
+    /// for joining on threads since the vector_index
+    /// may be re-used when the join operation occurs.
+    termination_vector_clock: Option<VClock>,
+}
+
+/// Global data-race detection state, contains the currently
+/// executing thread as well as the vector-clocks associated
+/// with each of the threads.
+// FIXME: it is probably better to have one large RefCell, than to have so many small ones.
+#[derive(Debug, Clone)]
+pub struct GlobalState {
+    /// Set to true once the first additional
+    /// thread has launched, due to the dependency
+    /// between before and after a thread launch.
+    /// Any data-races must be recorded after this
+    /// so concurrent execution can ignore recording
+    /// any data-races.
+    multi_threaded: Cell<bool>,
+
+    /// A flag to mark we are currently performing
+    /// a data race free action (such as atomic access)
+    /// to suppress the race detector
+    ongoing_action_data_race_free: Cell<bool>,
+
+    /// Mapping of a vector index to a known set of thread
+    /// clocks, this is not directly mapping from a thread id
+    /// since it may refer to multiple threads.
+    vector_clocks: RefCell<IndexVec<VectorIdx, ThreadClockSet>>,
+
+    /// Mapping of a given vector index to the current thread
+    /// that the execution is representing, this may change
+    /// if a vector index is re-assigned to a new thread.
+    vector_info: RefCell<IndexVec<VectorIdx, ThreadId>>,
+
+    /// The mapping of a given thread to associated thread metadata.
+    thread_info: RefCell<IndexVec<ThreadId, ThreadExtraState>>,
+
+    /// Potential vector indices that could be re-used on thread creation
+    /// values are inserted here on after the thread has terminated and
+    /// been joined with, and hence may potentially become free
+    /// for use as the index for a new thread.
+    /// Elements in this set may still require the vector index to
+    /// report data-races, and can only be re-used after all
+    /// active vector-clocks catch up with the threads timestamp.
+    reuse_candidates: RefCell<FxHashSet<VectorIdx>>,
+
+    /// We make SC fences act like RMWs on a global location.
+    /// To implement that, they all release and acquire into this clock.
+    last_sc_fence: RefCell<VClock>,
+
+    /// The timestamp of last SC write performed by each thread.
+    /// Threads only update their own index here!
+    last_sc_write_per_thread: RefCell<VClock>,
+
+    /// Track when an outdated (weak memory) load happens.
+    pub track_outdated_loads: bool,
+}
+
+impl VisitProvenance for GlobalState {
+    fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
+        // We don't have any tags.
+    }
+}
+
 impl AccessType {
     fn description(self, ty: Option<Ty<'_>>, size: Option<Size>) -> String {
         let mut msg = String::new();
@@ -309,30 +409,6 @@ impl AccessType {
     }
 }
 
-/// Per-byte vector clock metadata for data-race detection.
-#[derive(Clone, PartialEq, Eq, Debug)]
-struct MemoryCellClocks {
-    /// The vector-clock timestamp and the thread that did the last non-atomic write. We don't need
-    /// a full `VClock` here, it's always a single thread and nothing synchronizes, so the effective
-    /// clock is all-0 except for the thread that did the write.
-    write: (VectorIdx, VTimestamp),
-
-    /// The type of operation that the write index represents,
-    /// either newly allocated memory, a non-atomic write or
-    /// a deallocation of memory.
-    write_type: NaWriteType,
-
-    /// The vector-clock of all non-atomic reads that happened since the last non-atomic write
-    /// (i.e., we join together the "singleton" clocks corresponding to each read). It is reset to
-    /// zero on each write operation.
-    read: VClock,
-
-    /// Atomic access, acquire, release sequence 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>>,
-}
-
 impl AtomicMemoryCellClocks {
     fn new(size: Size) -> Self {
         AtomicMemoryCellClocks {
@@ -798,15 +874,27 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> {
                         // Either Acquire | AcqRel | SeqCst
                         clocks.apply_acquire_fence();
                     }
+                    if atomic == AtomicFenceOrd::SeqCst {
+                        // Behave like an RMW on the global fence location. This takes full care of
+                        // all the SC fence requirements, including C++17 §32.4 [atomics.order]
+                        // paragraph 6 (which would limit what future reads can see). It also rules
+                        // out many legal behaviors, but we don't currently have a model that would
+                        // be more precise.
+                        // Also see the second bullet on page 10 of
+                        // <https://www.cs.tau.ac.il/~orilahav/papers/popl21_robustness.pdf>.
+                        let mut sc_fence_clock = data_race.last_sc_fence.borrow_mut();
+                        sc_fence_clock.join(&clocks.clock);
+                        clocks.clock.join(&sc_fence_clock);
+                        // Also establish some sort of order with the last SC write that happened, globally
+                        // (but this is only respected by future reads).
+                        clocks.write_seqcst.join(&data_race.last_sc_write_per_thread.borrow());
+                    }
+                    // The release fence is last, since both of the above could alter our clock,
+                    // which should be part of what is being released.
                     if atomic != AtomicFenceOrd::Acquire {
                         // Either Release | AcqRel | SeqCst
                         clocks.apply_release_fence();
                     }
-                    if atomic == AtomicFenceOrd::SeqCst {
-                        data_race.last_sc_fence.borrow_mut().set_at_index(&clocks.clock, index);
-                        clocks.fence_seqcst.join(&data_race.last_sc_fence.borrow());
-                        clocks.write_seqcst.join(&data_race.last_sc_write.borrow());
-                    }
 
                     // Increment timestamp in case of release semantics.
                     interp_ok(atomic != AtomicFenceOrd::Acquire)
@@ -1463,80 +1551,6 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
     }
 }
 
-/// Extra metadata associated with a thread.
-#[derive(Debug, Clone, Default)]
-struct ThreadExtraState {
-    /// The current vector index in use by the
-    /// thread currently, this is set to None
-    /// after the vector index has been re-used
-    /// and hence the value will never need to be
-    /// read during data-race reporting.
-    vector_index: Option<VectorIdx>,
-
-    /// Thread termination vector clock, this
-    /// is set on thread termination and is used
-    /// for joining on threads since the vector_index
-    /// may be re-used when the join operation occurs.
-    termination_vector_clock: Option<VClock>,
-}
-
-/// Global data-race detection state, contains the currently
-/// executing thread as well as the vector-clocks associated
-/// with each of the threads.
-// FIXME: it is probably better to have one large RefCell, than to have so many small ones.
-#[derive(Debug, Clone)]
-pub struct GlobalState {
-    /// Set to true once the first additional
-    /// thread has launched, due to the dependency
-    /// between before and after a thread launch.
-    /// Any data-races must be recorded after this
-    /// so concurrent execution can ignore recording
-    /// any data-races.
-    multi_threaded: Cell<bool>,
-
-    /// A flag to mark we are currently performing
-    /// a data race free action (such as atomic access)
-    /// to suppress the race detector
-    ongoing_action_data_race_free: Cell<bool>,
-
-    /// Mapping of a vector index to a known set of thread
-    /// clocks, this is not directly mapping from a thread id
-    /// since it may refer to multiple threads.
-    vector_clocks: RefCell<IndexVec<VectorIdx, ThreadClockSet>>,
-
-    /// Mapping of a given vector index to the current thread
-    /// that the execution is representing, this may change
-    /// if a vector index is re-assigned to a new thread.
-    vector_info: RefCell<IndexVec<VectorIdx, ThreadId>>,
-
-    /// The mapping of a given thread to associated thread metadata.
-    thread_info: RefCell<IndexVec<ThreadId, ThreadExtraState>>,
-
-    /// Potential vector indices that could be re-used on thread creation
-    /// values are inserted here on after the thread has terminated and
-    /// been joined with, and hence may potentially become free
-    /// for use as the index for a new thread.
-    /// Elements in this set may still require the vector index to
-    /// report data-races, and can only be re-used after all
-    /// active vector-clocks catch up with the threads timestamp.
-    reuse_candidates: RefCell<FxHashSet<VectorIdx>>,
-
-    /// The timestamp of last SC fence performed by each thread
-    last_sc_fence: RefCell<VClock>,
-
-    /// The timestamp of last SC write performed by each thread
-    last_sc_write: RefCell<VClock>,
-
-    /// Track when an outdated (weak memory) load happens.
-    pub track_outdated_loads: bool,
-}
-
-impl VisitProvenance for GlobalState {
-    fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
-        // We don't have any tags.
-    }
-}
-
 impl GlobalState {
     /// Create a new global state, setup with just thread-id=0
     /// advanced to timestamp = 1.
@@ -1549,7 +1563,7 @@ impl GlobalState {
             thread_info: RefCell::new(IndexVec::new()),
             reuse_candidates: RefCell::new(FxHashSet::default()),
             last_sc_fence: RefCell::new(VClock::default()),
-            last_sc_write: RefCell::new(VClock::default()),
+            last_sc_write_per_thread: RefCell::new(VClock::default()),
             track_outdated_loads: config.track_outdated_loads,
         };
 
@@ -1851,7 +1865,7 @@ impl GlobalState {
     // SC ATOMIC STORE rule in the paper.
     pub(super) fn sc_write(&self, thread_mgr: &ThreadManager<'_>) {
         let (index, clocks) = self.active_thread_state(thread_mgr);
-        self.last_sc_write.borrow_mut().set_at_index(&clocks.clock, index);
+        self.last_sc_write_per_thread.borrow_mut().set_at_index(&clocks.clock, index);
     }
 
     // SC ATOMIC READ rule in the paper.
diff --git a/src/tools/miri/src/concurrency/weak_memory.rs b/src/tools/miri/src/concurrency/weak_memory.rs
index c610f1999f7..1a3e9614f8a 100644
--- a/src/tools/miri/src/concurrency/weak_memory.rs
+++ b/src/tools/miri/src/concurrency/weak_memory.rs
@@ -11,10 +11,17 @@
 //! This implementation is not fully correct under the revised C++20 model and may generate behaviours C++20
 //! disallows (<https://github.com/rust-lang/miri/issues/2301>).
 //!
-//! A modification is made to the paper's model to partially address C++20 changes.
-//! Specifically, if an SC load reads from an atomic store of any ordering, then a later SC load cannot read from
-//! an earlier store in the location's modification order. This is to prevent creating a backwards S edge from the second
-//! load to the first, as a result of C++20's coherence-ordered before rules.
+//! Modifications are made to the paper's model to address C++20 changes:
+//! - If an SC load reads from an atomic store of any ordering, then a later SC load cannot read
+//!   from an earlier store in the location's modification order. This is to prevent creating a
+//!   backwards S edge from the second load to the first, as a result of C++20's coherence-ordered
+//!   before rules. (This seems to rule out behaviors that were actually permitted by the RC11 model
+//!   that C++20 intended to copy (<https://plv.mpi-sws.org/scfix/paper.pdf>); a change was
+//!   introduced when translating the math to English. According to Viktor Vafeiadis, this
+//!   difference is harmless. So we stick to what the standard says, and allow fewer behaviors.)
+//! - SC fences are treated like AcqRel RMWs to a global clock, to ensure they induce enough
+//!   synchronization with the surrounding accesses. This rules out legal behavior, but it is really
+//!   hard to be more precise here.
 //!
 //! Rust follows the C++20 memory model (except for the Consume ordering and some operations not performable through C++'s
 //! `std::atomic<T>` API). It is therefore possible for this implementation to generate behaviours never observable when the
@@ -138,6 +145,7 @@ struct StoreElement {
 
     /// 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>,
@@ -335,11 +343,6 @@ 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.fence_seqcst[store_elem.store_index] {
-                    // The current load, which may be sequenced-after an SC fence, cannot read-before
-                    // the last store sequenced-before an SC fence in another thread.
-                    // C++17 §32.4 [atomics.order] paragraph 6
-                    false
                 } else if store_elem.timestamp <= clocks.write_seqcst[store_elem.store_index]
                     && store_elem.is_seqcst
                 {
@@ -356,9 +359,9 @@ impl<'tcx> StoreBuffer {
                     false
                 } else if is_seqcst && store_elem.load_info.borrow().sc_loaded {
                     // The current SC load cannot read-before a store that an earlier SC load has observed.
-                    // See https://github.com/rust-lang/miri/issues/2301#issuecomment-1222720427
+                    // See https://github.com/rust-lang/miri/issues/2301#issuecomment-1222720427.
                     // Consequences of C++20 §31.4 [atomics.order] paragraph 3.1, 3.3 (coherence-ordered before)
-                    // and 4.1 (coherence-ordered before between SC makes global total order S)
+                    // and 4.1 (coherence-ordered before between SC makes global total order S).
                     false
                 } else {
                     true
diff --git a/src/tools/miri/src/intrinsics/simd.rs b/src/tools/miri/src/intrinsics/simd.rs
index 075b6f35e0e..54bdd3f02c2 100644
--- a/src/tools/miri/src/intrinsics/simd.rs
+++ b/src/tools/miri/src/intrinsics/simd.rs
@@ -1,4 +1,5 @@
 use either::Either;
+use rand::Rng;
 use rustc_abi::{Endian, HasDataLayout};
 use rustc_apfloat::{Float, Round};
 use rustc_middle::ty::FloatTy;
@@ -286,7 +287,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     this.write_scalar(val, &dest)?;
                 }
             }
-            "fma" => {
+            "fma" | "relaxed_fma" => {
                 let [a, b, c] = check_arg_count(args)?;
                 let (a, a_len) = this.project_to_simd(a)?;
                 let (b, b_len) = this.project_to_simd(b)?;
@@ -303,6 +304,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     let c = this.read_scalar(&this.project_index(&c, i)?)?;
                     let dest = this.project_index(&dest, i)?;
 
+                    let fuse: bool = intrinsic_name == "fma" || this.machine.rng.get_mut().gen();
+
                     // Works for f32 and f64.
                     // FIXME: using host floats to work around https://github.com/rust-lang/miri/issues/2468.
                     let ty::Float(float_ty) = dest.layout.ty.kind() else {
@@ -314,7 +317,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                             let a = a.to_f32()?;
                             let b = b.to_f32()?;
                             let c = c.to_f32()?;
-                            let res = a.to_host().mul_add(b.to_host(), c.to_host()).to_soft();
+                            let res = if fuse {
+                                a.to_host().mul_add(b.to_host(), c.to_host()).to_soft()
+                            } else {
+                                ((a * b).value + c).value
+                            };
                             let res = this.adjust_nan(res, &[a, b, c]);
                             Scalar::from(res)
                         }
@@ -322,7 +329,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                             let a = a.to_f64()?;
                             let b = b.to_f64()?;
                             let c = c.to_f64()?;
-                            let res = a.to_host().mul_add(b.to_host(), c.to_host()).to_soft();
+                            let res = if fuse {
+                                a.to_host().mul_add(b.to_host(), c.to_host()).to_soft()
+                            } else {
+                                ((a * b).value + c).value
+                            };
                             let res = this.adjust_nan(res, &[a, b, c]);
                             Scalar::from(res)
                         }
diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs
index 814dc6d2b01..7cc22f83a24 100644
--- a/src/tools/miri/src/machine.rs
+++ b/src/tools/miri/src/machine.rs
@@ -270,6 +270,9 @@ impl interpret::Provenance for Provenance {
     /// We use absolute addresses in the `offset` of a `StrictPointer`.
     const OFFSET_IS_ADDR: bool = true;
 
+    /// Miri implements wildcard provenance.
+    const WILDCARD: Option<Self> = Some(Provenance::Wildcard);
+
     fn get_alloc_id(self) -> Option<AllocId> {
         match self {
             Provenance::Concrete { alloc_id, .. } => Some(alloc_id),
@@ -1242,8 +1245,11 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
     /// Called on `ptr as usize` casts.
     /// (Actually computing the resulting `usize` doesn't need machine help,
     /// that's just `Scalar::try_to_int`.)
-    fn expose_ptr(ecx: &mut InterpCx<'tcx, Self>, ptr: StrictPointer) -> InterpResult<'tcx> {
-        match ptr.provenance {
+    fn expose_provenance(
+        ecx: &InterpCx<'tcx, Self>,
+        provenance: Self::Provenance,
+    ) -> InterpResult<'tcx> {
+        match provenance {
             Provenance::Concrete { alloc_id, tag } => ecx.expose_ptr(alloc_id, tag),
             Provenance::Wildcard => {
                 // No need to do anything for wildcard pointers as
diff --git a/src/tools/miri/src/shims/backtrace.rs b/src/tools/miri/src/shims/backtrace.rs
index 64bd546458d..c7b399228bf 100644
--- a/src/tools/miri/src/shims/backtrace.rs
+++ b/src/tools/miri/src/shims/backtrace.rs
@@ -1,5 +1,4 @@
 use rustc_abi::{ExternAbi, Size};
-use rustc_ast::ast::Mutability;
 use rustc_middle::ty::layout::LayoutOf as _;
 use rustc_middle::ty::{self, Instance, Ty};
 use rustc_span::{BytePos, Loc, Symbol, hygiene};
@@ -179,14 +178,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
         match flags {
             0 => {
-                // These are "mutable" allocations as we consider them to be owned by the callee.
-                let name_alloc =
-                    this.allocate_str(&name, MiriMemoryKind::Rust.into(), Mutability::Mut)?;
-                let filename_alloc =
-                    this.allocate_str(&filename, MiriMemoryKind::Rust.into(), Mutability::Mut)?;
-
-                this.write_immediate(name_alloc.to_ref(this), &this.project_field(dest, 0)?)?;
-                this.write_immediate(filename_alloc.to_ref(this), &this.project_field(dest, 1)?)?;
+                throw_unsup_format!("miri_resolve_frame: v0 is not supported any more");
             }
             1 => {
                 this.write_scalar(
diff --git a/src/tools/miri/src/shims/files.rs b/src/tools/miri/src/shims/files.rs
new file mode 100644
index 00000000000..f673b834be2
--- /dev/null
+++ b/src/tools/miri/src/shims/files.rs
@@ -0,0 +1,411 @@
+use std::any::Any;
+use std::collections::BTreeMap;
+use std::io::{IsTerminal, Read, SeekFrom, Write};
+use std::ops::Deref;
+use std::rc::{Rc, Weak};
+use std::{fs, io};
+
+use rustc_abi::Size;
+
+use crate::shims::unix::UnixFileDescription;
+use crate::*;
+
+/// Represents an open file description.
+pub trait FileDescription: std::fmt::Debug + Any {
+    fn name(&self) -> &'static str;
+
+    /// Reads as much as possible into the given buffer `ptr`.
+    /// `len` indicates how many bytes we should try to read.
+    /// `dest` is where the return value should be stored: number of bytes read, or `-1` in case of error.
+    fn read<'tcx>(
+        &self,
+        _self_ref: &FileDescriptionRef,
+        _communicate_allowed: bool,
+        _ptr: Pointer,
+        _len: usize,
+        _dest: &MPlaceTy<'tcx>,
+        _ecx: &mut MiriInterpCx<'tcx>,
+    ) -> InterpResult<'tcx> {
+        throw_unsup_format!("cannot read from {}", self.name());
+    }
+
+    /// Writes as much as possible from the given buffer `ptr`.
+    /// `len` indicates how many bytes we should try to write.
+    /// `dest` is where the return value should be stored: number of bytes written, or `-1` in case of error.
+    fn write<'tcx>(
+        &self,
+        _self_ref: &FileDescriptionRef,
+        _communicate_allowed: bool,
+        _ptr: Pointer,
+        _len: usize,
+        _dest: &MPlaceTy<'tcx>,
+        _ecx: &mut MiriInterpCx<'tcx>,
+    ) -> InterpResult<'tcx> {
+        throw_unsup_format!("cannot write to {}", self.name());
+    }
+
+    /// Seeks to the given offset (which can be relative to the beginning, end, or current position).
+    /// Returns the new position from the start of the stream.
+    fn seek<'tcx>(
+        &self,
+        _communicate_allowed: bool,
+        _offset: SeekFrom,
+    ) -> InterpResult<'tcx, io::Result<u64>> {
+        throw_unsup_format!("cannot seek on {}", self.name());
+    }
+
+    fn close<'tcx>(
+        self: Box<Self>,
+        _communicate_allowed: bool,
+        _ecx: &mut MiriInterpCx<'tcx>,
+    ) -> InterpResult<'tcx, io::Result<()>> {
+        throw_unsup_format!("cannot close {}", self.name());
+    }
+
+    fn metadata<'tcx>(&self) -> InterpResult<'tcx, io::Result<fs::Metadata>> {
+        throw_unsup_format!("obtaining metadata is only supported on file-backed file descriptors");
+    }
+
+    fn is_tty(&self, _communicate_allowed: bool) -> bool {
+        // Most FDs are not tty's and the consequence of a wrong `false` are minor,
+        // so we use a default impl here.
+        false
+    }
+
+    fn as_unix(&self) -> &dyn UnixFileDescription {
+        panic!("Not a unix file descriptor: {}", self.name());
+    }
+}
+
+impl dyn FileDescription {
+    #[inline(always)]
+    pub fn downcast<T: Any>(&self) -> Option<&T> {
+        (self as &dyn Any).downcast_ref()
+    }
+}
+
+impl FileDescription for io::Stdin {
+    fn name(&self) -> &'static str {
+        "stdin"
+    }
+
+    fn read<'tcx>(
+        &self,
+        _self_ref: &FileDescriptionRef,
+        communicate_allowed: bool,
+        ptr: Pointer,
+        len: usize,
+        dest: &MPlaceTy<'tcx>,
+        ecx: &mut MiriInterpCx<'tcx>,
+    ) -> InterpResult<'tcx> {
+        let mut bytes = vec![0; len];
+        if !communicate_allowed {
+            // We want isolation mode to be deterministic, so we have to disallow all reads, even stdin.
+            helpers::isolation_abort_error("`read` from stdin")?;
+        }
+        let result = Read::read(&mut { self }, &mut bytes);
+        match result {
+            Ok(read_size) => ecx.return_read_success(ptr, &bytes, read_size, dest),
+            Err(e) => ecx.set_last_error_and_return(e, dest),
+        }
+    }
+
+    fn is_tty(&self, communicate_allowed: bool) -> bool {
+        communicate_allowed && self.is_terminal()
+    }
+}
+
+impl FileDescription for io::Stdout {
+    fn name(&self) -> &'static str {
+        "stdout"
+    }
+
+    fn write<'tcx>(
+        &self,
+        _self_ref: &FileDescriptionRef,
+        _communicate_allowed: bool,
+        ptr: Pointer,
+        len: usize,
+        dest: &MPlaceTy<'tcx>,
+        ecx: &mut MiriInterpCx<'tcx>,
+    ) -> InterpResult<'tcx> {
+        let bytes = ecx.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
+        // We allow writing to stderr even with isolation enabled.
+        let result = Write::write(&mut { self }, bytes);
+        // Stdout is buffered, flush to make sure it appears on the
+        // screen.  This is the write() syscall of the interpreted
+        // program, we want it to correspond to a write() syscall on
+        // the host -- there is no good in adding extra buffering
+        // here.
+        io::stdout().flush().unwrap();
+        match result {
+            Ok(write_size) => ecx.return_write_success(write_size, dest),
+            Err(e) => ecx.set_last_error_and_return(e, dest),
+        }
+    }
+
+    fn is_tty(&self, communicate_allowed: bool) -> bool {
+        communicate_allowed && self.is_terminal()
+    }
+}
+
+impl FileDescription for io::Stderr {
+    fn name(&self) -> &'static str {
+        "stderr"
+    }
+
+    fn write<'tcx>(
+        &self,
+        _self_ref: &FileDescriptionRef,
+        _communicate_allowed: bool,
+        ptr: Pointer,
+        len: usize,
+        dest: &MPlaceTy<'tcx>,
+        ecx: &mut MiriInterpCx<'tcx>,
+    ) -> InterpResult<'tcx> {
+        let bytes = ecx.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
+        // We allow writing to stderr even with isolation enabled.
+        // No need to flush, stderr is not buffered.
+        let result = Write::write(&mut { self }, bytes);
+        match result {
+            Ok(write_size) => ecx.return_write_success(write_size, dest),
+            Err(e) => ecx.set_last_error_and_return(e, dest),
+        }
+    }
+
+    fn is_tty(&self, communicate_allowed: bool) -> bool {
+        communicate_allowed && self.is_terminal()
+    }
+}
+
+/// Like /dev/null
+#[derive(Debug)]
+pub struct NullOutput;
+
+impl FileDescription for NullOutput {
+    fn name(&self) -> &'static str {
+        "stderr and stdout"
+    }
+
+    fn write<'tcx>(
+        &self,
+        _self_ref: &FileDescriptionRef,
+        _communicate_allowed: bool,
+        _ptr: Pointer,
+        len: usize,
+        dest: &MPlaceTy<'tcx>,
+        ecx: &mut MiriInterpCx<'tcx>,
+    ) -> InterpResult<'tcx> {
+        // We just don't write anything, but report to the user that we did.
+        ecx.return_write_success(len, dest)
+    }
+}
+
+/// Structure contains both the file description and its unique identifier.
+#[derive(Clone, Debug)]
+pub struct FileDescWithId<T: FileDescription + ?Sized> {
+    id: FdId,
+    file_description: Box<T>,
+}
+
+#[derive(Clone, Debug)]
+pub struct FileDescriptionRef(Rc<FileDescWithId<dyn FileDescription>>);
+
+impl Deref for FileDescriptionRef {
+    type Target = dyn FileDescription;
+
+    fn deref(&self) -> &Self::Target {
+        &*self.0.file_description
+    }
+}
+
+impl FileDescriptionRef {
+    fn new(fd: impl FileDescription, id: FdId) -> Self {
+        FileDescriptionRef(Rc::new(FileDescWithId { id, file_description: Box::new(fd) }))
+    }
+
+    pub fn close<'tcx>(
+        self,
+        communicate_allowed: bool,
+        ecx: &mut MiriInterpCx<'tcx>,
+    ) -> InterpResult<'tcx, io::Result<()>> {
+        // Destroy this `Rc` using `into_inner` so we can call `close` instead of
+        // implicitly running the destructor of the file description.
+        let id = self.get_id();
+        match Rc::into_inner(self.0) {
+            Some(fd) => {
+                // Remove entry from the global epoll_event_interest table.
+                ecx.machine.epoll_interests.remove(id);
+
+                fd.file_description.close(communicate_allowed, ecx)
+            }
+            None => interp_ok(Ok(())),
+        }
+    }
+
+    pub fn downgrade(&self) -> WeakFileDescriptionRef {
+        WeakFileDescriptionRef { weak_ref: Rc::downgrade(&self.0) }
+    }
+
+    pub fn get_id(&self) -> FdId {
+        self.0.id
+    }
+}
+
+/// Holds a weak reference to the actual file description.
+#[derive(Clone, Debug, Default)]
+pub struct WeakFileDescriptionRef {
+    weak_ref: Weak<FileDescWithId<dyn FileDescription>>,
+}
+
+impl WeakFileDescriptionRef {
+    pub fn upgrade(&self) -> Option<FileDescriptionRef> {
+        if let Some(file_desc_with_id) = self.weak_ref.upgrade() {
+            return Some(FileDescriptionRef(file_desc_with_id));
+        }
+        None
+    }
+}
+
+impl VisitProvenance for WeakFileDescriptionRef {
+    fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
+        // A weak reference can never be the only reference to some pointer or place.
+        // Since the actual file description is tracked by strong ref somewhere,
+        // it is ok to make this a NOP operation.
+    }
+}
+
+/// A unique id for file descriptions. While we could use the address, considering that
+/// is definitely unique, the address would expose interpreter internal state when used
+/// for sorting things. So instead we generate a unique id per file description is the name
+/// for all `dup`licates and is never reused.
+#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Ord, PartialOrd)]
+pub struct FdId(usize);
+
+/// The file descriptor table
+#[derive(Debug)]
+pub struct FdTable {
+    pub fds: BTreeMap<i32, FileDescriptionRef>,
+    /// Unique identifier for file description, used to differentiate between various file description.
+    next_file_description_id: FdId,
+}
+
+impl VisitProvenance for FdTable {
+    fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
+        // All our FileDescription instances do not have any tags.
+    }
+}
+
+impl FdTable {
+    fn new() -> Self {
+        FdTable { fds: BTreeMap::new(), next_file_description_id: FdId(0) }
+    }
+    pub(crate) fn init(mute_stdout_stderr: bool) -> FdTable {
+        let mut fds = FdTable::new();
+        fds.insert_new(io::stdin());
+        if mute_stdout_stderr {
+            assert_eq!(fds.insert_new(NullOutput), 1);
+            assert_eq!(fds.insert_new(NullOutput), 2);
+        } else {
+            assert_eq!(fds.insert_new(io::stdout()), 1);
+            assert_eq!(fds.insert_new(io::stderr()), 2);
+        }
+        fds
+    }
+
+    pub fn new_ref(&mut self, fd: impl FileDescription) -> FileDescriptionRef {
+        let file_handle = FileDescriptionRef::new(fd, self.next_file_description_id);
+        self.next_file_description_id = FdId(self.next_file_description_id.0.strict_add(1));
+        file_handle
+    }
+
+    /// Insert a new file description to the FdTable.
+    pub fn insert_new(&mut self, fd: impl FileDescription) -> i32 {
+        let fd_ref = self.new_ref(fd);
+        self.insert(fd_ref)
+    }
+
+    pub fn insert(&mut self, fd_ref: FileDescriptionRef) -> i32 {
+        self.insert_with_min_num(fd_ref, 0)
+    }
+
+    /// Insert a file description, giving it a file descriptor that is at least `min_fd_num`.
+    pub fn insert_with_min_num(&mut self, file_handle: FileDescriptionRef, min_fd_num: i32) -> i32 {
+        // Find the lowest unused FD, starting from min_fd. If the first such unused FD is in
+        // between used FDs, the find_map combinator will return it. If the first such unused FD
+        // is after all other used FDs, the find_map combinator will return None, and we will use
+        // the FD following the greatest FD thus far.
+        let candidate_new_fd =
+            self.fds.range(min_fd_num..).zip(min_fd_num..).find_map(|((fd_num, _fd), counter)| {
+                if *fd_num != counter {
+                    // There was a gap in the fds stored, return the first unused one
+                    // (note that this relies on BTreeMap iterating in key order)
+                    Some(counter)
+                } else {
+                    // This fd is used, keep going
+                    None
+                }
+            });
+        let new_fd_num = candidate_new_fd.unwrap_or_else(|| {
+            // find_map ran out of BTreeMap entries before finding a free fd, use one plus the
+            // maximum fd in the map
+            self.fds.last_key_value().map(|(fd_num, _)| fd_num.strict_add(1)).unwrap_or(min_fd_num)
+        });
+
+        self.fds.try_insert(new_fd_num, file_handle).unwrap();
+        new_fd_num
+    }
+
+    pub fn get(&self, fd_num: i32) -> Option<FileDescriptionRef> {
+        let fd = self.fds.get(&fd_num)?;
+        Some(fd.clone())
+    }
+
+    pub fn remove(&mut self, fd_num: i32) -> Option<FileDescriptionRef> {
+        self.fds.remove(&fd_num)
+    }
+
+    pub fn is_fd_num(&self, fd_num: i32) -> bool {
+        self.fds.contains_key(&fd_num)
+    }
+}
+
+impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
+pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
+    /// Helper to implement `FileDescription::read`:
+    /// This is only used when `read` is successful.
+    /// `actual_read_size` should be the return value of some underlying `read` call that used
+    /// `bytes` as its output buffer.
+    /// The length of `bytes` must not exceed either the host's or the target's `isize`.
+    /// `bytes` is written to `buf` and the size is written to `dest`.
+    fn return_read_success(
+        &mut self,
+        buf: Pointer,
+        bytes: &[u8],
+        actual_read_size: usize,
+        dest: &MPlaceTy<'tcx>,
+    ) -> InterpResult<'tcx> {
+        let this = self.eval_context_mut();
+        // If reading to `bytes` did not fail, we write those bytes to the buffer.
+        // Crucially, if fewer than `bytes.len()` bytes were read, only write
+        // that much into the output buffer!
+        this.write_bytes_ptr(buf, bytes[..actual_read_size].iter().copied())?;
+
+        // The actual read size is always less than what got originally requested so this cannot fail.
+        this.write_int(u64::try_from(actual_read_size).unwrap(), dest)?;
+        interp_ok(())
+    }
+
+    /// Helper to implement `FileDescription::write`:
+    /// This function is only used when `write` is successful, and writes `actual_write_size` to `dest`
+    fn return_write_success(
+        &mut self,
+        actual_write_size: usize,
+        dest: &MPlaceTy<'tcx>,
+    ) -> InterpResult<'tcx> {
+        let this = self.eval_context_mut();
+        // The actual write size is always less than what got originally requested so this cannot fail.
+        this.write_int(u64::try_from(actual_write_size).unwrap(), dest)?;
+        interp_ok(())
+    }
+}
diff --git a/src/tools/miri/src/shims/io_error.rs b/src/tools/miri/src/shims/io_error.rs
index 0cbb4850b7f..acf3f74a93d 100644
--- a/src/tools/miri/src/shims/io_error.rs
+++ b/src/tools/miri/src/shims/io_error.rs
@@ -44,7 +44,7 @@ const UNIX_IO_ERROR_TABLE: &[(&str, std::io::ErrorKind)] = {
         ("ECONNREFUSED", ConnectionRefused),
         ("ECONNRESET", ConnectionReset),
         ("EDEADLK", Deadlock),
-        ("EDQUOT", FilesystemQuotaExceeded),
+        ("EDQUOT", QuotaExceeded),
         ("EEXIST", AlreadyExists),
         ("EFBIG", FileTooLarge),
         ("EHOSTUNREACH", HostUnreachable),
@@ -82,11 +82,72 @@ const UNIX_IO_ERROR_TABLE: &[(&str, std::io::ErrorKind)] = {
 // <https://github.com/rust-lang/rust/blob/master/library/std/src/sys/pal/windows/mod.rs>.
 const WINDOWS_IO_ERROR_TABLE: &[(&str, std::io::ErrorKind)] = {
     use std::io::ErrorKind::*;
-    // FIXME: this is still incomplete.
+    // It's common for multiple error codes to map to the same io::ErrorKind. We have all for the
+    // forwards mapping; only the first one will be used for the backwards mapping.
+    // Slightly arbitrarily, we prefer non-WSA and the most generic sounding variant for backwards
+    // mapping.
     &[
-        ("ERROR_ACCESS_DENIED", PermissionDenied),
-        ("ERROR_FILE_NOT_FOUND", NotFound),
+        ("WSAEADDRINUSE", AddrInUse),
+        ("WSAEADDRNOTAVAIL", AddrNotAvailable),
+        ("ERROR_ALREADY_EXISTS", AlreadyExists),
+        ("ERROR_FILE_EXISTS", AlreadyExists),
+        ("ERROR_NO_DATA", BrokenPipe),
+        ("WSAECONNABORTED", ConnectionAborted),
+        ("WSAECONNREFUSED", ConnectionRefused),
+        ("WSAECONNRESET", ConnectionReset),
+        ("ERROR_NOT_SAME_DEVICE", CrossesDevices),
+        ("ERROR_POSSIBLE_DEADLOCK", Deadlock),
+        ("ERROR_DIR_NOT_EMPTY", DirectoryNotEmpty),
+        ("ERROR_CANT_RESOLVE_FILENAME", FilesystemLoop),
+        ("ERROR_DISK_QUOTA_EXCEEDED", QuotaExceeded),
+        ("WSAEDQUOT", QuotaExceeded),
+        ("ERROR_FILE_TOO_LARGE", FileTooLarge),
+        ("ERROR_HOST_UNREACHABLE", HostUnreachable),
+        ("WSAEHOSTUNREACH", HostUnreachable),
+        ("ERROR_INVALID_NAME", InvalidFilename),
+        ("ERROR_BAD_PATHNAME", InvalidFilename),
+        ("ERROR_FILENAME_EXCED_RANGE", InvalidFilename),
         ("ERROR_INVALID_PARAMETER", InvalidInput),
+        ("WSAEINVAL", InvalidInput),
+        ("ERROR_DIRECTORY_NOT_SUPPORTED", IsADirectory),
+        ("WSAENETDOWN", NetworkDown),
+        ("ERROR_NETWORK_UNREACHABLE", NetworkUnreachable),
+        ("WSAENETUNREACH", NetworkUnreachable),
+        ("ERROR_DIRECTORY", NotADirectory),
+        ("WSAENOTCONN", NotConnected),
+        ("ERROR_FILE_NOT_FOUND", NotFound),
+        ("ERROR_PATH_NOT_FOUND", NotFound),
+        ("ERROR_INVALID_DRIVE", NotFound),
+        ("ERROR_BAD_NETPATH", NotFound),
+        ("ERROR_BAD_NET_NAME", NotFound),
+        ("ERROR_SEEK_ON_DEVICE", NotSeekable),
+        ("ERROR_NOT_ENOUGH_MEMORY", OutOfMemory),
+        ("ERROR_OUTOFMEMORY", OutOfMemory),
+        ("ERROR_ACCESS_DENIED", PermissionDenied),
+        ("WSAEACCES", PermissionDenied),
+        ("ERROR_WRITE_PROTECT", ReadOnlyFilesystem),
+        ("ERROR_BUSY", ResourceBusy),
+        ("ERROR_DISK_FULL", StorageFull),
+        ("ERROR_HANDLE_DISK_FULL", StorageFull),
+        ("WAIT_TIMEOUT", TimedOut),
+        ("WSAETIMEDOUT", TimedOut),
+        ("ERROR_DRIVER_CANCEL_TIMEOUT", TimedOut),
+        ("ERROR_OPERATION_ABORTED", TimedOut),
+        ("ERROR_SERVICE_REQUEST_TIMEOUT", TimedOut),
+        ("ERROR_COUNTER_TIMEOUT", TimedOut),
+        ("ERROR_TIMEOUT", TimedOut),
+        ("ERROR_RESOURCE_CALL_TIMED_OUT", TimedOut),
+        ("ERROR_CTX_MODEM_RESPONSE_TIMEOUT", TimedOut),
+        ("ERROR_CTX_CLIENT_QUERY_TIMEOUT", TimedOut),
+        ("FRS_ERR_SYSVOL_POPULATE_TIMEOUT", TimedOut),
+        ("ERROR_DS_TIMELIMIT_EXCEEDED", TimedOut),
+        ("DNS_ERROR_RECORD_TIMED_OUT", TimedOut),
+        ("ERROR_IPSEC_IKE_TIMED_OUT", TimedOut),
+        ("ERROR_RUNLEVEL_SWITCH_TIMEOUT", TimedOut),
+        ("ERROR_RUNLEVEL_SWITCH_AGENT_TIMEOUT", TimedOut),
+        ("ERROR_TOO_MANY_LINKS", TooManyLinks),
+        ("ERROR_CALL_NOT_IMPLEMENTED", Unsupported),
+        ("WSAEWOULDBLOCK", WouldBlock),
     ]
 };
 
diff --git a/src/tools/miri/src/shims/mod.rs b/src/tools/miri/src/shims/mod.rs
index b9317ac1a15..61681edcf76 100644
--- a/src/tools/miri/src/shims/mod.rs
+++ b/src/tools/miri/src/shims/mod.rs
@@ -2,6 +2,7 @@
 
 mod alloc;
 mod backtrace;
+mod files;
 #[cfg(unix)]
 mod native_lib;
 mod unix;
@@ -18,7 +19,8 @@ pub mod panic;
 pub mod time;
 pub mod tls;
 
-pub use self::unix::{DirTable, EpollInterestTable, FdTable};
+pub use self::files::FdTable;
+pub use self::unix::{DirTable, EpollInterestTable};
 
 /// What needs to be done after emulating an item (a shim or an intrinsic) is done.
 pub enum EmulateItemResult {
diff --git a/src/tools/miri/src/shims/native_lib.rs b/src/tools/miri/src/shims/native_lib.rs
index e7a4251242e..f18d0236774 100644
--- a/src/tools/miri/src/shims/native_lib.rs
+++ b/src/tools/miri/src/shims/native_lib.rs
@@ -3,7 +3,8 @@ use std::ops::Deref;
 
 use libffi::high::call as ffi;
 use libffi::low::CodePtr;
-use rustc_abi::{BackendRepr, HasDataLayout};
+use rustc_abi::{BackendRepr, HasDataLayout, Size};
+use rustc_middle::mir::interpret::Pointer;
 use rustc_middle::ty::{self as ty, IntTy, UintTy};
 use rustc_span::Symbol;
 
@@ -75,6 +76,11 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 unsafe { ffi::call::<()>(ptr, libffi_args.as_slice()) };
                 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()));
+                Scalar::from_pointer(ptr, this)
+            }
             _ => throw_unsup_format!("unsupported return type for native call: {:?}", link_name),
         };
         interp_ok(ImmTy::from_scalar(scalar, dest.layout))
@@ -152,9 +158,27 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             if !matches!(arg.layout.backend_repr, BackendRepr::Scalar(_)) {
                 throw_unsup_format!("only scalar argument types are support for native calls")
             }
-            libffi_args.push(imm_to_carg(this.read_immediate(arg)?, this)?);
+            let imm = this.read_immediate(arg)?;
+            libffi_args.push(imm_to_carg(&imm, this)?);
+            // If we are passing a pointer, prepare the memory it points to.
+            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.
+                    continue;
+                };
+                // We use `get_alloc_id` for its best-effort behaviour with Wildcard provenance.
+                let Some(alloc_id) = prov.get_alloc_id() else {
+                    // Wildcard pointer, whatever it points to must be already exposed.
+                    continue;
+                };
+                this.prepare_for_native_call(alloc_id, prov)?;
+            }
         }
 
+        // FIXME: In the future, we should also call `prepare_for_native_call` on all previously
+        // exposed allocations, since C may access any of them.
+
         // Convert them to `libffi::high::Arg` type.
         let libffi_args = libffi_args
             .iter()
@@ -220,7 +244,7 @@ impl<'a> CArg {
 
 /// 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> {
+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.
@@ -238,18 +262,10 @@ fn imm_to_carg<'tcx>(v: ImmTy<'tcx>, cx: &impl HasDataLayout) -> InterpResult<'t
         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(_, mutability) => {
-            // Arbitrary mutable pointer accesses are not currently supported in Miri.
-            if mutability.is_mut() {
-                throw_unsup_format!(
-                    "unsupported mutable pointer type for native call: {}",
-                    v.layout.ty
-                );
-            } else {
-                let s = v.to_scalar().to_pointer(cx)?.addr();
-                // This relies on the `expose_provenance` in `addr_from_alloc_id`.
-                CArg::RawPtr(std::ptr::with_exposed_provenance_mut(s.bytes_usize()))
-            }
+        ty::RawPtr(..) => {
+            let s = v.to_scalar().to_pointer(cx)?.addr();
+            // This relies on the `expose_provenance` in `addr_from_alloc_id`.
+            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/panic.rs b/src/tools/miri/src/shims/panic.rs
index 722c3a2f0c5..93479540009 100644
--- a/src/tools/miri/src/shims/panic.rs
+++ b/src/tools/miri/src/shims/panic.rs
@@ -12,7 +12,6 @@
 //!   metadata we remembered when pushing said frame.
 
 use rustc_abi::ExternAbi;
-use rustc_ast::Mutability;
 use rustc_middle::{mir, ty};
 use rustc_target::spec::PanicStrategy;
 
@@ -161,7 +160,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let this = self.eval_context_mut();
 
         // First arg: message.
-        let msg = this.allocate_str(msg, MiriMemoryKind::Machine.into(), Mutability::Not)?;
+        let msg = this.allocate_str_dedup(msg)?;
 
         // Call the lang item.
         let panic = this.tcx.lang_items().panic_fn().unwrap();
@@ -180,7 +179,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let this = self.eval_context_mut();
 
         // First arg: message.
-        let msg = this.allocate_str(msg, MiriMemoryKind::Machine.into(), Mutability::Not)?;
+        let msg = this.allocate_str_dedup(msg)?;
 
         // Call the lang item.
         let panic = this.tcx.lang_items().panic_nounwind().unwrap();
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 80ad40e1624..f9003885450 100644
--- a/src/tools/miri/src/shims/unix/android/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/android/foreign_items.rs
@@ -2,7 +2,9 @@ use rustc_abi::ExternAbi;
 use rustc_span::Symbol;
 
 use crate::shims::unix::android::thread::prctl;
-use crate::shims::unix::linux::syscall::syscall;
+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 {
@@ -20,6 +22,31 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     ) -> InterpResult<'tcx, EmulateItemResult> {
         let this = self.eval_context_mut();
         match link_name.as_str() {
+            // epoll, eventfd
+            "epoll_create1" => {
+                let [flag] =
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
+                let result = this.epoll_create1(flag)?;
+                this.write_scalar(result, dest)?;
+            }
+            "epoll_ctl" => {
+                let [epfd, op, fd, event] =
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
+                let result = this.epoll_ctl(epfd, op, fd, event)?;
+                this.write_scalar(result, dest)?;
+            }
+            "epoll_wait" => {
+                let [epfd, events, maxevents, timeout] =
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
+                this.epoll_wait(epfd, events, maxevents, timeout, dest)?;
+            }
+            "eventfd" => {
+                let [val, flag] =
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
+                let result = this.eventfd(val, flag)?;
+                this.write_scalar(result, dest)?;
+            }
+
             // Miscellaneous
             "__errno" => {
                 let [] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs
index 27bdd508f77..e5dead1a263 100644
--- a/src/tools/miri/src/shims/unix/fd.rs
+++ b/src/tools/miri/src/shims/unix/fd.rs
@@ -1,16 +1,14 @@
 //! General management of file descriptors, and support for
 //! standard file descriptors (stdin/stdout/stderr).
 
-use std::any::Any;
-use std::collections::BTreeMap;
-use std::io::{self, ErrorKind, IsTerminal, Read, SeekFrom, Write};
-use std::ops::Deref;
-use std::rc::{Rc, Weak};
+use std::io;
+use std::io::ErrorKind;
 
 use rustc_abi::Size;
 
 use crate::helpers::check_min_arg_count;
-use crate::shims::unix::linux::epoll::EpollReadyEvents;
+use crate::shims::files::FileDescription;
+use crate::shims::unix::linux_like::epoll::EpollReadyEvents;
 use crate::shims::unix::*;
 use crate::*;
 
@@ -21,40 +19,8 @@ pub(crate) enum FlockOp {
     Unlock,
 }
 
-/// Represents an open file description.
-pub trait FileDescription: std::fmt::Debug + Any {
-    fn name(&self) -> &'static str;
-
-    /// Reads as much as possible into the given buffer `ptr`.
-    /// `len` indicates how many bytes we should try to read.
-    /// `dest` is where the return value should be stored: number of bytes read, or `-1` in case of error.
-    fn read<'tcx>(
-        &self,
-        _self_ref: &FileDescriptionRef,
-        _communicate_allowed: bool,
-        _ptr: Pointer,
-        _len: usize,
-        _dest: &MPlaceTy<'tcx>,
-        _ecx: &mut MiriInterpCx<'tcx>,
-    ) -> InterpResult<'tcx> {
-        throw_unsup_format!("cannot read from {}", self.name());
-    }
-
-    /// Writes as much as possible from the given buffer `ptr`.
-    /// `len` indicates how many bytes we should try to write.
-    /// `dest` is where the return value should be stored: number of bytes written, or `-1` in case of error.
-    fn write<'tcx>(
-        &self,
-        _self_ref: &FileDescriptionRef,
-        _communicate_allowed: bool,
-        _ptr: Pointer,
-        _len: usize,
-        _dest: &MPlaceTy<'tcx>,
-        _ecx: &mut MiriInterpCx<'tcx>,
-    ) -> InterpResult<'tcx> {
-        throw_unsup_format!("cannot write to {}", self.name());
-    }
-
+/// Represents unix-specific file descriptions.
+pub trait UnixFileDescription: FileDescription {
     /// Reads as much as possible into the given buffer `ptr` from a given offset.
     /// `len` indicates how many bytes we should try to read.
     /// `dest` is where the return value should be stored: number of bytes read, or `-1` in case of error.
@@ -86,24 +52,6 @@ pub trait FileDescription: std::fmt::Debug + Any {
         throw_unsup_format!("cannot pwrite to {}", self.name());
     }
 
-    /// Seeks to the given offset (which can be relative to the beginning, end, or current position).
-    /// Returns the new position from the start of the stream.
-    fn seek<'tcx>(
-        &self,
-        _communicate_allowed: bool,
-        _offset: SeekFrom,
-    ) -> InterpResult<'tcx, io::Result<u64>> {
-        throw_unsup_format!("cannot seek on {}", self.name());
-    }
-
-    fn close<'tcx>(
-        self: Box<Self>,
-        _communicate_allowed: bool,
-        _ecx: &mut MiriInterpCx<'tcx>,
-    ) -> InterpResult<'tcx, io::Result<()>> {
-        throw_unsup_format!("cannot close {}", self.name());
-    }
-
     fn flock<'tcx>(
         &self,
         _communicate_allowed: bool,
@@ -112,311 +60,12 @@ pub trait FileDescription: std::fmt::Debug + Any {
         throw_unsup_format!("cannot flock {}", self.name());
     }
 
-    fn is_tty(&self, _communicate_allowed: bool) -> bool {
-        // Most FDs are not tty's and the consequence of a wrong `false` are minor,
-        // so we use a default impl here.
-        false
-    }
-
     /// Check the readiness of file description.
     fn get_epoll_ready_events<'tcx>(&self) -> InterpResult<'tcx, EpollReadyEvents> {
         throw_unsup_format!("{}: epoll does not support this file description", self.name());
     }
 }
 
-impl dyn FileDescription {
-    #[inline(always)]
-    pub fn downcast<T: Any>(&self) -> Option<&T> {
-        (self as &dyn Any).downcast_ref()
-    }
-}
-
-impl FileDescription for io::Stdin {
-    fn name(&self) -> &'static str {
-        "stdin"
-    }
-
-    fn read<'tcx>(
-        &self,
-        _self_ref: &FileDescriptionRef,
-        communicate_allowed: bool,
-        ptr: Pointer,
-        len: usize,
-        dest: &MPlaceTy<'tcx>,
-        ecx: &mut MiriInterpCx<'tcx>,
-    ) -> InterpResult<'tcx> {
-        let mut bytes = vec![0; len];
-        if !communicate_allowed {
-            // We want isolation mode to be deterministic, so we have to disallow all reads, even stdin.
-            helpers::isolation_abort_error("`read` from stdin")?;
-        }
-        let result = Read::read(&mut { self }, &mut bytes);
-        match result {
-            Ok(read_size) => ecx.return_read_success(ptr, &bytes, read_size, dest),
-            Err(e) => ecx.set_last_error_and_return(e, dest),
-        }
-    }
-
-    fn is_tty(&self, communicate_allowed: bool) -> bool {
-        communicate_allowed && self.is_terminal()
-    }
-}
-
-impl FileDescription for io::Stdout {
-    fn name(&self) -> &'static str {
-        "stdout"
-    }
-
-    fn write<'tcx>(
-        &self,
-        _self_ref: &FileDescriptionRef,
-        _communicate_allowed: bool,
-        ptr: Pointer,
-        len: usize,
-        dest: &MPlaceTy<'tcx>,
-        ecx: &mut MiriInterpCx<'tcx>,
-    ) -> InterpResult<'tcx> {
-        let bytes = ecx.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
-        // We allow writing to stderr even with isolation enabled.
-        let result = Write::write(&mut { self }, bytes);
-        // Stdout is buffered, flush to make sure it appears on the
-        // screen.  This is the write() syscall of the interpreted
-        // program, we want it to correspond to a write() syscall on
-        // the host -- there is no good in adding extra buffering
-        // here.
-        io::stdout().flush().unwrap();
-        match result {
-            Ok(write_size) => ecx.return_write_success(write_size, dest),
-            Err(e) => ecx.set_last_error_and_return(e, dest),
-        }
-    }
-
-    fn is_tty(&self, communicate_allowed: bool) -> bool {
-        communicate_allowed && self.is_terminal()
-    }
-}
-
-impl FileDescription for io::Stderr {
-    fn name(&self) -> &'static str {
-        "stderr"
-    }
-
-    fn write<'tcx>(
-        &self,
-        _self_ref: &FileDescriptionRef,
-        _communicate_allowed: bool,
-        ptr: Pointer,
-        len: usize,
-        dest: &MPlaceTy<'tcx>,
-        ecx: &mut MiriInterpCx<'tcx>,
-    ) -> InterpResult<'tcx> {
-        let bytes = ecx.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
-        // We allow writing to stderr even with isolation enabled.
-        // No need to flush, stderr is not buffered.
-        let result = Write::write(&mut { self }, bytes);
-        match result {
-            Ok(write_size) => ecx.return_write_success(write_size, dest),
-            Err(e) => ecx.set_last_error_and_return(e, dest),
-        }
-    }
-
-    fn is_tty(&self, communicate_allowed: bool) -> bool {
-        communicate_allowed && self.is_terminal()
-    }
-}
-
-/// Like /dev/null
-#[derive(Debug)]
-pub struct NullOutput;
-
-impl FileDescription for NullOutput {
-    fn name(&self) -> &'static str {
-        "stderr and stdout"
-    }
-
-    fn write<'tcx>(
-        &self,
-        _self_ref: &FileDescriptionRef,
-        _communicate_allowed: bool,
-        _ptr: Pointer,
-        len: usize,
-        dest: &MPlaceTy<'tcx>,
-        ecx: &mut MiriInterpCx<'tcx>,
-    ) -> InterpResult<'tcx> {
-        // We just don't write anything, but report to the user that we did.
-        ecx.return_write_success(len, dest)
-    }
-}
-
-/// Structure contains both the file description and its unique identifier.
-#[derive(Clone, Debug)]
-pub struct FileDescWithId<T: FileDescription + ?Sized> {
-    id: FdId,
-    file_description: Box<T>,
-}
-
-#[derive(Clone, Debug)]
-pub struct FileDescriptionRef(Rc<FileDescWithId<dyn FileDescription>>);
-
-impl Deref for FileDescriptionRef {
-    type Target = dyn FileDescription;
-
-    fn deref(&self) -> &Self::Target {
-        &*self.0.file_description
-    }
-}
-
-impl FileDescriptionRef {
-    fn new(fd: impl FileDescription, id: FdId) -> Self {
-        FileDescriptionRef(Rc::new(FileDescWithId { id, file_description: Box::new(fd) }))
-    }
-
-    pub fn close<'tcx>(
-        self,
-        communicate_allowed: bool,
-        ecx: &mut MiriInterpCx<'tcx>,
-    ) -> InterpResult<'tcx, io::Result<()>> {
-        // Destroy this `Rc` using `into_inner` so we can call `close` instead of
-        // implicitly running the destructor of the file description.
-        let id = self.get_id();
-        match Rc::into_inner(self.0) {
-            Some(fd) => {
-                // Remove entry from the global epoll_event_interest table.
-                ecx.machine.epoll_interests.remove(id);
-
-                fd.file_description.close(communicate_allowed, ecx)
-            }
-            None => interp_ok(Ok(())),
-        }
-    }
-
-    pub fn downgrade(&self) -> WeakFileDescriptionRef {
-        WeakFileDescriptionRef { weak_ref: Rc::downgrade(&self.0) }
-    }
-
-    pub fn get_id(&self) -> FdId {
-        self.0.id
-    }
-}
-
-/// Holds a weak reference to the actual file description.
-#[derive(Clone, Debug, Default)]
-pub struct WeakFileDescriptionRef {
-    weak_ref: Weak<FileDescWithId<dyn FileDescription>>,
-}
-
-impl WeakFileDescriptionRef {
-    pub fn upgrade(&self) -> Option<FileDescriptionRef> {
-        if let Some(file_desc_with_id) = self.weak_ref.upgrade() {
-            return Some(FileDescriptionRef(file_desc_with_id));
-        }
-        None
-    }
-}
-
-impl VisitProvenance for WeakFileDescriptionRef {
-    fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
-        // A weak reference can never be the only reference to some pointer or place.
-        // Since the actual file description is tracked by strong ref somewhere,
-        // it is ok to make this a NOP operation.
-    }
-}
-
-/// A unique id for file descriptions. While we could use the address, considering that
-/// is definitely unique, the address would expose interpreter internal state when used
-/// for sorting things. So instead we generate a unique id per file description that stays
-/// the same even if a file descriptor is duplicated and gets a new integer file descriptor.
-#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Ord, PartialOrd)]
-pub struct FdId(usize);
-
-/// The file descriptor table
-#[derive(Debug)]
-pub struct FdTable {
-    pub fds: BTreeMap<i32, FileDescriptionRef>,
-    /// Unique identifier for file description, used to differentiate between various file description.
-    next_file_description_id: FdId,
-}
-
-impl VisitProvenance for FdTable {
-    fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
-        // All our FileDescription instances do not have any tags.
-    }
-}
-
-impl FdTable {
-    fn new() -> Self {
-        FdTable { fds: BTreeMap::new(), next_file_description_id: FdId(0) }
-    }
-    pub(crate) fn init(mute_stdout_stderr: bool) -> FdTable {
-        let mut fds = FdTable::new();
-        fds.insert_new(io::stdin());
-        if mute_stdout_stderr {
-            assert_eq!(fds.insert_new(NullOutput), 1);
-            assert_eq!(fds.insert_new(NullOutput), 2);
-        } else {
-            assert_eq!(fds.insert_new(io::stdout()), 1);
-            assert_eq!(fds.insert_new(io::stderr()), 2);
-        }
-        fds
-    }
-
-    pub fn new_ref(&mut self, fd: impl FileDescription) -> FileDescriptionRef {
-        let file_handle = FileDescriptionRef::new(fd, self.next_file_description_id);
-        self.next_file_description_id = FdId(self.next_file_description_id.0.strict_add(1));
-        file_handle
-    }
-
-    /// Insert a new file description to the FdTable.
-    pub fn insert_new(&mut self, fd: impl FileDescription) -> i32 {
-        let fd_ref = self.new_ref(fd);
-        self.insert(fd_ref)
-    }
-
-    pub fn insert(&mut self, fd_ref: FileDescriptionRef) -> i32 {
-        self.insert_with_min_num(fd_ref, 0)
-    }
-
-    /// Insert a file description, giving it a file descriptor that is at least `min_fd_num`.
-    fn insert_with_min_num(&mut self, file_handle: FileDescriptionRef, min_fd_num: i32) -> i32 {
-        // Find the lowest unused FD, starting from min_fd. If the first such unused FD is in
-        // between used FDs, the find_map combinator will return it. If the first such unused FD
-        // is after all other used FDs, the find_map combinator will return None, and we will use
-        // the FD following the greatest FD thus far.
-        let candidate_new_fd =
-            self.fds.range(min_fd_num..).zip(min_fd_num..).find_map(|((fd_num, _fd), counter)| {
-                if *fd_num != counter {
-                    // There was a gap in the fds stored, return the first unused one
-                    // (note that this relies on BTreeMap iterating in key order)
-                    Some(counter)
-                } else {
-                    // This fd is used, keep going
-                    None
-                }
-            });
-        let new_fd_num = candidate_new_fd.unwrap_or_else(|| {
-            // find_map ran out of BTreeMap entries before finding a free fd, use one plus the
-            // maximum fd in the map
-            self.fds.last_key_value().map(|(fd_num, _)| fd_num.strict_add(1)).unwrap_or(min_fd_num)
-        });
-
-        self.fds.try_insert(new_fd_num, file_handle).unwrap();
-        new_fd_num
-    }
-
-    pub fn get(&self, fd_num: i32) -> Option<FileDescriptionRef> {
-        let fd = self.fds.get(&fd_num)?;
-        Some(fd.clone())
-    }
-
-    pub fn remove(&mut self, fd_num: i32) -> Option<FileDescriptionRef> {
-        self.fds.remove(&fd_num)
-    }
-
-    pub fn is_fd_num(&self, fd_num: i32) -> bool {
-        self.fds.contains_key(&fd_num)
-    }
-}
-
 impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
 pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn dup(&mut self, old_fd_num: i32) -> InterpResult<'tcx, Scalar> {
@@ -472,7 +121,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             throw_unsup_format!("unsupported flags {:#x}", op);
         };
 
-        let result = fd.flock(this.machine.communicate(), parsed_op)?;
+        let result = fd.as_unix().flock(this.machine.communicate(), parsed_op)?;
         drop(fd);
         // return `0` if flock is successful
         let result = result.map(|()| 0i32);
@@ -602,7 +251,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 let Ok(offset) = u64::try_from(offset) else {
                     return this.set_last_error_and_return(LibcError("EINVAL"), dest);
                 };
-                fd.pread(communicate, offset, buf, count, dest, this)?
+                fd.as_unix().pread(communicate, offset, buf, count, dest, this)?
             }
         };
         interp_ok(())
@@ -642,46 +291,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 let Ok(offset) = u64::try_from(offset) else {
                     return this.set_last_error_and_return(LibcError("EINVAL"), dest);
                 };
-                fd.pwrite(communicate, buf, count, offset, dest, this)?
+                fd.as_unix().pwrite(communicate, buf, count, offset, dest, this)?
             }
         };
         interp_ok(())
     }
-
-    /// Helper to implement `FileDescription::read`:
-    /// This is only used when `read` is successful.
-    /// `actual_read_size` should be the return value of some underlying `read` call that used
-    /// `bytes` as its output buffer.
-    /// The length of `bytes` must not exceed either the host's or the target's `isize`.
-    /// `bytes` is written to `buf` and the size is written to `dest`.
-    fn return_read_success(
-        &mut self,
-        buf: Pointer,
-        bytes: &[u8],
-        actual_read_size: usize,
-        dest: &MPlaceTy<'tcx>,
-    ) -> InterpResult<'tcx> {
-        let this = self.eval_context_mut();
-        // If reading to `bytes` did not fail, we write those bytes to the buffer.
-        // Crucially, if fewer than `bytes.len()` bytes were read, only write
-        // that much into the output buffer!
-        this.write_bytes_ptr(buf, bytes[..actual_read_size].iter().copied())?;
-
-        // The actual read size is always less than what got originally requested so this cannot fail.
-        this.write_int(u64::try_from(actual_read_size).unwrap(), dest)?;
-        interp_ok(())
-    }
-
-    /// Helper to implement `FileDescription::write`:
-    /// This function is only used when `write` is successful, and writes `actual_write_size` to `dest`
-    fn return_write_success(
-        &mut self,
-        actual_write_size: usize,
-        dest: &MPlaceTy<'tcx>,
-    ) -> InterpResult<'tcx> {
-        let this = self.eval_context_mut();
-        // The actual write size is always less than what got originally requested so this cannot fail.
-        this.write_int(u64::try_from(actual_write_size).unwrap(), dest)?;
-        interp_ok(())
-    }
 }
diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs
index 55202a08149..88ec32808b1 100644
--- a/src/tools/miri/src/shims/unix/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/foreign_items.rs
@@ -39,6 +39,64 @@ pub fn is_dyn_sym(name: &str, target_os: &str) -> bool {
 
 impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
 pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
+    // Querying system information
+    fn sysconf(&mut self, val: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
+        let this = self.eval_context_mut();
+
+        let name = this.read_scalar(val)?.to_i32()?;
+        // FIXME: Which of these are POSIX, and which are GNU/Linux?
+        // At least the names seem to all also exist on macOS.
+        let sysconfs: &[(&str, fn(&MiriInterpCx<'_>) -> Scalar)] = &[
+            ("_SC_PAGESIZE", |this| Scalar::from_int(this.machine.page_size, this.pointer_size())),
+            ("_SC_PAGE_SIZE", |this| Scalar::from_int(this.machine.page_size, this.pointer_size())),
+            ("_SC_NPROCESSORS_CONF", |this| {
+                Scalar::from_int(this.machine.num_cpus, this.pointer_size())
+            }),
+            ("_SC_NPROCESSORS_ONLN", |this| {
+                Scalar::from_int(this.machine.num_cpus, this.pointer_size())
+            }),
+            // 512 seems to be a reasonable default. The value is not critical, in
+            // the sense that getpwuid_r takes and checks the buffer length.
+            ("_SC_GETPW_R_SIZE_MAX", |this| Scalar::from_int(512, this.pointer_size())),
+            // Miri doesn't have a fixed limit on FDs, but we may be limited in terms of how
+            // many *host* FDs we can open. Just use some arbitrary, pretty big value;
+            // this can be adjusted if it causes problems.
+            // The spec imposes a minimum of `_POSIX_OPEN_MAX` (20).
+            ("_SC_OPEN_MAX", |this| Scalar::from_int(2_i32.pow(16), this.pointer_size())),
+        ];
+        for &(sysconf_name, value) in sysconfs {
+            let sysconf_name = this.eval_libc_i32(sysconf_name);
+            if sysconf_name == name {
+                return interp_ok(value(this));
+            }
+        }
+        throw_unsup_format!("unimplemented sysconf name: {}", name)
+    }
+
+    fn strerror_r(
+        &mut self,
+        errnum: &OpTy<'tcx>,
+        buf: &OpTy<'tcx>,
+        buflen: &OpTy<'tcx>,
+    ) -> InterpResult<'tcx, Scalar> {
+        let this = self.eval_context_mut();
+
+        let errnum = this.read_scalar(errnum)?;
+        let buf = this.read_pointer(buf)?;
+        let buflen = this.read_target_usize(buflen)?;
+        let error = this.try_errnum_to_io_error(errnum)?;
+        let formatted = match error {
+            Some(err) => format!("{err}"),
+            None => format!("<unknown errnum in strerror_r: {errnum}>"),
+        };
+        let (complete, _) = this.write_os_str_to_c_str(OsStr::new(&formatted), buf, buflen)?;
+        if complete {
+            interp_ok(Scalar::from_i32(0))
+        } else {
+            interp_ok(Scalar::from_i32(this.eval_libc_i32("ERANGE")))
+        }
+    }
+
     fn emulate_foreign_item_inner(
         &mut self,
         link_name: Symbol,
@@ -84,6 +142,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 this.write_scalar(result, dest)?;
             }
 
+            "sysconf" => {
+                let [val] =
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
+                let result = this.sysconf(val)?;
+                this.write_scalar(result, dest)?;
+            }
+
             // File descriptors
             "read" => {
                 let [fd, buf, count] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
@@ -393,35 +458,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 }
             }
 
-            // Querying system information
-            "sysconf" => {
-                let [name] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
-                let name = this.read_scalar(name)?.to_i32()?;
-                // FIXME: Which of these are POSIX, and which are GNU/Linux?
-                // At least the names seem to all also exist on macOS.
-                let sysconfs: &[(&str, fn(&MiriInterpCx<'_>) -> Scalar)] = &[
-                    ("_SC_PAGESIZE", |this| Scalar::from_int(this.machine.page_size, this.pointer_size())),
-                    ("_SC_NPROCESSORS_CONF", |this| Scalar::from_int(this.machine.num_cpus, this.pointer_size())),
-                    ("_SC_NPROCESSORS_ONLN", |this| Scalar::from_int(this.machine.num_cpus, this.pointer_size())),
-                    // 512 seems to be a reasonable default. The value is not critical, in
-                    // the sense that getpwuid_r takes and checks the buffer length.
-                    ("_SC_GETPW_R_SIZE_MAX", |this| Scalar::from_int(512, this.pointer_size()))
-                ];
-                let mut result = None;
-                for &(sysconf_name, value) in sysconfs {
-                    let sysconf_name = this.eval_libc_i32(sysconf_name);
-                    if sysconf_name == name {
-                        result = Some(value(this));
-                        break;
-                    }
-                }
-                if let Some(result) = result {
-                    this.write_scalar(result, dest)?;
-                } else {
-                    throw_unsup_format!("unimplemented sysconf name: {}", name)
-                }
-            }
-
             // Thread-local storage
             "pthread_key_create" => {
                 let [key, dtor] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
@@ -724,21 +760,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 // We do not support forking, so there is nothing to do here.
                 this.write_null(dest)?;
             }
-            "strerror_r" | "__xpg_strerror_r" => {
-                let [errnum, buf, buflen] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
-                let errnum = this.read_scalar(errnum)?;
-                let buf = this.read_pointer(buf)?;
-                let buflen = this.read_target_usize(buflen)?;
-
-                let error = this.try_errnum_to_io_error(errnum)?;
-                let formatted = match error {
-                    Some(err) => format!("{err}"),
-                    None => format!("<unknown errnum in strerror_r: {errnum}>"),
-                };
-                let (complete, _) = this.write_os_str_to_c_str(OsStr::new(&formatted), buf, buflen)?;
-                let ret = if complete { 0 } else { this.eval_libc_i32("ERANGE") };
-                this.write_int(ret, dest)?;
-            }
             "getentropy" => {
                 // This function is non-standard but exists with the same signature and behavior on
                 // Linux, macOS, FreeBSD and Solaris/Illumos.
@@ -766,6 +787,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     this.write_null(dest)?;
                 }
             }
+
+            "strerror_r" => {
+                let [errnum, buf, buflen] =
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
+                let result = this.strerror_r(errnum, buf, buflen)?;
+                this.write_scalar(result, dest)?;
+            }
+
             "getrandom" => {
                 // This function is non-standard but exists with the same signature and behavior on
                 // Linux, FreeBSD and Solaris/Illumos.
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 1346d8de7ea..0c9bc005ece 100644
--- a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs
@@ -53,19 +53,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             "stat" | "stat@FBSD_1.0" => {
                 let [path, buf] =
                     this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
-                let result = this.macos_fbsd_stat(path, buf)?;
+                let result = this.macos_fbsd_solaris_stat(path, buf)?;
                 this.write_scalar(result, dest)?;
             }
             "lstat" | "lstat@FBSD_1.0" => {
                 let [path, buf] =
                     this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
-                let result = this.macos_fbsd_lstat(path, buf)?;
+                let result = this.macos_fbsd_solaris_lstat(path, buf)?;
                 this.write_scalar(result, dest)?;
             }
             "fstat" | "fstat@FBSD_1.0" => {
                 let [fd, buf] =
                     this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
-                let result = this.macos_fbsd_fstat(fd, buf)?;
+                let result = this.macos_fbsd_solaris_fstat(fd, buf)?;
                 this.write_scalar(result, dest)?;
             }
             "readdir_r" | "readdir_r@FBSD_1.0" => {
diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs
index 091def7ac65..b41a4d2246f 100644
--- a/src/tools/miri/src/shims/unix/fs.rs
+++ b/src/tools/miri/src/shims/unix/fs.rs
@@ -2,7 +2,8 @@
 
 use std::borrow::Cow;
 use std::fs::{
-    DirBuilder, File, FileType, OpenOptions, ReadDir, read_dir, remove_dir, remove_file, rename,
+    DirBuilder, File, FileType, Metadata, OpenOptions, ReadDir, read_dir, remove_dir, remove_file,
+    rename,
 };
 use std::io::{self, ErrorKind, IsTerminal, Read, Seek, SeekFrom, Write};
 use std::path::{Path, PathBuf};
@@ -11,12 +12,11 @@ use std::time::SystemTime;
 use rustc_abi::Size;
 use rustc_data_structures::fx::FxHashMap;
 
-use self::fd::FlockOp;
 use self::shims::time::system_time_to_duration;
 use crate::helpers::check_min_arg_count;
+use crate::shims::files::{EvalContextExt as _, FileDescription, FileDescriptionRef};
 use crate::shims::os_str::bytes_to_os_str;
-use crate::shims::unix::fd::FileDescriptionRef;
-use crate::shims::unix::*;
+use crate::shims::unix::fd::{FlockOp, UnixFileDescription};
 use crate::*;
 
 #[derive(Debug)]
@@ -66,6 +66,55 @@ impl FileDescription for FileHandle {
         }
     }
 
+    fn seek<'tcx>(
+        &self,
+        communicate_allowed: bool,
+        offset: SeekFrom,
+    ) -> InterpResult<'tcx, io::Result<u64>> {
+        assert!(communicate_allowed, "isolation should have prevented even opening a file");
+        interp_ok((&mut &self.file).seek(offset))
+    }
+
+    fn close<'tcx>(
+        self: Box<Self>,
+        communicate_allowed: bool,
+        _ecx: &mut MiriInterpCx<'tcx>,
+    ) -> InterpResult<'tcx, io::Result<()>> {
+        assert!(communicate_allowed, "isolation should have prevented even opening a file");
+        // We sync the file if it was opened in a mode different than read-only.
+        if self.writable {
+            // `File::sync_all` does the checks that are done when closing a file. We do this to
+            // to handle possible errors correctly.
+            let result = self.file.sync_all();
+            // Now we actually close the file and return the result.
+            drop(*self);
+            interp_ok(result)
+        } else {
+            // We drop the file, this closes it but ignores any errors
+            // produced when closing it. This is done because
+            // `File::sync_all` cannot be done over files like
+            // `/dev/urandom` which are read-only. Check
+            // https://github.com/rust-lang/miri/issues/999#issuecomment-568920439
+            // for a deeper discussion.
+            drop(*self);
+            interp_ok(Ok(()))
+        }
+    }
+
+    fn metadata<'tcx>(&self) -> InterpResult<'tcx, io::Result<Metadata>> {
+        interp_ok(self.file.metadata())
+    }
+
+    fn is_tty(&self, communicate_allowed: bool) -> bool {
+        communicate_allowed && self.file.is_terminal()
+    }
+
+    fn as_unix(&self) -> &dyn UnixFileDescription {
+        self
+    }
+}
+
+impl UnixFileDescription for FileHandle {
     fn pread<'tcx>(
         &self,
         communicate_allowed: bool,
@@ -128,41 +177,6 @@ impl FileDescription for FileHandle {
         }
     }
 
-    fn seek<'tcx>(
-        &self,
-        communicate_allowed: bool,
-        offset: SeekFrom,
-    ) -> InterpResult<'tcx, io::Result<u64>> {
-        assert!(communicate_allowed, "isolation should have prevented even opening a file");
-        interp_ok((&mut &self.file).seek(offset))
-    }
-
-    fn close<'tcx>(
-        self: Box<Self>,
-        communicate_allowed: bool,
-        _ecx: &mut MiriInterpCx<'tcx>,
-    ) -> InterpResult<'tcx, io::Result<()>> {
-        assert!(communicate_allowed, "isolation should have prevented even opening a file");
-        // We sync the file if it was opened in a mode different than read-only.
-        if self.writable {
-            // `File::sync_all` does the checks that are done when closing a file. We do this to
-            // to handle possible errors correctly.
-            let result = self.file.sync_all();
-            // Now we actually close the file and return the result.
-            drop(*self);
-            interp_ok(result)
-        } else {
-            // We drop the file, this closes it but ignores any errors
-            // produced when closing it. This is done because
-            // `File::sync_all` cannot be done over files like
-            // `/dev/urandom` which are read-only. Check
-            // https://github.com/rust-lang/miri/issues/999#issuecomment-568920439
-            // for a deeper discussion.
-            drop(*self);
-            interp_ok(Ok(()))
-        }
-    }
-
     fn flock<'tcx>(
         &self,
         communicate_allowed: bool,
@@ -257,55 +271,63 @@ impl FileDescription for FileHandle {
             compile_error!("flock is supported only on UNIX and Windows hosts");
         }
     }
-
-    fn is_tty(&self, communicate_allowed: bool) -> bool {
-        communicate_allowed && self.file.is_terminal()
-    }
 }
 
 impl<'tcx> EvalContextExtPrivate<'tcx> for crate::MiriInterpCx<'tcx> {}
 trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> {
-    fn macos_stat_write_buf(
+    fn macos_fbsd_solaris_write_buf(
         &mut self,
         metadata: FileMetadata,
         buf_op: &OpTy<'tcx>,
     ) -> InterpResult<'tcx, i32> {
         let this = self.eval_context_mut();
 
-        let mode: u16 = metadata.mode.to_u16()?;
-
         let (access_sec, access_nsec) = metadata.accessed.unwrap_or((0, 0));
         let (created_sec, created_nsec) = metadata.created.unwrap_or((0, 0));
         let (modified_sec, modified_nsec) = metadata.modified.unwrap_or((0, 0));
+        let mode = metadata.mode.to_uint(this.libc_ty_layout("mode_t").size)?;
 
         let buf = this.deref_pointer_as(buf_op, this.libc_ty_layout("stat"))?;
-
         this.write_int_fields_named(
             &[
                 ("st_dev", 0),
-                ("st_mode", mode.into()),
+                ("st_mode", mode.try_into().unwrap()),
                 ("st_nlink", 0),
                 ("st_ino", 0),
                 ("st_uid", 0),
                 ("st_gid", 0),
                 ("st_rdev", 0),
                 ("st_atime", access_sec.into()),
-                ("st_atime_nsec", access_nsec.into()),
                 ("st_mtime", modified_sec.into()),
-                ("st_mtime_nsec", modified_nsec.into()),
                 ("st_ctime", 0),
-                ("st_ctime_nsec", 0),
-                ("st_birthtime", created_sec.into()),
-                ("st_birthtime_nsec", created_nsec.into()),
                 ("st_size", metadata.size.into()),
                 ("st_blocks", 0),
                 ("st_blksize", 0),
-                ("st_flags", 0),
-                ("st_gen", 0),
             ],
             &buf,
         )?;
 
+        if matches!(&*this.tcx.sess.target.os, "macos" | "freebsd") {
+            this.write_int_fields_named(
+                &[
+                    ("st_atime_nsec", access_nsec.into()),
+                    ("st_mtime_nsec", modified_nsec.into()),
+                    ("st_ctime_nsec", 0),
+                    ("st_birthtime", created_sec.into()),
+                    ("st_birthtime_nsec", created_nsec.into()),
+                    ("st_flags", 0),
+                    ("st_gen", 0),
+                ],
+                &buf,
+            )?;
+        }
+
+        if matches!(&*this.tcx.sess.target.os, "solaris" | "illumos") {
+            // FIXME: write st_fstype field once libc is updated.
+            // https://github.com/rust-lang/libc/pull/4145
+            //this.write_int_fields_named(&[("st_fstype", 0)], &buf)?;
+        }
+
         interp_ok(0)
     }
 
@@ -648,15 +670,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         interp_ok(Scalar::from_i32(this.try_unwrap_io_result(result)?))
     }
 
-    fn macos_fbsd_stat(
+    fn macos_fbsd_solaris_stat(
         &mut self,
         path_op: &OpTy<'tcx>,
         buf_op: &OpTy<'tcx>,
     ) -> InterpResult<'tcx, Scalar> {
         let this = self.eval_context_mut();
 
-        if !matches!(&*this.tcx.sess.target.os, "macos" | "freebsd") {
-            panic!("`macos_fbsd_stat` should not be called on {}", this.tcx.sess.target.os);
+        if !matches!(&*this.tcx.sess.target.os, "macos" | "freebsd" | "solaris" | "illumos") {
+            panic!("`macos_fbsd_solaris_stat` should not be called on {}", this.tcx.sess.target.os);
         }
 
         let path_scalar = this.read_pointer(path_op)?;
@@ -674,19 +696,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             Err(err) => return this.set_last_error_and_return_i32(err),
         };
 
-        interp_ok(Scalar::from_i32(this.macos_stat_write_buf(metadata, buf_op)?))
+        interp_ok(Scalar::from_i32(this.macos_fbsd_solaris_write_buf(metadata, buf_op)?))
     }
 
     // `lstat` is used to get symlink metadata.
-    fn macos_fbsd_lstat(
+    fn macos_fbsd_solaris_lstat(
         &mut self,
         path_op: &OpTy<'tcx>,
         buf_op: &OpTy<'tcx>,
     ) -> InterpResult<'tcx, Scalar> {
         let this = self.eval_context_mut();
 
-        if !matches!(&*this.tcx.sess.target.os, "macos" | "freebsd") {
-            panic!("`macos_fbsd_lstat` should not be called on {}", this.tcx.sess.target.os);
+        if !matches!(&*this.tcx.sess.target.os, "macos" | "freebsd" | "solaris" | "illumos") {
+            panic!(
+                "`macos_fbsd_solaris_lstat` should not be called on {}",
+                this.tcx.sess.target.os
+            );
         }
 
         let path_scalar = this.read_pointer(path_op)?;
@@ -703,18 +728,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             Err(err) => return this.set_last_error_and_return_i32(err),
         };
 
-        interp_ok(Scalar::from_i32(this.macos_stat_write_buf(metadata, buf_op)?))
+        interp_ok(Scalar::from_i32(this.macos_fbsd_solaris_write_buf(metadata, buf_op)?))
     }
 
-    fn macos_fbsd_fstat(
+    fn macos_fbsd_solaris_fstat(
         &mut self,
         fd_op: &OpTy<'tcx>,
         buf_op: &OpTy<'tcx>,
     ) -> InterpResult<'tcx, Scalar> {
         let this = self.eval_context_mut();
 
-        if !matches!(&*this.tcx.sess.target.os, "macos" | "freebsd") {
-            panic!("`macos_fbsd_fstat` should not be called on {}", this.tcx.sess.target.os);
+        if !matches!(&*this.tcx.sess.target.os, "macos" | "freebsd" | "solaris" | "illumos") {
+            panic!(
+                "`macos_fbsd_solaris_fstat` should not be called on {}",
+                this.tcx.sess.target.os
+            );
         }
 
         let fd = this.read_scalar(fd_op)?.to_i32()?;
@@ -730,7 +758,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             Ok(metadata) => metadata,
             Err(err) => return this.set_last_error_and_return_i32(err),
         };
-        interp_ok(Scalar::from_i32(this.macos_stat_write_buf(metadata, buf_op)?))
+        interp_ok(Scalar::from_i32(this.macos_fbsd_solaris_write_buf(metadata, buf_op)?))
     }
 
     fn linux_statx(
@@ -1675,16 +1703,7 @@ impl FileMetadata {
             return interp_ok(Err(LibcError("EBADF")));
         };
 
-        let file = &fd
-            .downcast::<FileHandle>()
-            .ok_or_else(|| {
-                err_unsup_format!(
-                    "obtaining metadata is only supported on file-backed file descriptors"
-                )
-            })?
-            .file;
-
-        let metadata = file.metadata();
+        let metadata = fd.metadata()?;
         drop(fd);
         FileMetadata::from_meta(ecx, metadata)
     }
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 85f0d6e1330..bc3619090c0 100644
--- a/src/tools/miri/src/shims/unix/linux/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/linux/foreign_items.rs
@@ -1,11 +1,12 @@
 use rustc_abi::ExternAbi;
 use rustc_span::Symbol;
 
-use self::shims::unix::linux::epoll::EvalContextExt as _;
-use self::shims::unix::linux::eventfd::EvalContextExt as _;
 use self::shims::unix::linux::mem::EvalContextExt as _;
-use self::shims::unix::linux::syscall::syscall;
+use self::shims::unix::linux_like::epoll::EvalContextExt as _;
+use self::shims::unix::linux_like::eventfd::EvalContextExt as _;
+use self::shims::unix::linux_like::syscall::syscall;
 use crate::machine::{SIGRTMAX, SIGRTMIN};
+use crate::shims::unix::foreign_items::EvalContextExt as _;
 use crate::shims::unix::*;
 use crate::*;
 
@@ -143,6 +144,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 let ptr = this.mremap(old_address, old_size, new_size, flags)?;
                 this.write_scalar(ptr, dest)?;
             }
+            "__xpg_strerror_r" => {
+                let [errnum, buf, buflen] =
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
+                let result = this.strerror_r(errnum, buf, buflen)?;
+                this.write_scalar(result, dest)?;
+            }
             "__errno_location" => {
                 let [] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let errno_place = this.last_error_place()?;
diff --git a/src/tools/miri/src/shims/unix/linux/mod.rs b/src/tools/miri/src/shims/unix/linux/mod.rs
index 159e5aca031..c10dc52cb28 100644
--- a/src/tools/miri/src/shims/unix/linux/mod.rs
+++ b/src/tools/miri/src/shims/unix/linux/mod.rs
@@ -1,6 +1,2 @@
-pub mod epoll;
-pub mod eventfd;
 pub mod foreign_items;
 pub mod mem;
-pub mod sync;
-pub mod syscall;
diff --git a/src/tools/miri/src/shims/unix/linux/epoll.rs b/src/tools/miri/src/shims/unix/linux_like/epoll.rs
index de108665e9f..5b240351c20 100644
--- a/src/tools/miri/src/shims/unix/linux/epoll.rs
+++ b/src/tools/miri/src/shims/unix/linux_like/epoll.rs
@@ -5,8 +5,8 @@ use std::rc::{Rc, Weak};
 use std::time::Duration;
 
 use crate::concurrency::VClock;
-use crate::shims::unix::fd::{FdId, FileDescriptionRef, WeakFileDescriptionRef};
-use crate::shims::unix::*;
+use crate::shims::files::{FdId, FileDescription, FileDescriptionRef, WeakFileDescriptionRef};
+use crate::shims::unix::UnixFileDescription;
 use crate::*;
 
 /// An `Epoll` file descriptor connects file handles and epoll events
@@ -152,8 +152,14 @@ impl FileDescription for Epoll {
     ) -> InterpResult<'tcx, io::Result<()>> {
         interp_ok(Ok(()))
     }
+
+    fn as_unix(&self) -> &dyn UnixFileDescription {
+        self
+    }
 }
 
+impl UnixFileDescription for Epoll {}
+
 /// The table of all EpollEventInterest.
 /// The BTreeMap key is the FdId of an active file description registered with
 /// any epoll instance. The value is a list of EpollEventInterest associated
@@ -539,7 +545,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     )?;
                     if is_updated {
                         // Edge-triggered notification only notify one thread even if there are
-                        // multiple threads block on the same epfd.
+                        // multiple threads blocked on the same epfd.
 
                         // This unwrap can never fail because if the current epoll instance were
                         // closed, the upgrade of weak_epoll_interest
@@ -595,7 +601,7 @@ fn check_and_update_one_event_interest<'tcx>(
     ecx: &MiriInterpCx<'tcx>,
 ) -> InterpResult<'tcx, bool> {
     // Get the bitmask of ready events for a file description.
-    let ready_events_bitmask = fd_ref.get_epoll_ready_events()?.get_event_bitmask(ecx);
+    let ready_events_bitmask = fd_ref.as_unix().get_epoll_ready_events()?.get_event_bitmask(ecx);
     let epoll_event_interest = interest.borrow();
     // This checks if any of the events specified in epoll_event_interest.events
     // match those in ready_events.
diff --git a/src/tools/miri/src/shims/unix/linux/eventfd.rs b/src/tools/miri/src/shims/unix/linux_like/eventfd.rs
index 63b7d37b13e..4bbe417ea8d 100644
--- a/src/tools/miri/src/shims/unix/linux/eventfd.rs
+++ b/src/tools/miri/src/shims/unix/linux_like/eventfd.rs
@@ -4,9 +4,9 @@ use std::io;
 use std::io::ErrorKind;
 
 use crate::concurrency::VClock;
-use crate::shims::unix::fd::{FileDescriptionRef, WeakFileDescriptionRef};
-use crate::shims::unix::linux::epoll::{EpollReadyEvents, EvalContextExt as _};
-use crate::shims::unix::*;
+use crate::shims::files::{FileDescription, FileDescriptionRef, WeakFileDescriptionRef};
+use crate::shims::unix::UnixFileDescription;
+use crate::shims::unix::linux_like::epoll::{EpollReadyEvents, EvalContextExt as _};
 use crate::*;
 
 /// Maximum value that the eventfd counter can hold.
@@ -37,17 +37,6 @@ impl FileDescription for Event {
         "event"
     }
 
-    fn get_epoll_ready_events<'tcx>(&self) -> InterpResult<'tcx, EpollReadyEvents> {
-        // We only check the status of EPOLLIN and EPOLLOUT flags for eventfd. If other event flags
-        // need to be supported in the future, the check should be added here.
-
-        interp_ok(EpollReadyEvents {
-            epollin: self.counter.get() != 0,
-            epollout: self.counter.get() != MAX_COUNTER,
-            ..EpollReadyEvents::new()
-        })
-    }
-
     fn close<'tcx>(
         self: Box<Self>,
         _communicate_allowed: bool,
@@ -121,6 +110,23 @@ impl FileDescription for Event {
         let weak_eventfd = self_ref.downgrade();
         eventfd_write(num, buf_place, dest, weak_eventfd, ecx)
     }
+
+    fn as_unix(&self) -> &dyn UnixFileDescription {
+        self
+    }
+}
+
+impl UnixFileDescription for Event {
+    fn get_epoll_ready_events<'tcx>(&self) -> InterpResult<'tcx, EpollReadyEvents> {
+        // We only check the status of EPOLLIN and EPOLLOUT flags for eventfd. If other event flags
+        // need to be supported in the future, the check should be added here.
+
+        interp_ok(EpollReadyEvents {
+            epollin: self.counter.get() != 0,
+            epollout: self.counter.get() != MAX_COUNTER,
+            ..EpollReadyEvents::new()
+        })
+    }
 }
 
 impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
@@ -144,9 +150,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn eventfd(&mut self, val: &OpTy<'tcx>, flags: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
         let this = self.eval_context_mut();
 
-        // eventfd is Linux specific.
-        this.assert_target_os("linux", "eventfd");
-
         let val = this.read_scalar(val)?.to_u32()?;
         let mut flags = this.read_scalar(flags)?.to_i32()?;
 
@@ -211,10 +214,10 @@ fn eventfd_write<'tcx>(
                 eventfd.clock.borrow_mut().join(clock);
             });
 
-            // When this function is called, the addition is guaranteed to not exceed u64::MAX - 1.
+            // Store new counter value.
             eventfd.counter.set(new_count);
 
-            // When any of the event happened, we check and update the status of all supported event
+            // The state changed; we check and update the status of all supported event
             // types for current file description.
             ecx.check_and_update_readiness(&eventfd_ref)?;
 
@@ -228,10 +231,11 @@ fn eventfd_write<'tcx>(
                 ecx.unblock_thread(thread_id, BlockReason::Eventfd)?;
             }
 
-            // Return how many bytes we wrote.
+            // Return how many bytes we consumed from the user-provided buffer.
             return ecx.write_int(buf_place.layout.size.bytes(), dest);
         }
         None | Some(u64::MAX) => {
+            // We can't update the state, so we have to block.
             if eventfd.is_nonblock {
                 return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest);
             }
@@ -251,6 +255,7 @@ fn eventfd_write<'tcx>(
                         weak_eventfd: WeakFileDescriptionRef,
                     }
                     @unblock = |this| {
+                        // When we get unblocked, try again.
                         eventfd_write(num, buf_place, &dest, weak_eventfd, this)
                     }
                 ),
@@ -276,9 +281,10 @@ fn eventfd_read<'tcx>(
     // an eventfd file description.
     let eventfd = eventfd_ref.downcast::<Event>().unwrap();
 
-    // Block when counter == 0.
+    // Set counter to 0, get old value.
     let counter = eventfd.counter.replace(0);
 
+    // Block when counter == 0.
     if counter == 0 {
         if eventfd.is_nonblock {
             return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest);
@@ -297,6 +303,7 @@ fn eventfd_read<'tcx>(
                     weak_eventfd: WeakFileDescriptionRef,
                 }
                 @unblock = |this| {
+                    // When we get unblocked, try again.
                     eventfd_read(buf_place, &dest, weak_eventfd, this)
                 }
             ),
@@ -305,10 +312,10 @@ fn eventfd_read<'tcx>(
         // Synchronize with all prior `write` calls to this FD.
         ecx.acquire_clock(&eventfd.clock.borrow());
 
-        // Give old counter value to userspace, and set counter value to 0.
+        // Return old counter value into user-space buffer.
         ecx.write_int(counter, &buf_place)?;
 
-        // When any of the events happened, we check and update the status of all supported event
+        // The state changed; we check and update the status of all supported event
         // types for current file description.
         ecx.check_and_update_readiness(&eventfd_ref)?;
 
@@ -322,7 +329,7 @@ fn eventfd_read<'tcx>(
             ecx.unblock_thread(thread_id, BlockReason::Eventfd)?;
         }
 
-        // Tell userspace how many bytes we read.
+        // Tell userspace how many bytes we put into the buffer.
         return ecx.write_int(buf_place.layout.size.bytes(), dest);
     }
     interp_ok(())
diff --git a/src/tools/miri/src/shims/unix/linux_like/mod.rs b/src/tools/miri/src/shims/unix/linux_like/mod.rs
new file mode 100644
index 00000000000..1a22539a4ee
--- /dev/null
+++ b/src/tools/miri/src/shims/unix/linux_like/mod.rs
@@ -0,0 +1,4 @@
+pub mod epoll;
+pub mod eventfd;
+pub mod sync;
+pub mod syscall;
diff --git a/src/tools/miri/src/shims/unix/linux/sync.rs b/src/tools/miri/src/shims/unix/linux_like/sync.rs
index 51124fb2a00..51124fb2a00 100644
--- a/src/tools/miri/src/shims/unix/linux/sync.rs
+++ b/src/tools/miri/src/shims/unix/linux_like/sync.rs
diff --git a/src/tools/miri/src/shims/unix/linux/syscall.rs b/src/tools/miri/src/shims/unix/linux_like/syscall.rs
index 9f6935f096b..e9a32a26326 100644
--- a/src/tools/miri/src/shims/unix/linux/syscall.rs
+++ b/src/tools/miri/src/shims/unix/linux_like/syscall.rs
@@ -1,9 +1,9 @@
 use rustc_abi::ExternAbi;
 use rustc_span::Symbol;
 
-use self::shims::unix::linux::eventfd::EvalContextExt as _;
 use crate::helpers::check_min_arg_count;
-use crate::shims::unix::linux::sync::futex;
+use crate::shims::unix::linux_like::eventfd::EvalContextExt as _;
+use crate::shims::unix::linux_like::sync::futex;
 use crate::*;
 
 pub fn syscall<'tcx>(
diff --git a/src/tools/miri/src/shims/unix/macos/foreign_items.rs b/src/tools/miri/src/shims/unix/macos/foreign_items.rs
index 003025916cd..103bea08620 100644
--- a/src/tools/miri/src/shims/unix/macos/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/macos/foreign_items.rs
@@ -40,19 +40,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             "stat" | "stat64" | "stat$INODE64" => {
                 let [path, buf] =
                     this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
-                let result = this.macos_fbsd_stat(path, buf)?;
+                let result = this.macos_fbsd_solaris_stat(path, buf)?;
                 this.write_scalar(result, dest)?;
             }
             "lstat" | "lstat64" | "lstat$INODE64" => {
                 let [path, buf] =
                     this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
-                let result = this.macos_fbsd_lstat(path, buf)?;
+                let result = this.macos_fbsd_solaris_lstat(path, buf)?;
                 this.write_scalar(result, dest)?;
             }
             "fstat" | "fstat64" | "fstat$INODE64" => {
                 let [fd, buf] =
                     this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
-                let result = this.macos_fbsd_fstat(fd, buf)?;
+                let result = this.macos_fbsd_solaris_fstat(fd, buf)?;
                 this.write_scalar(result, dest)?;
             }
             "opendir$INODE64" => {
diff --git a/src/tools/miri/src/shims/unix/mod.rs b/src/tools/miri/src/shims/unix/mod.rs
index c8c25c636ee..660e4ded5cd 100644
--- a/src/tools/miri/src/shims/unix/mod.rs
+++ b/src/tools/miri/src/shims/unix/mod.rs
@@ -10,15 +10,16 @@ mod unnamed_socket;
 
 mod android;
 mod freebsd;
-mod linux;
+pub mod linux;
+mod linux_like;
 mod macos;
 mod solarish;
 
 // All the Unix-specific extension traits
 pub use self::env::{EvalContextExt as _, UnixEnvVars};
-pub use self::fd::{EvalContextExt as _, FdTable, FileDescription};
+pub use self::fd::{EvalContextExt as _, UnixFileDescription};
 pub use self::fs::{DirTable, EvalContextExt as _};
-pub use self::linux::epoll::EpollInterestTable;
+pub use self::linux_like::epoll::EpollInterestTable;
 pub use self::mem::EvalContextExt as _;
 pub use self::sync::EvalContextExt as _;
 pub use self::thread::{EvalContextExt as _, ThreadNameResult};
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 526b64cff69..e4529170368 100644
--- a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs
@@ -1,6 +1,7 @@
 use rustc_abi::ExternAbi;
 use rustc_span::Symbol;
 
+use crate::shims::unix::foreign_items::EvalContextExt as _;
 use crate::shims::unix::*;
 use crate::*;
 
@@ -56,6 +57,26 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 this.write_scalar(res, dest)?;
             }
 
+            // File related shims
+            "stat" | "stat64" => {
+                let [path, buf] =
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
+                let result = this.macos_fbsd_solaris_stat(path, buf)?;
+                this.write_scalar(result, dest)?;
+            }
+            "lstat" | "lstat64" => {
+                let [path, buf] =
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
+                let result = this.macos_fbsd_solaris_lstat(path, buf)?;
+                this.write_scalar(result, dest)?;
+            }
+            "fstat" | "fstat64" => {
+                let [fd, buf] =
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
+                let result = this.macos_fbsd_solaris_fstat(fd, buf)?;
+                this.write_scalar(result, dest)?;
+            }
+
             // Miscellaneous
             "___errno" => {
                 let [] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
@@ -112,6 +133,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 this.write_null(dest)?;
             }
 
+            "__sysconf_xpg7" => {
+                let [val] =
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
+                let result = this.sysconf(val)?;
+                this.write_scalar(result, dest)?;
+            }
+
             _ => return interp_ok(EmulateItemResult::NotSupported),
         }
         interp_ok(EmulateItemResult::NeedsReturn)
diff --git a/src/tools/miri/src/shims/unix/unnamed_socket.rs b/src/tools/miri/src/shims/unix/unnamed_socket.rs
index 8ccce7c1986..40a76ea7439 100644
--- a/src/tools/miri/src/shims/unix/unnamed_socket.rs
+++ b/src/tools/miri/src/shims/unix/unnamed_socket.rs
@@ -10,9 +10,11 @@ use std::io::{ErrorKind, Read};
 use rustc_abi::Size;
 
 use crate::concurrency::VClock;
-use crate::shims::unix::fd::{FileDescriptionRef, WeakFileDescriptionRef};
-use crate::shims::unix::linux::epoll::{EpollReadyEvents, EvalContextExt as _};
-use crate::shims::unix::*;
+use crate::shims::files::{
+    EvalContextExt as _, FileDescription, FileDescriptionRef, WeakFileDescriptionRef,
+};
+use crate::shims::unix::UnixFileDescription;
+use crate::shims::unix::linux_like::epoll::{EpollReadyEvents, EvalContextExt as _};
 use crate::*;
 
 /// The maximum capacity of the socketpair buffer in bytes.
@@ -60,52 +62,6 @@ impl FileDescription for AnonSocket {
         "socketpair"
     }
 
-    fn get_epoll_ready_events<'tcx>(&self) -> InterpResult<'tcx, EpollReadyEvents> {
-        // We only check the status of EPOLLIN, EPOLLOUT, EPOLLHUP and EPOLLRDHUP flags.
-        // If other event flags need to be supported in the future, the check should be added here.
-
-        let mut epoll_ready_events = EpollReadyEvents::new();
-
-        // Check if it is readable.
-        if let Some(readbuf) = &self.readbuf {
-            if !readbuf.borrow().buf.is_empty() {
-                epoll_ready_events.epollin = true;
-            }
-        } else {
-            // Without a read buffer, reading never blocks, so we are always ready.
-            epoll_ready_events.epollin = true;
-        }
-
-        // Check if is writable.
-        if let Some(peer_fd) = self.peer_fd().upgrade() {
-            if let Some(writebuf) = &peer_fd.downcast::<AnonSocket>().unwrap().readbuf {
-                let data_size = writebuf.borrow().buf.len();
-                let available_space = MAX_SOCKETPAIR_BUFFER_CAPACITY.strict_sub(data_size);
-                if available_space != 0 {
-                    epoll_ready_events.epollout = true;
-                }
-            } else {
-                // Without a write buffer, writing never blocks.
-                epoll_ready_events.epollout = true;
-            }
-        } else {
-            // Peer FD has been closed. This always sets both the RDHUP and HUP flags
-            // as we do not support `shutdown` that could be used to partially close the stream.
-            epoll_ready_events.epollrdhup = true;
-            epoll_ready_events.epollhup = true;
-            // Since the peer is closed, even if no data is available reads will return EOF and
-            // writes will return EPIPE. In other words, they won't block, so we mark this as ready
-            // for read and write.
-            epoll_ready_events.epollin = true;
-            epoll_ready_events.epollout = true;
-            // If there is data lost in peer_fd, set EPOLLERR.
-            if self.peer_lost_data.get() {
-                epoll_ready_events.epollerr = true;
-            }
-        }
-        interp_ok(epoll_ready_events)
-    }
-
     fn close<'tcx>(
         self: Box<Self>,
         _communicate_allowed: bool,
@@ -134,11 +90,9 @@ impl FileDescription for AnonSocket {
         dest: &MPlaceTy<'tcx>,
         ecx: &mut MiriInterpCx<'tcx>,
     ) -> InterpResult<'tcx> {
-        let mut bytes = vec![0; len];
-
         // Always succeed on read size 0.
         if len == 0 {
-            return ecx.return_read_success(ptr, &bytes, 0, dest);
+            return ecx.return_read_success(ptr, &[], 0, dest);
         }
 
         let Some(readbuf) = &self.readbuf else {
@@ -146,12 +100,11 @@ impl FileDescription for AnonSocket {
             // corresponding ErrorKind variant.
             throw_unsup_format!("reading from the write end of a pipe");
         };
-        let mut readbuf = readbuf.borrow_mut();
-        if readbuf.buf.is_empty() {
+        if readbuf.borrow().buf.is_empty() {
             if self.peer_fd().upgrade().is_none() {
                 // Socketpair with no peer and empty buffer.
                 // 0 bytes successfully read indicates end-of-file.
-                return ecx.return_read_success(ptr, &bytes, 0, dest);
+                return ecx.return_read_success(ptr, &[], 0, dest);
             } else {
                 if self.is_nonblock {
                     // Non-blocking socketpair with writer and empty buffer.
@@ -167,31 +120,8 @@ impl FileDescription for AnonSocket {
                 }
             }
         }
-
-        // 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);
-
-        // Do full read / partial read based on the space available.
-        // Conveniently, `read` exists on `VecDeque` and has exactly the desired behavior.
-        let actual_read_size = readbuf.buf.read(&mut bytes).unwrap();
-
-        // Need to drop before others can access the readbuf again.
-        drop(readbuf);
-
-        // A notification should be provided for the peer file description even when it can
-        // only write 1 byte. This implementation is not compliant with the actual Linux kernel
-        // implementation. For optimization reasons, the kernel will only mark the file description
-        // as "writable" when it can write more than a certain number of bytes. Since we
-        // don't know what that *certain number* is, we will provide a notification every time
-        // a read is successful. This might result in our epoll emulation providing more
-        // notifications than the real system.
-        if let Some(peer_fd) = self.peer_fd().upgrade() {
-            ecx.check_and_update_readiness(&peer_fd)?;
-        }
-
-        ecx.return_read_success(ptr, &bytes, actual_read_size, dest)
+        // TODO: We might need to decide what to do if peer_fd is closed when read is blocked.
+        anonsocket_read(self, self.peer_fd().upgrade(), len, ptr, dest, ecx)
     }
 
     fn write<'tcx>(
@@ -221,9 +151,8 @@ impl FileDescription for AnonSocket {
             // corresponding ErrorKind variant.
             throw_unsup_format!("writing to the reading end of a pipe");
         };
-        let mut writebuf = writebuf.borrow_mut();
-        let data_size = writebuf.buf.len();
-        let available_space = MAX_SOCKETPAIR_BUFFER_CAPACITY.strict_sub(data_size);
+        let available_space =
+            MAX_SOCKETPAIR_BUFFER_CAPACITY.strict_sub(writebuf.borrow().buf.len());
         if available_space == 0 {
             if self.is_nonblock {
                 // Non-blocking socketpair with a full buffer.
@@ -233,23 +162,138 @@ impl FileDescription for AnonSocket {
                 throw_unsup_format!("socketpair/pipe/pipe2 write: blocking isn't supported yet");
             }
         }
-        // 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 actual_write_size = len.min(available_space);
-        let bytes = ecx.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
-        writebuf.buf.extend(&bytes[..actual_write_size]);
+        anonsocket_write(available_space, &peer_fd, ptr, len, dest, ecx)
+    }
 
-        // Need to stop accessing peer_fd so that it can be notified.
-        drop(writebuf);
+    fn as_unix(&self) -> &dyn UnixFileDescription {
+        self
+    }
+}
 
-        // Notification should be provided for peer fd as it became readable.
-        // The kernel does this even if the fd was already readable before, so we follow suit.
+/// Write to AnonSocket based on the space available and return the written byte size.
+fn anonsocket_write<'tcx>(
+    available_space: usize,
+    peer_fd: &FileDescriptionRef,
+    ptr: Pointer,
+    len: usize,
+    dest: &MPlaceTy<'tcx>,
+    ecx: &mut MiriInterpCx<'tcx>,
+) -> InterpResult<'tcx> {
+    let Some(writebuf) = &peer_fd.downcast::<AnonSocket>().unwrap().readbuf else {
+        // FIXME: This should return EBADF, but there's no nice way to do that as there's no
+        // corresponding ErrorKind variant.
+        throw_unsup_format!("writing to the reading end of a pipe")
+    };
+    let mut writebuf = writebuf.borrow_mut();
+
+    // 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 actual_write_size = len.min(available_space);
+    let bytes = ecx.read_bytes_ptr_strip_provenance(ptr, Size::from_bytes(len))?;
+    writebuf.buf.extend(&bytes[..actual_write_size]);
+
+    // Need to stop accessing peer_fd so that it can be notified.
+    drop(writebuf);
+
+    // Notification should be provided for peer fd as it became readable.
+    // The kernel does this even if the fd was already readable before, so we follow suit.
+    ecx.check_and_update_readiness(peer_fd)?;
+
+    ecx.return_write_success(actual_write_size, dest)
+}
+
+/// Read from AnonSocket and return the number of bytes read.
+fn anonsocket_read<'tcx>(
+    anonsocket: &AnonSocket,
+    peer_fd: Option<FileDescriptionRef>,
+    len: usize,
+    ptr: Pointer,
+    dest: &MPlaceTy<'tcx>,
+    ecx: &mut MiriInterpCx<'tcx>,
+) -> InterpResult<'tcx> {
+    let mut bytes = vec![0; len];
+
+    let Some(readbuf) = &anonsocket.readbuf else {
+        // FIXME: This should return EBADF, but there's no nice way to do that as there's no
+        // corresponding ErrorKind variant.
+        throw_unsup_format!("reading from the write end of a pipe")
+    };
+    let mut readbuf = readbuf.borrow_mut();
+
+    // 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);
+
+    // Do full read / partial read based on the space available.
+    // Conveniently, `read` exists on `VecDeque` and has exactly the desired behavior.
+    let actual_read_size = readbuf.buf.read(&mut bytes[..]).unwrap();
+
+    // Need to drop before others can access the readbuf again.
+    drop(readbuf);
+
+    // A notification should be provided for the peer file description even when it can
+    // only write 1 byte. This implementation is not compliant with the actual Linux kernel
+    // implementation. For optimization reasons, the kernel will only mark the file description
+    // as "writable" when it can write more than a certain number of bytes. Since we
+    // don't know what that *certain number* is, we will provide a notification every time
+    // a read is successful. This might result in our epoll emulation providing more
+    // notifications than the real system.
+    if let Some(peer_fd) = peer_fd {
         ecx.check_and_update_readiness(&peer_fd)?;
+    }
+
+    ecx.return_read_success(ptr, &bytes, actual_read_size, dest)
+}
+
+impl UnixFileDescription for AnonSocket {
+    fn get_epoll_ready_events<'tcx>(&self) -> InterpResult<'tcx, EpollReadyEvents> {
+        // We only check the status of EPOLLIN, EPOLLOUT, EPOLLHUP and EPOLLRDHUP flags.
+        // If other event flags need to be supported in the future, the check should be added here.
+
+        let mut epoll_ready_events = EpollReadyEvents::new();
 
-        ecx.return_write_success(actual_write_size, dest)
+        // Check if it is readable.
+        if let Some(readbuf) = &self.readbuf {
+            if !readbuf.borrow().buf.is_empty() {
+                epoll_ready_events.epollin = true;
+            }
+        } else {
+            // Without a read buffer, reading never blocks, so we are always ready.
+            epoll_ready_events.epollin = true;
+        }
+
+        // Check if is writable.
+        if let Some(peer_fd) = self.peer_fd().upgrade() {
+            if let Some(writebuf) = &peer_fd.downcast::<AnonSocket>().unwrap().readbuf {
+                let data_size = writebuf.borrow().buf.len();
+                let available_space = MAX_SOCKETPAIR_BUFFER_CAPACITY.strict_sub(data_size);
+                if available_space != 0 {
+                    epoll_ready_events.epollout = true;
+                }
+            } else {
+                // Without a write buffer, writing never blocks.
+                epoll_ready_events.epollout = true;
+            }
+        } else {
+            // Peer FD has been closed. This always sets both the RDHUP and HUP flags
+            // as we do not support `shutdown` that could be used to partially close the stream.
+            epoll_ready_events.epollrdhup = true;
+            epoll_ready_events.epollhup = true;
+            // Since the peer is closed, even if no data is available reads will return EOF and
+            // writes will return EPIPE. In other words, they won't block, so we mark this as ready
+            // for read and write.
+            epoll_ready_events.epollin = true;
+            epoll_ready_events.epollout = true;
+            // If there is data lost in peer_fd, set EPOLLERR.
+            if self.peer_lost_data.get() {
+                epoll_ready_events.epollerr = true;
+            }
+        }
+        interp_ok(epoll_ready_events)
     }
 }
 
diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs
index a1fad6f9af4..d6a180451d7 100644
--- a/src/tools/miri/src/shims/windows/foreign_items.rs
+++ b/src/tools/miri/src/shims/windows/foreign_items.rs
@@ -383,7 +383,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 this.write_int(1, dest)?;
             }
             "TlsFree" => {
-                let [key] = this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
+                let [key] =
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 let key = u128::from(this.read_scalar(key)?.to_u32()?);
                 this.machine.tls.delete_tls_key(key)?;
 
diff --git a/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.rs b/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.rs
index 65d29b2c6ba..81a96103db4 100644
--- a/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.rs
+++ b/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.rs
@@ -14,7 +14,7 @@ use std::thread;
 // 2. Thread 2 blocks.
 // 3. Thread 3 unblocks both thread 1 and thread 2.
 // 4. Thread 1 reads.
-// 5. Thread 2's `read` deadlocked.
+// 5. Thread 2's `read` can never complete -> deadlocked.
 
 fn main() {
     // eventfd write will block when EFD_NONBLOCK flag is clear
@@ -23,7 +23,6 @@ fn main() {
     let fd = unsafe { libc::eventfd(0, flags) };
 
     let thread1 = thread::spawn(move || {
-        thread::park();
         let mut buf: [u8; 8] = [0; 8];
         // This read will block initially.
         let res: i64 = unsafe { libc::read(fd, buf.as_mut_ptr().cast(), 8).try_into().unwrap() };
@@ -33,7 +32,6 @@ fn main() {
     });
 
     let thread2 = thread::spawn(move || {
-        thread::park();
         let mut buf: [u8; 8] = [0; 8];
         // This read will block initially, then get unblocked by thread3, then get blocked again
         // because the `read` in thread1 executes first and set the counter to 0 again.
@@ -45,7 +43,6 @@ fn main() {
     });
 
     let thread3 = thread::spawn(move || {
-        thread::park();
         let sized_8_data = 1_u64.to_ne_bytes();
         // Write 1 to the counter, so both thread1 and thread2 will unblock.
         let res: i64 = unsafe {
@@ -55,10 +52,6 @@ fn main() {
         assert_eq!(res, 8);
     });
 
-    thread1.thread().unpark();
-    thread2.thread().unpark();
-    thread3.thread().unpark();
-
     thread1.join().unwrap();
     thread2.join().unwrap();
     thread3.join().unwrap();
diff --git a/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.rs b/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.rs
index f9d34d2fb58..7a2a6f4eb1a 100644
--- a/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.rs
+++ b/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.rs
@@ -14,7 +14,7 @@ use std::thread;
 // 2. Thread 2 blocks.
 // 3. Thread 3 unblocks both thread 1 and thread 2.
 // 4. Thread 1 writes u64::MAX.
-// 5. Thread 2's `write` deadlocked.
+// 5. Thread 2's `write` can never complete -> deadlocked.
 fn main() {
     // eventfd write will block when EFD_NONBLOCK flag is clear
     // and the addition caused counter to exceed u64::MAX - 1.
@@ -28,7 +28,6 @@ fn main() {
     assert_eq!(res, 8);
 
     let thread1 = thread::spawn(move || {
-        thread::park();
         let sized_8_data = (u64::MAX - 1).to_ne_bytes();
         let res: i64 = unsafe {
             libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap()
@@ -38,7 +37,6 @@ fn main() {
     });
 
     let thread2 = thread::spawn(move || {
-        thread::park();
         let sized_8_data = (u64::MAX - 1).to_ne_bytes();
         // Write u64::MAX - 1, so the all subsequent write will block.
         let res: i64 = unsafe {
@@ -52,7 +50,6 @@ fn main() {
     });
 
     let thread3 = thread::spawn(move || {
-        thread::park();
         let mut buf: [u8; 8] = [0; 8];
         // This will unblock both `write` in thread1 and thread2.
         let res: i64 = unsafe { libc::read(fd, buf.as_mut_ptr().cast(), 8).try_into().unwrap() };
@@ -61,10 +58,6 @@ fn main() {
         assert_eq!(counter, (u64::MAX - 1));
     });
 
-    thread1.thread().unpark();
-    thread2.thread().unpark();
-    thread3.thread().unpark();
-
     thread1.join().unwrap();
     thread2.join().unwrap();
     thread3.join().unwrap();
diff --git a/src/tools/miri/tests/fail-dep/libc/libc-epoll-data-race.rs b/src/tools/miri/tests/fail-dep/libc/libc-epoll-data-race.rs
index 398bc92b392..7bef687e339 100644
--- a/src/tools/miri/tests/fail-dep/libc/libc-epoll-data-race.rs
+++ b/src/tools/miri/tests/fail-dep/libc/libc-epoll-data-race.rs
@@ -2,7 +2,7 @@
 //! and we only read one of them, we do not synchronize with the other events
 //! and therefore still report a data race for things that need to see the second event
 //! to be considered synchronized.
-//@only-target: linux
+//@only-target: linux android
 // ensure deterministic schedule
 //@compile-flags: -Zmiri-preemption-rate=0
 
diff --git a/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs b/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs
index 7f5ec477e19..1c6c2f70c1d 100644
--- a/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs
+++ b/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs
@@ -5,7 +5,6 @@
 //@error-in-other-file: deadlock
 
 use std::convert::TryInto;
-use std::thread;
 use std::thread::spawn;
 
 // Using `as` cast since `EPOLLET` wraps around
@@ -41,10 +40,10 @@ fn check_epoll_wait<const N: usize>(
 
 // Test if only one thread is unblocked if multiple threads blocked on same epfd.
 // Expected execution:
-// 1. Thread 2 blocks.
-// 2. Thread 3 blocks.
-// 3. Thread 1 unblocks thread 3.
-// 4. Thread 2 deadlocks.
+// 1. Thread 1 blocks.
+// 2. Thread 2 blocks.
+// 3. Thread 3 unblocks thread 2.
+// 4. Thread 1 deadlocks.
 fn main() {
     // Create an epoll instance.
     let epfd = unsafe { libc::epoll_create1(0) };
@@ -65,30 +64,21 @@ fn main() {
     let expected_value = fds[0] as u64;
     check_epoll_wait::<1>(epfd, &[(expected_event, expected_value)], 0);
 
-    let thread1 = spawn(move || {
-        thread::park();
-        let data = "abcde".as_bytes().as_ptr();
-        let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) };
-        assert_eq!(res, 5);
-    });
-
     let expected_event = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap();
     let expected_value = fds[0] as u64;
-    let thread2 = spawn(move || {
-        thread::park();
+    let thread1 = spawn(move || {
         check_epoll_wait::<1>(epfd, &[(expected_event, expected_value)], -1);
         //~^ERROR: deadlocked
     });
-    let thread3 = spawn(move || {
-        thread::park();
+    let thread2 = spawn(move || {
         check_epoll_wait::<1>(epfd, &[(expected_event, expected_value)], -1);
     });
 
-    thread2.thread().unpark();
-    thread::yield_now();
-    thread3.thread().unpark();
-    thread::yield_now();
-    thread1.thread().unpark();
+    let thread3 = spawn(move || {
+        let data = "abcde".as_bytes().as_ptr();
+        let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) };
+        assert_eq!(res, 5);
+    });
 
     thread1.join().unwrap();
     thread2.join().unwrap();
diff --git a/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.stderr b/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.stderr
index 010dabc1364..b29794f68dd 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
@@ -1,4 +1,10 @@
 error: deadlock: the evaluated program deadlocked
+   |
+   = note: the evaluated program deadlocked
+   = note: (no span available)
+   = note: BACKTRACE on thread `unnamed-ID`:
+
+error: deadlock: the evaluated program deadlocked
   --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
    |
 LL |         let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
@@ -11,16 +17,10 @@ LL |         let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
 note: inside `main`
   --> tests/fail-dep/libc/libc_epoll_block_two_thread.rs:LL:CC
    |
-LL |     thread2.join().unwrap();
+LL |     thread1.join().unwrap();
    |     ^^^^^^^^^^^^^^
 
 error: deadlock: the evaluated program deadlocked
-   |
-   = note: the evaluated program deadlocked
-   = note: (no span available)
-   = note: BACKTRACE on thread `unnamed-ID`:
-
-error: deadlock: the evaluated program deadlocked
   --> tests/fail-dep/libc/libc_epoll_block_two_thread.rs:LL:CC
    |
 LL |         check_epoll_wait::<TAG>(epfd, &[(expected_event, expected_value)], -1);
diff --git a/src/tools/miri/tests/fail/breakpoint.rs b/src/tools/miri/tests/fail/breakpoint.rs
index 2dd87ea6083..42943d58191 100644
--- a/src/tools/miri/tests/fail/breakpoint.rs
+++ b/src/tools/miri/tests/fail/breakpoint.rs
@@ -1,7 +1,5 @@
 #![feature(core_intrinsics)]
 
 fn main() {
-    unsafe {
-        core::intrinsics::breakpoint() //~ ERROR: trace/breakpoint trap
-    };
+    core::intrinsics::breakpoint(); //~ ERROR: trace/breakpoint trap
 }
diff --git a/src/tools/miri/tests/fail/breakpoint.stderr b/src/tools/miri/tests/fail/breakpoint.stderr
index ca98e81f1f4..f203cb0c15f 100644
--- a/src/tools/miri/tests/fail/breakpoint.stderr
+++ b/src/tools/miri/tests/fail/breakpoint.stderr
@@ -1,8 +1,8 @@
 error: abnormal termination: trace/breakpoint trap
   --> tests/fail/breakpoint.rs:LL:CC
    |
-LL |         core::intrinsics::breakpoint()
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trace/breakpoint trap
+LL |     core::intrinsics::breakpoint();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ trace/breakpoint trap
    |
    = note: BACKTRACE:
    = note: inside `main` at tests/fail/breakpoint.rs:LL:CC
diff --git a/src/tools/miri/tests/fail/enum-untagged-variant-invalid-encoding.rs b/src/tools/miri/tests/fail/enum-untagged-variant-invalid-encoding.rs
new file mode 100644
index 00000000000..bd02e7f5fb4
--- /dev/null
+++ b/src/tools/miri/tests/fail/enum-untagged-variant-invalid-encoding.rs
@@ -0,0 +1,27 @@
+// Validity makes this fail at the wrong place.
+//@compile-flags: -Zmiri-disable-validation
+use std::mem;
+
+// This enum has untagged variant idx 1, with niche_variants being 0..=2
+// and niche_start being 2.
+// That means the untagged variants is in the niche variant range!
+// However, using the corresponding value (2+1 = 3) is not a valid encoding of this variant.
+#[derive(Copy, Clone, PartialEq)]
+enum Foo {
+    Var1,
+    Var2(bool),
+    Var3,
+}
+
+fn main() {
+    unsafe {
+        assert!(Foo::Var2(false) == mem::transmute(0u8));
+        assert!(Foo::Var2(true) == mem::transmute(1u8));
+        assert!(Foo::Var1 == mem::transmute(2u8));
+        assert!(Foo::Var3 == mem::transmute(4u8));
+
+        let invalid: Foo = mem::transmute(3u8);
+        assert!(matches!(invalid, Foo::Var2(_)));
+        //~^ ERROR: invalid tag
+    }
+}
diff --git a/src/tools/miri/tests/fail/enum-untagged-variant-invalid-encoding.stderr b/src/tools/miri/tests/fail/enum-untagged-variant-invalid-encoding.stderr
new file mode 100644
index 00000000000..759dbc36380
--- /dev/null
+++ b/src/tools/miri/tests/fail/enum-untagged-variant-invalid-encoding.stderr
@@ -0,0 +1,15 @@
+error: Undefined Behavior: enum value has invalid tag: 0x03
+  --> tests/fail/enum-untagged-variant-invalid-encoding.rs:LL:CC
+   |
+LL |         assert!(matches!(invalid, Foo::Var2(_)));
+   |                          ^^^^^^^ enum value has invalid tag: 0x03
+   |
+   = 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/enum-untagged-variant-invalid-encoding.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/should-pass/cpp20_rwc_syncs.rs b/src/tools/miri/tests/fail/should-pass/cpp20_rwc_syncs.rs
deleted file mode 100644
index cebad507ea9..00000000000
--- a/src/tools/miri/tests/fail/should-pass/cpp20_rwc_syncs.rs
+++ /dev/null
@@ -1,87 +0,0 @@
-//@compile-flags: -Zmiri-ignore-leaks
-
-// https://plv.mpi-sws.org/scfix/paper.pdf
-// 2.2 Second Problem: SC Fences are Too Weak
-// This test should pass under the C++20 model Rust is using.
-// Unfortunately, Miri's weak memory emulation only follows the C++11 model
-// as we don't know how to correctly emulate C++20's revised SC semantics,
-// so we have to stick to C++11 emulation from existing research.
-
-use std::sync::atomic::Ordering::*;
-use std::sync::atomic::{AtomicUsize, fence};
-use std::thread::spawn;
-
-// Spins until it reads the given value
-fn reads_value(loc: &AtomicUsize, val: usize) -> usize {
-    while loc.load(Relaxed) != val {
-        std::hint::spin_loop();
-    }
-    val
-}
-
-// We can't create static items because we need to run each test
-// multiple tests
-fn static_atomic(val: usize) -> &'static AtomicUsize {
-    let ret = Box::leak(Box::new(AtomicUsize::new(val)));
-    // A workaround to put the initialization value in the store buffer.
-    // See https://github.com/rust-lang/miri/issues/2164
-    ret.load(Relaxed);
-    ret
-}
-
-fn test_cpp20_rwc_syncs() {
-    /*
-    int main() {
-        atomic_int x = 0;
-        atomic_int y = 0;
-
-        {{{ x.store(1,mo_relaxed);
-        ||| { r1=x.load(mo_relaxed).readsvalue(1);
-              fence(mo_seq_cst);
-              r2=y.load(mo_relaxed); }
-        ||| { y.store(1,mo_relaxed);
-              fence(mo_seq_cst);
-              r3=x.load(mo_relaxed); }
-        }}}
-        return 0;
-    }
-    */
-    let x = static_atomic(0);
-    let y = static_atomic(0);
-
-    let j1 = spawn(move || {
-        x.store(1, Relaxed);
-    });
-
-    let j2 = spawn(move || {
-        reads_value(&x, 1);
-        fence(SeqCst);
-        y.load(Relaxed)
-    });
-
-    let j3 = spawn(move || {
-        y.store(1, Relaxed);
-        fence(SeqCst);
-        x.load(Relaxed)
-    });
-
-    j1.join().unwrap();
-    let b = j2.join().unwrap();
-    let c = j3.join().unwrap();
-
-    // We cannot write assert_ne!() since ui_test's fail
-    // tests expect exit status 1, whereas panics produce 101.
-    // Our ui_test does not yet support overriding failure status codes.
-    if (b, c) == (0, 0) {
-        // This *should* be unreachable, but Miri will reach it.
-        unsafe {
-            std::hint::unreachable_unchecked(); //~ERROR: unreachable
-        }
-    }
-}
-
-pub fn main() {
-    for _ in 0..500 {
-        test_cpp20_rwc_syncs();
-    }
-}
diff --git a/src/tools/miri/tests/fail/should-pass/cpp20_rwc_syncs.stderr b/src/tools/miri/tests/fail/should-pass/cpp20_rwc_syncs.stderr
deleted file mode 100644
index 5185845568e..00000000000
--- a/src/tools/miri/tests/fail/should-pass/cpp20_rwc_syncs.stderr
+++ /dev/null
@@ -1,20 +0,0 @@
-error: Undefined Behavior: entering unreachable code
-  --> tests/fail/should-pass/cpp20_rwc_syncs.rs:LL:CC
-   |
-LL |             std::hint::unreachable_unchecked();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ entering unreachable code
-   |
-   = 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 `test_cpp20_rwc_syncs` at tests/fail/should-pass/cpp20_rwc_syncs.rs:LL:CC
-note: inside `main`
-  --> tests/fail/should-pass/cpp20_rwc_syncs.rs:LL:CC
-   |
-LL |         test_cpp20_rwc_syncs();
-   |         ^^^^^^^^^^^^^^^^^^^^^^
-
-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/ptr_read_access.rs b/src/tools/miri/tests/native-lib/pass/ptr_read_access.rs
index 46eb5778b32..3ccfecc6fb3 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
@@ -3,17 +3,14 @@
 //@only-on-host
 
 fn main() {
-    test_pointer();
-
-    test_simple();
-
-    test_nested();
-
-    test_static();
+    test_access_pointer();
+    test_access_simple();
+    test_access_nested();
+    test_access_static();
 }
 
-// Test void function that dereferences a pointer and prints its contents from C.
-fn test_pointer() {
+/// Test function that dereferences an int pointer and prints its contents from C.
+fn test_access_pointer() {
     extern "C" {
         fn print_pointer(ptr: *const i32);
     }
@@ -23,8 +20,8 @@ fn test_pointer() {
     unsafe { print_pointer(&x) };
 }
 
-// Test function that dereferences a simple struct pointer and accesses a field.
-fn test_simple() {
+/// Test function that dereferences a simple struct pointer and accesses a field.
+fn test_access_simple() {
     #[repr(C)]
     struct Simple {
         field: i32,
@@ -39,8 +36,8 @@ fn test_simple() {
     assert_eq!(unsafe { access_simple(&simple) }, -42);
 }
 
-// Test function that dereferences nested struct pointers and accesses fields.
-fn test_nested() {
+/// Test function that dereferences nested struct pointers and accesses fields.
+fn test_access_nested() {
     use std::ptr::NonNull;
 
     #[derive(Debug, PartialEq, Eq)]
@@ -61,8 +58,8 @@ fn test_nested() {
     assert_eq!(unsafe { access_nested(&nested_2) }, 97);
 }
 
-// Test function that dereferences static struct pointers and accesses fields.
-fn test_static() {
+/// Test function that dereferences a static struct pointer and accesses fields.
+fn test_access_static() {
     #[repr(C)]
     struct Static {
         value: i32,
diff --git a/src/tools/miri/tests/native-lib/pass/ptr_write_access.rs b/src/tools/miri/tests/native-lib/pass/ptr_write_access.rs
new file mode 100644
index 00000000000..a92e63a4da6
--- /dev/null
+++ b/src/tools/miri/tests/native-lib/pass/ptr_write_access.rs
@@ -0,0 +1,202 @@
+// Only works on Unix targets
+//@ignore-target: windows wasm
+//@only-on-host
+//@compile-flags: -Zmiri-permissive-provenance
+
+#![feature(box_as_ptr)]
+
+use std::mem::MaybeUninit;
+use std::ptr::null;
+
+fn main() {
+    test_increment_int();
+    test_init_int();
+    test_init_array();
+    test_init_static_inner();
+    test_exposed();
+    test_swap_ptr();
+    test_swap_ptr_tuple();
+    test_overwrite_dangling();
+    test_pass_dangling();
+    test_swap_ptr_triple_dangling();
+    test_return_ptr();
+}
+
+/// Test function that modifies an int.
+fn test_increment_int() {
+    extern "C" {
+        fn increment_int(ptr: *mut i32);
+    }
+
+    let mut x = 11;
+
+    unsafe { increment_int(&mut x) };
+    assert_eq!(x, 12);
+}
+
+/// Test function that initializes an int.
+fn test_init_int() {
+    extern "C" {
+        fn init_int(ptr: *mut i32, val: i32);
+    }
+
+    let mut x = MaybeUninit::<i32>::uninit();
+    let val = 21;
+
+    let x = unsafe {
+        init_int(x.as_mut_ptr(), val);
+        x.assume_init()
+    };
+    assert_eq!(x, val);
+}
+
+/// Test function that initializes an array.
+fn test_init_array() {
+    extern "C" {
+        fn init_array(ptr: *mut i32, len: usize, val: i32);
+    }
+
+    const LEN: usize = 3;
+    let mut array = MaybeUninit::<[i32; LEN]>::uninit();
+    let val = 31;
+
+    let array = unsafe {
+        init_array(array.as_mut_ptr().cast::<i32>(), LEN, val);
+        array.assume_init()
+    };
+    assert_eq!(array, [val; LEN]);
+}
+
+/// Test function that initializes an int pointed to by an immutable static.
+fn test_init_static_inner() {
+    #[repr(C)]
+    struct SyncPtr {
+        ptr: *mut i32,
+    }
+    unsafe impl Sync for SyncPtr {}
+
+    extern "C" {
+        fn init_static_inner(s_ptr: *const SyncPtr, val: i32);
+    }
+
+    static mut INNER: MaybeUninit<i32> = MaybeUninit::uninit();
+    #[allow(static_mut_refs)]
+    static STATIC: SyncPtr = SyncPtr { ptr: unsafe { INNER.as_mut_ptr() } };
+    let val = 41;
+
+    let inner = unsafe {
+        init_static_inner(&STATIC, val);
+        INNER.assume_init()
+    };
+    assert_eq!(inner, val);
+}
+
+// Test function that marks an allocation as exposed.
+fn test_exposed() {
+    extern "C" {
+        fn ignore_ptr(ptr: *const i32);
+    }
+
+    let x = 51;
+    let ptr = &raw const x;
+    let p = ptr.addr();
+
+    unsafe { ignore_ptr(ptr) };
+    assert_eq!(unsafe { *(p as *const i32) }, x);
+}
+
+/// Test function that swaps two pointers and exposes the alloc of an int.
+fn test_swap_ptr() {
+    extern "C" {
+        fn swap_ptr(pptr0: *mut *const i32, pptr1: *mut *const i32);
+    }
+
+    let x = 61;
+    let (mut ptr0, mut ptr1) = (&raw const x, null());
+
+    unsafe { swap_ptr(&mut ptr0, &mut ptr1) };
+    assert_eq!(unsafe { *ptr1 }, x);
+}
+
+/// Test function that swaps two pointers in a struct and exposes the alloc of an int.
+fn test_swap_ptr_tuple() {
+    #[repr(C)]
+    struct Tuple {
+        ptr0: *const i32,
+        ptr1: *const i32,
+    }
+
+    extern "C" {
+        fn swap_ptr_tuple(t_ptr: *mut Tuple);
+    }
+
+    let x = 71;
+    let mut tuple = Tuple { ptr0: &raw const x, ptr1: null() };
+
+    unsafe { swap_ptr_tuple(&mut tuple) }
+    assert_eq!(unsafe { *tuple.ptr1 }, x);
+}
+
+/// Test function that interacts with a dangling pointer.
+fn test_overwrite_dangling() {
+    extern "C" {
+        fn overwrite_ptr(pptr: *mut *const i32);
+    }
+
+    let b = Box::new(81);
+    let mut ptr = Box::as_ptr(&b);
+    drop(b);
+
+    unsafe { overwrite_ptr(&mut ptr) };
+    assert_eq!(ptr, null());
+}
+
+/// Test function that passes a dangling pointer.
+fn test_pass_dangling() {
+    extern "C" {
+        fn ignore_ptr(ptr: *const i32);
+    }
+
+    let b = Box::new(91);
+    let ptr = Box::as_ptr(&b);
+    drop(b);
+
+    unsafe { ignore_ptr(ptr) };
+}
+
+/// Test function that interacts with a struct storing a dangling pointer.
+fn test_swap_ptr_triple_dangling() {
+    #[repr(C)]
+    struct Triple {
+        ptr0: *const i32,
+        ptr1: *const i32,
+        ptr2: *const i32,
+    }
+
+    extern "C" {
+        fn swap_ptr_triple_dangling(t_ptr: *const Triple);
+    }
+
+    let x = 101;
+    let b = Box::new(111);
+    let ptr = Box::as_ptr(&b);
+    drop(b);
+    let z = 121;
+    let triple = Triple { ptr0: &raw const x, ptr1: ptr, ptr2: &raw const z };
+
+    unsafe { swap_ptr_triple_dangling(&triple) }
+    assert_eq!(unsafe { *triple.ptr2 }, x);
+}
+
+/// Test function that directly returns its pointer argument.
+fn test_return_ptr() {
+    extern "C" {
+        fn return_ptr(ptr: *const i32) -> *const i32;
+    }
+
+    let x = 131;
+    let ptr = &raw const x;
+
+    let ptr = unsafe { return_ptr(ptr) };
+    assert_eq!(unsafe { *ptr }, x);
+}
diff --git a/src/tools/miri/tests/native-lib/ptr_read_access.c b/src/tools/miri/tests/native-lib/ptr_read_access.c
index 540845d53a7..3b427d6033e 100644
--- a/src/tools/miri/tests/native-lib/ptr_read_access.c
+++ b/src/tools/miri/tests/native-lib/ptr_read_access.c
@@ -3,13 +3,13 @@
 // See comments in build_native_lib()
 #define EXPORT __attribute__((visibility("default")))
 
-/* Test: test_pointer */
+/* Test: test_access_pointer */
 
 EXPORT void print_pointer(const int *ptr) {
   printf("printing pointer dereference from C: %d\n", *ptr);
 }
 
-/* Test: test_simple */
+/* Test: test_access_simple */
 
 typedef struct Simple {
   int field;
@@ -19,7 +19,7 @@ EXPORT int access_simple(const Simple *s_ptr) {
   return s_ptr->field;
 }
 
-/* Test: test_nested */
+/* Test: test_access_nested */
 
 typedef struct Nested {
   int value;
@@ -38,7 +38,7 @@ EXPORT int access_nested(const Nested *n_ptr) {
   return n_ptr->value;
 }
 
-/* Test: test_static */
+/* Test: test_access_static */
 
 typedef struct Static {
     int value;
diff --git a/src/tools/miri/tests/native-lib/ptr_write_access.c b/src/tools/miri/tests/native-lib/ptr_write_access.c
new file mode 100644
index 00000000000..b54c5d86b21
--- /dev/null
+++ b/src/tools/miri/tests/native-lib/ptr_write_access.c
@@ -0,0 +1,90 @@
+#include <stddef.h>
+
+// See comments in build_native_lib()
+#define EXPORT __attribute__((visibility("default")))
+
+/* Test: test_increment_int */
+
+EXPORT void increment_int(int *ptr) {
+  *ptr += 1;
+}
+
+/* Test: test_init_int */
+
+EXPORT void init_int(int *ptr, int val) {
+  *ptr = val;
+}
+
+/* Test: test_init_array */
+
+EXPORT void init_array(int *array, size_t len, int val) {
+  for (size_t i = 0; i < len; i++) {
+    array[i] = val;
+  }
+}
+
+/* Test: test_init_static_inner */
+
+typedef struct SyncPtr {
+    int *ptr;
+} SyncPtr;
+
+EXPORT void init_static_inner(const SyncPtr *s_ptr, int val) {
+  *(s_ptr->ptr) = val;
+}
+
+/* Tests: test_exposed, test_pass_dangling */
+
+EXPORT void ignore_ptr(__attribute__((unused)) const int *ptr) {
+  return;
+}
+
+/* Test: test_expose_int */
+EXPORT void expose_int(const int *int_ptr, const int **pptr) {
+  *pptr = int_ptr;
+}
+
+/* Test: test_swap_ptr */
+
+EXPORT void swap_ptr(const int **pptr0, const int **pptr1) {
+  const int *tmp = *pptr0;
+  *pptr0 = *pptr1;
+  *pptr1 = tmp;
+}
+
+/* Test: test_swap_ptr_tuple */
+
+typedef struct Tuple {
+    int *ptr0;
+    int *ptr1;
+} Tuple;
+
+EXPORT void swap_ptr_tuple(Tuple *t_ptr) {
+  int *tmp = t_ptr->ptr0;
+  t_ptr->ptr0 = t_ptr->ptr1;
+  t_ptr->ptr1 = tmp;
+}
+
+/* Test: test_overwrite_dangling */
+
+EXPORT void overwrite_ptr(const int **pptr) {
+  *pptr = NULL;
+}
+
+/* Test: test_swap_ptr_triple_dangling */
+
+typedef struct Triple {
+    int *ptr0;
+    int *ptr1;
+    int *ptr2;
+} Triple;
+
+EXPORT void swap_ptr_triple_dangling(Triple *t_ptr) {
+  int *tmp = t_ptr->ptr0;
+  t_ptr->ptr0 = t_ptr->ptr2;
+  t_ptr->ptr2 = tmp;
+}
+
+EXPORT const int *return_ptr(const int *ptr) {
+  return ptr;
+}
diff --git a/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs b/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs
index 9bcc776e281..e3c42b2701c 100644
--- a/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs
+++ b/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs
@@ -1,4 +1,4 @@
-//@only-target: linux
+//@only-target: linux android
 // test_epoll_block_then_unblock and test_epoll_race depend on a deterministic schedule.
 //@compile-flags: -Zmiri-preemption-rate=0
 
diff --git a/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs b/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs
index 288c1d41f30..111e639c864 100644
--- a/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs
+++ b/src/tools/miri/tests/pass-dep/libc/libc-epoll-no-blocking.rs
@@ -1,4 +1,4 @@
-//@only-target: linux
+//@only-target: linux android
 
 use std::convert::TryInto;
 
diff --git a/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs b/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs
index dd9c0eb0b54..2e453215ec9 100644
--- a/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs
+++ b/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs
@@ -1,4 +1,4 @@
-//@only-target: linux
+//@only-target: linux android
 // test_race, test_blocking_read and test_blocking_write depend on a deterministic schedule.
 //@compile-flags: -Zmiri-preemption-rate=0
 
@@ -197,7 +197,6 @@ fn test_two_threads_blocked_on_eventfd() {
     assert_eq!(res, 8);
 
     let thread1 = thread::spawn(move || {
-        thread::park();
         let sized_8_data = 1_u64.to_ne_bytes();
         let res: i64 = unsafe {
             libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap()
@@ -207,7 +206,6 @@ fn test_two_threads_blocked_on_eventfd() {
     });
 
     let thread2 = thread::spawn(move || {
-        thread::park();
         let sized_8_data = 1_u64.to_ne_bytes();
         let res: i64 = unsafe {
             libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap()
@@ -217,7 +215,6 @@ fn test_two_threads_blocked_on_eventfd() {
     });
 
     let thread3 = thread::spawn(move || {
-        thread::park();
         let mut buf: [u8; 8] = [0; 8];
         // This will unblock previously blocked eventfd read.
         let res = read_bytes(fd, &mut buf);
@@ -227,10 +224,6 @@ fn test_two_threads_blocked_on_eventfd() {
         assert_eq!(counter, (u64::MAX - 1));
     });
 
-    thread1.thread().unpark();
-    thread2.thread().unpark();
-    thread3.thread().unpark();
-
     thread1.join().unwrap();
     thread2.join().unwrap();
     thread3.join().unwrap();
diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs-symlink.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs-symlink.rs
index a35c92636ce..fd7fc801dc2 100644
--- a/src/tools/miri/tests/pass-dep/libc/libc-fs-symlink.rs
+++ b/src/tools/miri/tests/pass-dep/libc/libc-fs-symlink.rs
@@ -47,9 +47,12 @@ fn test_readlink() {
     assert_eq!(res, small_buf.len() as isize);
 
     // Test that we report a proper error for a missing path.
-    let bad_path = CString::new("MIRI_MISSING_FILE_NAME").unwrap();
     let res = unsafe {
-        libc::readlink(bad_path.as_ptr(), small_buf.as_mut_ptr().cast(), small_buf.len())
+        libc::readlink(
+            c"MIRI_MISSING_FILE_NAME".as_ptr(),
+            small_buf.as_mut_ptr().cast(),
+            small_buf.len(),
+        )
     };
     assert_eq!(res, -1);
     assert_eq!(Error::last_os_error().kind(), ErrorKind::NotFound);
diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs-with-isolation.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs-with-isolation.rs
index ab3fd6733ff..cffcf4a867f 100644
--- a/src/tools/miri/tests/pass-dep/libc/libc-fs-with-isolation.rs
+++ b/src/tools/miri/tests/pass-dep/libc/libc-fs-with-isolation.rs
@@ -2,7 +2,6 @@
 //@compile-flags: -Zmiri-isolation-error=warn-nobacktrace
 //@normalize-stderr-test: "(stat(x)?)" -> "$$STAT"
 
-use std::ffi::CString;
 use std::fs;
 use std::io::{Error, ErrorKind};
 
@@ -13,10 +12,9 @@ fn main() {
     }
 
     // test `readlink`
-    let symlink_c_str = CString::new("foo.txt").unwrap();
     let mut buf = vec![0; "foo_link.txt".len() + 1];
     unsafe {
-        assert_eq!(libc::readlink(symlink_c_str.as_ptr(), buf.as_mut_ptr(), buf.len()), -1);
+        assert_eq!(libc::readlink(c"foo.txt".as_ptr(), buf.as_mut_ptr(), buf.len()), -1);
         assert_eq!(Error::last_os_error().raw_os_error(), Some(libc::EACCES));
     }
 
diff --git a/src/tools/miri/tests/pass-dep/libc/libc-mem.rs b/src/tools/miri/tests/pass-dep/libc/libc-mem.rs
index 10be78798a6..727533a9de6 100644
--- a/src/tools/miri/tests/pass-dep/libc/libc-mem.rs
+++ b/src/tools/miri/tests/pass-dep/libc/libc-mem.rs
@@ -55,12 +55,12 @@ fn test_memcpy() {
 }
 
 fn test_strcpy() {
-    use std::ffi::{CStr, CString};
+    use std::ffi::CStr;
 
     // case: src_size equals dest_size
     unsafe {
-        let src = CString::new("rust").unwrap();
-        let size = src.as_bytes_with_nul().len();
+        let src = c"rust";
+        let size = src.to_bytes_with_nul().len();
         let dest = libc::malloc(size);
         libc::strcpy(dest as *mut libc::c_char, src.as_ptr());
         assert_eq!(CStr::from_ptr(dest as *const libc::c_char), src.as_ref());
@@ -69,8 +69,8 @@ fn test_strcpy() {
 
     // case: src_size is less than dest_size
     unsafe {
-        let src = CString::new("rust").unwrap();
-        let size = src.as_bytes_with_nul().len();
+        let src = c"rust";
+        let size = src.to_bytes_with_nul().len();
         let dest = libc::malloc(size + 1);
         libc::strcpy(dest as *mut libc::c_char, src.as_ptr());
         assert_eq!(CStr::from_ptr(dest as *const libc::c_char), src.as_ref());
diff --git a/src/tools/miri/tests/pass-dep/libc/libc-strerror_r.rs b/src/tools/miri/tests/pass-dep/libc/libc-strerror_r.rs
new file mode 100644
index 00000000000..09885ce839d
--- /dev/null
+++ b/src/tools/miri/tests/pass-dep/libc/libc-strerror_r.rs
@@ -0,0 +1,16 @@
+//@ignore-target: windows # Supported only on unixes
+
+fn main() {
+    unsafe {
+        let mut buf = vec![0u8; 32];
+        assert_eq!(libc::strerror_r(libc::EPERM, buf.as_mut_ptr().cast(), buf.len()), 0);
+        let mut buf2 = vec![0u8; 64];
+        assert_eq!(libc::strerror_r(-1i32, buf2.as_mut_ptr().cast(), buf2.len()), 0);
+        // This buffer is deliberately too small so this triggers ERANGE.
+        let mut buf3 = vec![0u8; 2];
+        assert_eq!(
+            libc::strerror_r(libc::E2BIG, buf3.as_mut_ptr().cast(), buf3.len()),
+            libc::ERANGE
+        );
+    }
+}
diff --git a/src/tools/miri/tests/pass-dep/libc/libc-sysconf.rs b/src/tools/miri/tests/pass-dep/libc/libc-sysconf.rs
new file mode 100644
index 00000000000..b832b3033b7
--- /dev/null
+++ b/src/tools/miri/tests/pass-dep/libc/libc-sysconf.rs
@@ -0,0 +1,19 @@
+//@ignore-target: windows # Supported only on unixes
+
+fn test_sysconfbasic() {
+    unsafe {
+        let ncpus = libc::sysconf(libc::_SC_NPROCESSORS_CONF);
+        assert!(ncpus >= 1);
+        let psz = libc::sysconf(libc::_SC_PAGESIZE);
+        assert!(psz % 4096 == 0);
+        // note that in reality it can return -1 (no hard limit) on some platforms.
+        let gwmax = libc::sysconf(libc::_SC_GETPW_R_SIZE_MAX);
+        assert!(gwmax >= 512);
+        let omax = libc::sysconf(libc::_SC_OPEN_MAX);
+        assert_eq!(omax, 65536);
+    }
+}
+
+fn main() {
+    test_sysconfbasic();
+}
diff --git a/src/tools/miri/tests/pass/0weak_memory_consistency.rs b/src/tools/miri/tests/pass/0weak_memory_consistency.rs
index 10f7aed9418..ce5b8be07f0 100644
--- a/src/tools/miri/tests/pass/0weak_memory_consistency.rs
+++ b/src/tools/miri/tests/pass/0weak_memory_consistency.rs
@@ -1,4 +1,4 @@
-//@compile-flags: -Zmiri-ignore-leaks -Zmiri-disable-stacked-borrows -Zmiri-provenance-gc=10000
+//@compile-flags: -Zmiri-ignore-leaks -Zmiri-disable-stacked-borrows -Zmiri-disable-validation -Zmiri-provenance-gc=10000
 // 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.
 
@@ -22,7 +22,7 @@
 // Available: https://ss265.host.cs.st-andrews.ac.uk/papers/n3132.pdf.
 
 use std::sync::atomic::Ordering::*;
-use std::sync::atomic::{AtomicBool, AtomicI32, fence};
+use std::sync::atomic::{AtomicBool, AtomicI32, Ordering, fence};
 use std::thread::spawn;
 
 #[derive(Copy, Clone)]
@@ -34,19 +34,15 @@ 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: i32) -> &'static AtomicI32 {
-    let ret = Box::leak(Box::new(AtomicI32::new(val)));
-    ret.store(val, Relaxed); // work around https://github.com/rust-lang/miri/issues/2164
-    ret
+    Box::leak(Box::new(AtomicI32::new(val)))
 }
 fn static_atomic_bool(val: bool) -> &'static AtomicBool {
-    let ret = Box::leak(Box::new(AtomicBool::new(val)));
-    ret.store(val, Relaxed); // work around https://github.com/rust-lang/miri/issues/2164
-    ret
+    Box::leak(Box::new(AtomicBool::new(val)))
 }
 
 // Spins until it acquires a pre-determined value.
-fn acquires_value(loc: &AtomicI32, val: i32) -> i32 {
-    while loc.load(Acquire) != val {
+fn loads_value(loc: &AtomicI32, ord: Ordering, val: i32) -> i32 {
+    while loc.load(ord) != val {
         std::hint::spin_loop();
     }
     val
@@ -69,7 +65,7 @@ fn test_corr() {
     }); //                                           |                    |
     #[rustfmt::skip] //                              |synchronizes-with   |happens-before
     let j3 = spawn(move || { //                      |                    |
-        acquires_value(&y, 1); // <------------------+                    |
+        loads_value(&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
@@ -94,12 +90,12 @@ fn test_wrc() {
     }); //                                           |                     |
     #[rustfmt::skip] //                              |synchronizes-with    |
     let j2 = spawn(move || { //                      |                     |
-        acquires_value(&x, 1); // <------------------+                     |
+        loads_value(&x, Acquire, 1); // <------------+                     |
         y.store(1, Release); // ---------------------+                     |happens-before
     }); //                                           |                     |
     #[rustfmt::skip] //                              |synchronizes-with    |
     let j3 = spawn(move || { //                      |                     |
-        acquires_value(&y, 1); // <------------------+                     |
+        loads_value(&y, Acquire, 1); // <------------+                     |
         x.load(Relaxed) // <-----------------------------------------------+
     });
 
@@ -125,7 +121,7 @@ fn test_message_passing() {
     #[rustfmt::skip] //                              |synchronizes-with  | happens-before
     let j2 = spawn(move || { //                      |                   |
         let x = x; // avoid field capturing          |                   |
-        acquires_value(&y, 1); // <------------------+                   |
+        loads_value(&y, Acquire, 1); // <------------+                   |
         unsafe { *x.0 } // <---------------------------------------------+
     });
 
@@ -268,9 +264,6 @@ fn test_iriw_sc_rlx() {
     let x = static_atomic_bool(false);
     let y = static_atomic_bool(false);
 
-    x.store(false, Relaxed);
-    y.store(false, Relaxed);
-
     let a = spawn(move || x.store(true, Relaxed));
     let b = spawn(move || y.store(true, Relaxed));
     let c = spawn(move || {
@@ -290,6 +283,152 @@ fn test_iriw_sc_rlx() {
     assert!(c || d);
 }
 
+// Similar to `test_iriw_sc_rlx` but with fences instead of SC accesses.
+fn test_cpp20_sc_fence_fix() {
+    let x = static_atomic_bool(false);
+    let y = static_atomic_bool(false);
+
+    let thread1 = spawn(|| {
+        let a = x.load(Relaxed);
+        fence(SeqCst);
+        let b = y.load(Relaxed);
+        (a, b)
+    });
+
+    let thread2 = spawn(|| {
+        x.store(true, Relaxed);
+    });
+    let thread3 = spawn(|| {
+        y.store(true, Relaxed);
+    });
+
+    let thread4 = spawn(|| {
+        let c = y.load(Relaxed);
+        fence(SeqCst);
+        let d = x.load(Relaxed);
+        (c, d)
+    });
+
+    let (a, b) = thread1.join().unwrap();
+    thread2.join().unwrap();
+    thread3.join().unwrap();
+    let (c, d) = thread4.join().unwrap();
+    let bad = a == true && b == false && c == true && d == false;
+    assert!(!bad);
+}
+
+// https://plv.mpi-sws.org/scfix/paper.pdf
+// 2.2 Second Problem: SC Fences are Too Weak
+fn test_cpp20_rwc_syncs() {
+    /*
+    int main() {
+        atomic_int x = 0;
+        atomic_int y = 0;
+        {{{ x.store(1,mo_relaxed);
+        ||| { r1=x.load(mo_relaxed).readsvalue(1);
+              fence(mo_seq_cst);
+              r2=y.load(mo_relaxed); }
+        ||| { y.store(1,mo_relaxed);
+              fence(mo_seq_cst);
+              r3=x.load(mo_relaxed); }
+        }}}
+        return 0;
+    }
+    */
+    let x = static_atomic(0);
+    let y = static_atomic(0);
+
+    let j1 = spawn(move || {
+        x.store(1, Relaxed);
+    });
+
+    let j2 = spawn(move || {
+        loads_value(&x, Relaxed, 1);
+        fence(SeqCst);
+        y.load(Relaxed)
+    });
+
+    let j3 = spawn(move || {
+        y.store(1, Relaxed);
+        fence(SeqCst);
+        x.load(Relaxed)
+    });
+
+    j1.join().unwrap();
+    let b = j2.join().unwrap();
+    let c = j3.join().unwrap();
+
+    assert!((b, c) != (0, 0));
+}
+
+/// This checks that the *last* thing the SC fence does is act like a release fence.
+/// See <https://github.com/rust-lang/miri/pull/4057#issuecomment-2522296601>.
+fn test_sc_fence_release() {
+    let x = static_atomic(0);
+    let y = static_atomic(0);
+    let z = static_atomic(0);
+    let k = static_atomic(0);
+
+    let j1 = spawn(move || {
+        x.store(1, Relaxed);
+        fence(SeqCst);
+        k.store(1, Relaxed);
+    });
+    let j2 = spawn(move || {
+        y.store(1, Relaxed);
+        fence(SeqCst);
+        z.store(1, Relaxed);
+    });
+
+    let j3 = spawn(move || {
+        let kval = k.load(Acquire);
+        let yval = y.load(Relaxed);
+        (kval, yval)
+    });
+    let j4 = spawn(move || {
+        let zval = z.load(Acquire);
+        let xval = x.load(Relaxed);
+        (zval, xval)
+    });
+
+    j1.join().unwrap();
+    j2.join().unwrap();
+    let (kval, yval) = j3.join().unwrap();
+    let (zval, xval) = j4.join().unwrap();
+
+    let bad = kval == 1 && yval == 0 && zval == 1 && xval == 0;
+    assert!(!bad);
+}
+
+/// Test that SC fences and accesses sync correctly with each other.
+fn test_sc_fence_access() {
+    /*
+        Wx1 sc
+        Ry0 sc
+        ||
+        Wy1 rlx
+        SC-fence
+        Rx0 rlx
+    */
+    let x = static_atomic(0);
+    let y = static_atomic(0);
+
+    let j1 = spawn(move || {
+        x.store(1, SeqCst);
+        y.load(SeqCst)
+    });
+    let j2 = spawn(move || {
+        y.store(1, Relaxed);
+        fence(SeqCst);
+        x.load(Relaxed)
+    });
+
+    let v1 = j1.join().unwrap();
+    let v2 = j2.join().unwrap();
+    let bad = v1 == 0 && v2 == 0;
+    assert!(!bad);
+}
+
 pub fn main() {
     for _ in 0..50 {
         test_single_thread();
@@ -301,5 +440,9 @@ pub fn main() {
         test_sc_store_buffering();
         test_sync_through_rmw_and_fences();
         test_iriw_sc_rlx();
+        test_cpp20_sc_fence_fix();
+        test_cpp20_rwc_syncs();
+        test_sc_fence_release();
+        test_sc_fence_access();
     }
 }
diff --git a/src/tools/miri/tests/pass/async-closure-captures.rs b/src/tools/miri/tests/pass/async-closure-captures.rs
index cac26bfe146..979a6d1cbe0 100644
--- a/src/tools/miri/tests/pass/async-closure-captures.rs
+++ b/src/tools/miri/tests/pass/async-closure-captures.rs
@@ -1,6 +1,6 @@
 // Same as rustc's `tests/ui/async-await/async-closures/captures.rs`, keep in sync
 
-#![feature(async_closure, noop_waker)]
+#![feature(async_closure, async_trait_bounds)]
 
 use std::future::Future;
 use std::pin::pin;
diff --git a/src/tools/miri/tests/pass/async-closure-drop.rs b/src/tools/miri/tests/pass/async-closure-drop.rs
index 9b2fc2948bf..ad9822fa46d 100644
--- a/src/tools/miri/tests/pass/async-closure-drop.rs
+++ b/src/tools/miri/tests/pass/async-closure-drop.rs
@@ -1,4 +1,4 @@
-#![feature(async_closure, noop_waker, async_fn_traits)]
+#![feature(async_closure, async_trait_bounds)]
 
 use std::future::Future;
 use std::pin::pin;
diff --git a/src/tools/miri/tests/pass/async-closure.rs b/src/tools/miri/tests/pass/async-closure.rs
index 721af578883..979b83687e4 100644
--- a/src/tools/miri/tests/pass/async-closure.rs
+++ b/src/tools/miri/tests/pass/async-closure.rs
@@ -1,4 +1,4 @@
-#![feature(async_closure, noop_waker, async_fn_traits)]
+#![feature(async_closure, async_fn_traits)]
 #![allow(unused)]
 
 use std::future::Future;
diff --git a/src/tools/miri/tests/pass/async-drop.rs b/src/tools/miri/tests/pass/async-drop.rs
index 53e3476f620..a455f377e85 100644
--- a/src/tools/miri/tests/pass/async-drop.rs
+++ b/src/tools/miri/tests/pass/async-drop.rs
@@ -6,7 +6,7 @@
 // please consider modifying rustc's async drop test at
 // `tests/ui/async-await/async-drop.rs`.
 
-#![feature(async_drop, impl_trait_in_assoc_type, noop_waker, async_closure)]
+#![feature(async_drop, impl_trait_in_assoc_type, async_closure)]
 #![allow(incomplete_features, dead_code)]
 
 // FIXME(zetanumbers): consider AsyncDestruct::async_drop cleanup tests
diff --git a/src/tools/miri/tests/pass/async-fn.rs b/src/tools/miri/tests/pass/async-fn.rs
index 67ec2e26b30..42c60bb4fab 100644
--- a/src/tools/miri/tests/pass/async-fn.rs
+++ b/src/tools/miri/tests/pass/async-fn.rs
@@ -1,5 +1,4 @@
 #![feature(never_type)]
-#![feature(noop_waker)]
 
 use std::future::Future;
 
diff --git a/src/tools/miri/tests/pass/backtrace/backtrace-api-v0.rs b/src/tools/miri/tests/pass/backtrace/backtrace-api-v0.rs
deleted file mode 100644
index 3fff7921aff..00000000000
--- a/src/tools/miri/tests/pass/backtrace/backtrace-api-v0.rs
+++ /dev/null
@@ -1,69 +0,0 @@
-//@normalize-stderr-test: "::<.*>" -> ""
-
-#[inline(never)]
-fn func_a() -> Box<[*mut ()]> {
-    func_b::<u8>()
-}
-#[inline(never)]
-fn func_b<T>() -> Box<[*mut ()]> {
-    func_c()
-}
-
-macro_rules! invoke_func_d {
-    () => {
-        func_d()
-    };
-}
-
-#[inline(never)]
-fn func_c() -> Box<[*mut ()]> {
-    invoke_func_d!()
-}
-#[inline(never)]
-fn func_d() -> Box<[*mut ()]> {
-    unsafe { miri_get_backtrace(0) }
-}
-
-fn main() {
-    let mut seen_main = false;
-    let frames = func_a();
-    for frame in frames.iter() {
-        let miri_frame = unsafe { miri_resolve_frame(*frame, 0) };
-        let name = String::from_utf8(miri_frame.name.into()).unwrap();
-        let filename = String::from_utf8(miri_frame.filename.into()).unwrap();
-
-        if name == "func_a" {
-            assert_eq!(func_a as *mut (), miri_frame.fn_ptr);
-        }
-
-        // Print every frame to stderr.
-        let out = format!("{}:{}:{} ({})", filename, miri_frame.lineno, miri_frame.colno, name);
-        eprintln!("{}", out);
-        // Print the 'main' frame (and everything before it) to stdout, skipping
-        // the printing of internal (and possibly fragile) libstd frames.
-        // Stdout is less normalized so we see more, but it also means we can print less
-        // as platform differences would lead to test suite failures.
-        if !seen_main {
-            println!("{}", out);
-            seen_main = name == "main";
-        }
-    }
-}
-
-// This goes at the bottom of the file so that we can change it
-// without disturbing line numbers of the functions in the backtrace.
-
-extern "Rust" {
-    fn miri_get_backtrace(flags: u64) -> Box<[*mut ()]>;
-    fn miri_resolve_frame(ptr: *mut (), flags: u64) -> MiriFrame;
-}
-
-#[derive(Debug)]
-#[repr(C)]
-struct MiriFrame {
-    name: Box<[u8]>,
-    filename: Box<[u8]>,
-    lineno: u32,
-    colno: u32,
-    fn_ptr: *mut (),
-}
diff --git a/src/tools/miri/tests/pass/backtrace/backtrace-api-v0.stderr b/src/tools/miri/tests/pass/backtrace/backtrace-api-v0.stderr
deleted file mode 100644
index 9849a1aa74e..00000000000
--- a/src/tools/miri/tests/pass/backtrace/backtrace-api-v0.stderr
+++ /dev/null
@@ -1,18 +0,0 @@
-tests/pass/backtrace/backtrace-api-v0.rs:LL:CC (func_d)
-tests/pass/backtrace/backtrace-api-v0.rs:LL:CC (func_c)
-tests/pass/backtrace/backtrace-api-v0.rs:LL:CC (func_b)
-tests/pass/backtrace/backtrace-api-v0.rs:LL:CC (func_a)
-tests/pass/backtrace/backtrace-api-v0.rs:LL:CC (main)
-RUSTLIB/core/src/ops/function.rs:LL:CC (<fn() as std::ops::FnOnce<()>>::call_once - shim(fn()))
-RUSTLIB/std/src/sys/backtrace.rs:LL:CC (std::sys::backtrace::__rust_begin_short_backtrace)
-RUSTLIB/std/src/rt.rs:LL:CC (std::rt::lang_start::{closure#0})
-RUSTLIB/core/src/ops/function.rs:LL:CC (std::ops::function::impls::call_once)
-RUSTLIB/std/src/panicking.rs:LL:CC (std::panicking::r#try::do_call)
-RUSTLIB/std/src/panicking.rs:LL:CC (std::panicking::r#try)
-RUSTLIB/std/src/panic.rs:LL:CC (std::panic::catch_unwind)
-RUSTLIB/std/src/rt.rs:LL:CC (std::rt::lang_start_internal::{closure#1})
-RUSTLIB/std/src/panicking.rs:LL:CC (std::panicking::r#try::do_call)
-RUSTLIB/std/src/panicking.rs:LL:CC (std::panicking::r#try)
-RUSTLIB/std/src/panic.rs:LL:CC (std::panic::catch_unwind)
-RUSTLIB/std/src/rt.rs:LL:CC (std::rt::lang_start_internal)
-RUSTLIB/std/src/rt.rs:LL:CC (std::rt::lang_start)
diff --git a/src/tools/miri/tests/pass/backtrace/backtrace-api-v0.stdout b/src/tools/miri/tests/pass/backtrace/backtrace-api-v0.stdout
deleted file mode 100644
index 8c1bc5c353e..00000000000
--- a/src/tools/miri/tests/pass/backtrace/backtrace-api-v0.stdout
+++ /dev/null
@@ -1,5 +0,0 @@
-tests/pass/backtrace/backtrace-api-v0.rs:24:14 (func_d)
-tests/pass/backtrace/backtrace-api-v0.rs:14:9 (func_c)
-tests/pass/backtrace/backtrace-api-v0.rs:9:5 (func_b::<u8>)
-tests/pass/backtrace/backtrace-api-v0.rs:5:5 (func_a)
-tests/pass/backtrace/backtrace-api-v0.rs:29:18 (main)
diff --git a/src/tools/miri/tests/pass/dyn-star.rs b/src/tools/miri/tests/pass/dyn-star.rs
index dab589b4651..1ce0dd3c9d5 100644
--- a/src/tools/miri/tests/pass/dyn-star.rs
+++ b/src/tools/miri/tests/pass/dyn-star.rs
@@ -1,7 +1,6 @@
 #![feature(dyn_star)]
 #![allow(incomplete_features)]
 #![feature(custom_inner_attributes)]
-#![feature(noop_waker)]
 // rustfmt destroys `dyn* Trait` syntax
 #![rustfmt::skip]
 
diff --git a/src/tools/miri/tests/pass/function_pointers.rs b/src/tools/miri/tests/pass/function_pointers.rs
index a5c4bc5e0d9..7145be334a1 100644
--- a/src/tools/miri/tests/pass/function_pointers.rs
+++ b/src/tools/miri/tests/pass/function_pointers.rs
@@ -1,3 +1,5 @@
+#![allow(unpredictable_function_pointer_comparisons)]
+
 use std::mem;
 
 trait Answer {
diff --git a/src/tools/miri/tests/pass/future-self-referential.rs b/src/tools/miri/tests/pass/future-self-referential.rs
index 8aeb26a7a95..88d52d8f1c1 100644
--- a/src/tools/miri/tests/pass/future-self-referential.rs
+++ b/src/tools/miri/tests/pass/future-self-referential.rs
@@ -1,6 +1,5 @@
 //@revisions: stack tree
 //@[tree]compile-flags: -Zmiri-tree-borrows
-#![feature(noop_waker)]
 
 use std::future::*;
 use std::marker::PhantomPinned;
diff --git a/src/tools/miri/tests/pass/intrinsics/fmuladd_nondeterministic.rs b/src/tools/miri/tests/pass/intrinsics/fmuladd_nondeterministic.rs
index b46cf1ddf65..b688405c4b1 100644
--- a/src/tools/miri/tests/pass/intrinsics/fmuladd_nondeterministic.rs
+++ b/src/tools/miri/tests/pass/intrinsics/fmuladd_nondeterministic.rs
@@ -1,44 +1,75 @@
-#![feature(core_intrinsics)]
+#![feature(core_intrinsics, portable_simd)]
+use std::intrinsics::simd::simd_relaxed_fma;
 use std::intrinsics::{fmuladdf32, fmuladdf64};
+use std::simd::prelude::*;
 
-fn main() {
-    let mut saw_zero = false;
-    let mut saw_nonzero = false;
+fn ensure_both_happen(f: impl Fn() -> bool) -> bool {
+    let mut saw_true = false;
+    let mut saw_false = false;
     for _ in 0..50 {
-        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) };
-        if x == 0.0 {
-            saw_zero = true;
+        let b = f();
+        if b {
+            saw_true = true;
         } else {
-            saw_nonzero = true;
+            saw_false = true;
+        }
+        if saw_true && saw_false {
+            return true;
         }
     }
+    false
+}
+
+fn main() {
     assert!(
-        saw_zero && saw_nonzero,
+        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"
     );
 
-    let mut saw_zero = false;
-    let mut saw_nonzero = false;
-    for _ in 0..50 {
-        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) };
-        if x == 0.0 {
-            saw_zero = true;
-        } else {
-            saw_nonzero = true;
-        }
-    }
     assert!(
-        saw_zero && saw_nonzero,
+        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"
     );
+
+    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"
+    );
+
+    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"
+    );
 }
diff --git a/src/tools/miri/tests/pass/intrinsics/portable-simd.rs b/src/tools/miri/tests/pass/intrinsics/portable-simd.rs
index f560669dd63..acd3502f528 100644
--- a/src/tools/miri/tests/pass/intrinsics/portable-simd.rs
+++ b/src/tools/miri/tests/pass/intrinsics/portable-simd.rs
@@ -40,6 +40,17 @@ fn simd_ops_f32() {
         f32x4::splat(-3.2).mul_add(b, f32x4::splat(f32::NEG_INFINITY)),
         f32x4::splat(f32::NEG_INFINITY)
     );
+
+    unsafe {
+        assert_eq!(intrinsics::simd_relaxed_fma(a, b, a), (a * b) + a);
+        assert_eq!(intrinsics::simd_relaxed_fma(b, b, a), (b * b) + a);
+        assert_eq!(intrinsics::simd_relaxed_fma(a, b, b), (a * b) + b);
+        assert_eq!(
+            intrinsics::simd_relaxed_fma(f32x4::splat(-3.2), b, f32x4::splat(f32::NEG_INFINITY)),
+            f32x4::splat(f32::NEG_INFINITY)
+        );
+    }
+
     assert_eq!((a * a).sqrt(), a);
     assert_eq!((b * b).sqrt(), b.abs());
 
@@ -94,6 +105,17 @@ fn simd_ops_f64() {
         f64x4::splat(-3.2).mul_add(b, f64x4::splat(f64::NEG_INFINITY)),
         f64x4::splat(f64::NEG_INFINITY)
     );
+
+    unsafe {
+        assert_eq!(intrinsics::simd_relaxed_fma(a, b, a), (a * b) + a);
+        assert_eq!(intrinsics::simd_relaxed_fma(b, b, a), (b * b) + a);
+        assert_eq!(intrinsics::simd_relaxed_fma(a, b, b), (a * b) + b);
+        assert_eq!(
+            intrinsics::simd_relaxed_fma(f64x4::splat(-3.2), b, f64x4::splat(f64::NEG_INFINITY)),
+            f64x4::splat(f64::NEG_INFINITY)
+        );
+    }
+
     assert_eq!((a * a).sqrt(), a);
     assert_eq!((b * b).sqrt(), b.abs());
 
diff --git a/src/tools/miri/tests/pass/issues/issue-91636.rs b/src/tools/miri/tests/pass/issues/issue-91636.rs
index 00370165812..d0cadcd2c70 100644
--- a/src/tools/miri/tests/pass/issues/issue-91636.rs
+++ b/src/tools/miri/tests/pass/issues/issue-91636.rs
@@ -1,3 +1,5 @@
+#![allow(unpredictable_function_pointer_comparisons)]
+
 type BuiltIn = for<'a> fn(&str);
 
 struct Function {
diff --git a/src/tools/miri/tests/pass/issues/issue-miri-2068.rs b/src/tools/miri/tests/pass/issues/issue-miri-2068.rs
index ccee2221e29..1931b6c9d79 100644
--- a/src/tools/miri/tests/pass/issues/issue-miri-2068.rs
+++ b/src/tools/miri/tests/pass/issues/issue-miri-2068.rs
@@ -1,5 +1,3 @@
-#![feature(noop_waker)]
-
 use std::future::Future;
 use std::pin::Pin;
 use std::task::{Context, Poll, Waker};
diff --git a/src/tools/miri/tests/pass/move-data-across-await-point.rs b/src/tools/miri/tests/pass/move-data-across-await-point.rs
index 1a93a6bf664..5aafddd99b9 100644
--- a/src/tools/miri/tests/pass/move-data-across-await-point.rs
+++ b/src/tools/miri/tests/pass/move-data-across-await-point.rs
@@ -1,4 +1,3 @@
-#![feature(noop_waker)]
 use std::future::Future;
 use std::ptr;
 
diff --git a/src/tools/miri/tests/pass/shims/fs.rs b/src/tools/miri/tests/pass/shims/fs.rs
index 81151f4ac47..3e514d95ee9 100644
--- a/src/tools/miri/tests/pass/shims/fs.rs
+++ b/src/tools/miri/tests/pass/shims/fs.rs
@@ -27,8 +27,11 @@ fn main() {
     test_file_sync();
     test_errors();
     test_rename();
-    test_directory();
-    test_canonicalize();
+    // solarish needs to support readdir/readdir64 for these tests.
+    if cfg!(not(any(target_os = "solaris", target_os = "illumos"))) {
+        test_directory();
+        test_canonicalize();
+    }
     test_from_raw_os_error();
     #[cfg(unix)]
     test_pread_pwrite();
diff --git a/src/tools/miri/tests/ui.rs b/src/tools/miri/tests/ui.rs
index 9553a37c9a8..9b9542b88a9 100644
--- a/src/tools/miri/tests/ui.rs
+++ b/src/tools/miri/tests/ui.rs
@@ -64,6 +64,7 @@ fn build_native_lib() -> PathBuf {
             // FIXME: Automate gathering of all relevant C source files in the directory.
             "tests/native-lib/scalar_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.
             "-Wall",
             "-Wextra",
diff --git a/src/tools/nix-dev-shell/envrc-flake b/src/tools/nix-dev-shell/envrc-flake
index 218d88d8721..849ed1f4fc5 100644
--- a/src/tools/nix-dev-shell/envrc-flake
+++ b/src/tools/nix-dev-shell/envrc-flake
@@ -1,7 +1,7 @@
 # If you want to use this as an .envrc file to create a shell with necessery components 
 # to develop rustc, use the following command in the root of the rusr checkout:
 #	
-# ln -s ./src/tools/nix-dev-shell/envrc-flake ./.envrc && echo .envrc >> .git/info/exclude
+# ln -s ./src/tools/nix-dev-shell/envrc-flake ./.envrc && nix flake update --flake ./src/tools/nix-dev-shell
 
 if nix flake show path:./src/tools/nix-dev-shell &> /dev/null; then
   use flake path:./src/tools/nix-dev-shell
diff --git a/src/tools/nix-dev-shell/envrc-shell b/src/tools/nix-dev-shell/envrc-shell
index fb7231a6c30..d8f900fe86a 100644
--- a/src/tools/nix-dev-shell/envrc-shell
+++ b/src/tools/nix-dev-shell/envrc-shell
@@ -1,7 +1,7 @@
 # If you want to use this as an .envrc file to create a shell with necessery components 
 # to develop rustc, use the following command in the root of the rusr checkout:
 #	
-# ln -s ./src/tools/nix-dev-shell/envrc-shell ./.envrc && echo .envrc >> .git/info/exclude
+# ln -s ./src/tools/nix-dev-shell/envrc-shell ./.envrc
 
 use nix ./src/tools/nix-dev-shell/shell.nix
   
diff --git a/src/tools/nix-dev-shell/flake.nix b/src/tools/nix-dev-shell/flake.nix
index 8ab5e097427..1b838bd2f7b 100644
--- a/src/tools/nix-dev-shell/flake.nix
+++ b/src/tools/nix-dev-shell/flake.nix
@@ -24,9 +24,8 @@
           # Avoid creating text files for ICEs.
           RUSTC_ICE = "0";
           # Provide `libstdc++.so.6` for the self-contained lld.
-          LD_LIBRARY_PATH = "${with pkgs; lib.makeLibraryPath [
-            stdenv.cc.cc.lib
-          ]}";
+          # Provide `libz.so.1`.
+          LD_LIBRARY_PATH = "${with pkgs; lib.makeLibraryPath [stdenv.cc.cc.lib zlib]}";
         };
       }
     );
diff --git a/src/tools/nix-dev-shell/shell.nix b/src/tools/nix-dev-shell/shell.nix
index 8a5cbb7c89e..a3f5969bd81 100644
--- a/src/tools/nix-dev-shell/shell.nix
+++ b/src/tools/nix-dev-shell/shell.nix
@@ -13,7 +13,6 @@ pkgs.mkShell {
   # Avoid creating text files for ICEs.
   RUSTC_ICE = "0";
   # Provide `libstdc++.so.6` for the self-contained lld.
-  LD_LIBRARY_PATH = "${with pkgs; lib.makeLibraryPath [
-    stdenv.cc.cc.lib
-  ]}";
+  # Provide `libz.so.1`
+  LD_LIBRARY_PATH = "${with pkgs; lib.makeLibraryPath [stdenv.cc.cc.lib zlib]}";
 }
diff --git a/src/tools/publish_toolstate.py b/src/tools/publish_toolstate.py
index 328b48e04d2..a639dc20a60 100755
--- a/src/tools/publish_toolstate.py
+++ b/src/tools/publish_toolstate.py
@@ -14,6 +14,7 @@ import json
 import datetime
 import collections
 import textwrap
+
 try:
     import urllib2
     from urllib2 import HTTPError
@@ -21,7 +22,7 @@ except ImportError:
     import urllib.request as urllib2
     from urllib.error import HTTPError
 try:
-    import typing # noqa: F401 FIXME: py2
+    import typing  # noqa: F401 FIXME: py2
 except ImportError:
     pass
 
@@ -29,40 +30,41 @@ except ImportError:
 # These should be collaborators of the rust-lang/rust repository (with at least
 # read privileges on it). CI will fail otherwise.
 MAINTAINERS = {
-    'book': {'carols10cents'},
-    'nomicon': {'frewsxcv', 'Gankra', 'JohnTitor'},
-    'reference': {'Havvy', 'matthewjasper', 'ehuss'},
-    'rust-by-example': {'marioidival'},
-    'embedded-book': {'adamgreig', 'andre-richter', 'jamesmunns', 'therealprof'},
-    'edition-guide': {'ehuss'},
-    'rustc-dev-guide': {'spastorino', 'amanjeev', 'JohnTitor'},
+    "book": {"carols10cents"},
+    "nomicon": {"frewsxcv", "Gankra", "JohnTitor"},
+    "reference": {"Havvy", "matthewjasper", "ehuss"},
+    "rust-by-example": {"marioidival"},
+    "embedded-book": {"adamgreig", "andre-richter", "jamesmunns", "therealprof"},
+    "edition-guide": {"ehuss"},
+    "rustc-dev-guide": {"spastorino", "amanjeev", "JohnTitor"},
 }
 
 LABELS = {
-    'book': ['C-bug'],
-    'nomicon': ['C-bug'],
-    'reference': ['C-bug'],
-    'rust-by-example': ['C-bug'],
-    'embedded-book': ['C-bug'],
-    'edition-guide': ['C-bug'],
-    'rustc-dev-guide': ['C-bug'],
+    "book": ["C-bug"],
+    "nomicon": ["C-bug"],
+    "reference": ["C-bug"],
+    "rust-by-example": ["C-bug"],
+    "embedded-book": ["C-bug"],
+    "edition-guide": ["C-bug"],
+    "rustc-dev-guide": ["C-bug"],
 }
 
 REPOS = {
-    'book': 'https://github.com/rust-lang/book',
-    'nomicon': 'https://github.com/rust-lang/nomicon',
-    'reference': 'https://github.com/rust-lang/reference',
-    'rust-by-example': 'https://github.com/rust-lang/rust-by-example',
-    'embedded-book': 'https://github.com/rust-embedded/book',
-    'edition-guide': 'https://github.com/rust-lang/edition-guide',
-    'rustc-dev-guide': 'https://github.com/rust-lang/rustc-dev-guide',
+    "book": "https://github.com/rust-lang/book",
+    "nomicon": "https://github.com/rust-lang/nomicon",
+    "reference": "https://github.com/rust-lang/reference",
+    "rust-by-example": "https://github.com/rust-lang/rust-by-example",
+    "embedded-book": "https://github.com/rust-embedded/book",
+    "edition-guide": "https://github.com/rust-lang/edition-guide",
+    "rustc-dev-guide": "https://github.com/rust-lang/rustc-dev-guide",
 }
 
+
 def load_json_from_response(resp):
     # type: (typing.Any) -> typing.Any
     content = resp.read()
     if isinstance(content, bytes):
-        content_str = content.decode('utf-8')
+        content_str = content.decode("utf-8")
     else:
         print("Refusing to decode " + str(type(content)) + " to str")
     return json.loads(content_str)
@@ -70,11 +72,10 @@ def load_json_from_response(resp):
 
 def read_current_status(current_commit, path):
     # type: (str, str) -> typing.Mapping[str, typing.Any]
-    '''Reads build status of `current_commit` from content of `history/*.tsv`
-    '''
-    with open(path, 'r') as f:
+    """Reads build status of `current_commit` from content of `history/*.tsv`"""
+    with open(path, "r") as f:
         for line in f:
-            (commit, status) = line.split('\t', 1)
+            (commit, status) = line.split("\t", 1)
             if commit == current_commit:
                 return json.loads(status)
     return {}
@@ -82,12 +83,12 @@ def read_current_status(current_commit, path):
 
 def gh_url():
     # type: () -> str
-    return os.environ['TOOLSTATE_ISSUES_API_URL']
+    return os.environ["TOOLSTATE_ISSUES_API_URL"]
 
 
 def maybe_remove_mention(message):
     # type: (str) -> str
-    if os.environ.get('TOOLSTATE_SKIP_MENTIONS') is not None:
+    if os.environ.get("TOOLSTATE_SKIP_MENTIONS") is not None:
         return message.replace("@", "")
     return message
 
@@ -102,36 +103,45 @@ def issue(
     github_token,
 ):
     # type: (str, str, typing.Iterable[str], str, str, typing.List[str], str) -> None
-    '''Open an issue about the toolstate failure.'''
-    if status == 'test-fail':
-        status_description = 'has failing tests'
+    """Open an issue about the toolstate failure."""
+    if status == "test-fail":
+        status_description = "has failing tests"
     else:
-        status_description = 'no longer builds'
-    request = json.dumps({
-        'body': maybe_remove_mention(textwrap.dedent('''\
+        status_description = "no longer builds"
+    request = json.dumps(
+        {
+            "body": maybe_remove_mention(
+                textwrap.dedent("""\
         Hello, this is your friendly neighborhood mergebot.
         After merging PR {}, I observed that the tool {} {}.
         A follow-up PR to the repository {} is needed to fix the fallout.
 
         cc @{}, do you think you would have time to do the follow-up work?
         If so, that would be great!
-        ''').format(
-            relevant_pr_number, tool, status_description,
-            REPOS.get(tool), relevant_pr_user
-        )),
-        'title': '`{}` no longer builds after {}'.format(tool, relevant_pr_number),
-        'assignees': list(assignees),
-        'labels': labels,
-    })
-    print("Creating issue:\n{}".format(request))
-    response = urllib2.urlopen(urllib2.Request(
-        gh_url(),
-        request.encode(),
-        {
-            'Authorization': 'token ' + github_token,
-            'Content-Type': 'application/json',
+        """).format(
+                    relevant_pr_number,
+                    tool,
+                    status_description,
+                    REPOS.get(tool),
+                    relevant_pr_user,
+                )
+            ),
+            "title": "`{}` no longer builds after {}".format(tool, relevant_pr_number),
+            "assignees": list(assignees),
+            "labels": labels,
         }
-    ))
+    )
+    print("Creating issue:\n{}".format(request))
+    response = urllib2.urlopen(
+        urllib2.Request(
+            gh_url(),
+            request.encode(),
+            {
+                "Authorization": "token " + github_token,
+                "Content-Type": "application/json",
+            },
+        )
+    )
     response.read()
 
 
@@ -145,27 +155,26 @@ def update_latest(
     github_token,
 ):
     # type: (str, str, str, str, str, str, str) -> str
-    '''Updates `_data/latest.json` to match build result of the given commit.
-    '''
-    with open('_data/latest.json', 'r+') as f:
+    """Updates `_data/latest.json` to match build result of the given commit."""
+    with open("_data/latest.json", "r+") as f:
         latest = json.load(f, object_pairs_hook=collections.OrderedDict)
 
         current_status = {
-            os_: read_current_status(current_commit, 'history/' + os_ + '.tsv')
-            for os_ in ['windows', 'linux']
+            os_: read_current_status(current_commit, "history/" + os_ + ".tsv")
+            for os_ in ["windows", "linux"]
         }
 
-        slug = 'rust-lang/rust'
-        message = textwrap.dedent('''\
+        slug = "rust-lang/rust"
+        message = textwrap.dedent("""\
             📣 Toolstate changed by {}!
 
             Tested on commit {}@{}.
             Direct link to PR: <{}>
 
-        ''').format(relevant_pr_number, slug, current_commit, relevant_pr_url)
+        """).format(relevant_pr_number, slug, current_commit, relevant_pr_url)
         anything_changed = False
         for status in latest:
-            tool = status['tool']
+            tool = status["tool"]
             changed = False
             create_issue_for_status = None  # set to the status that caused the issue
 
@@ -173,57 +182,70 @@ def update_latest(
                 old = status[os_]
                 new = s.get(tool, old)
                 status[os_] = new
-                maintainers = ' '.join('@'+name for name in MAINTAINERS.get(tool, ()))
+                maintainers = " ".join("@" + name for name in MAINTAINERS.get(tool, ()))
                 # comparing the strings, but they are ordered appropriately:
                 # "test-pass" > "test-fail" > "build-fail"
                 if new > old:
                     # things got fixed or at least the status quo improved
                     changed = True
-                    message += '🎉 {} on {}: {} → {} (cc {}).\n' \
-                        .format(tool, os_, old, new, maintainers)
+                    message += "🎉 {} on {}: {} → {} (cc {}).\n".format(
+                        tool, os_, old, new, maintainers
+                    )
                 elif new < old:
                     # tests or builds are failing and were not failing before
                     changed = True
-                    title = '💔 {} on {}: {} → {}' \
-                        .format(tool, os_, old, new)
-                    message += '{} (cc {}).\n' \
-                        .format(title, maintainers)
+                    title = "💔 {} on {}: {} → {}".format(tool, os_, old, new)
+                    message += "{} (cc {}).\n".format(title, maintainers)
                     # See if we need to create an issue.
                     # Create issue if things no longer build.
                     # (No issue for mere test failures to avoid spurious issues.)
-                    if new == 'build-fail':
+                    if new == "build-fail":
                         create_issue_for_status = new
 
             if create_issue_for_status is not None:
                 try:
                     issue(
-                        tool, create_issue_for_status, MAINTAINERS.get(tool, ()),
-                        relevant_pr_number, relevant_pr_user, LABELS.get(tool, []),
+                        tool,
+                        create_issue_for_status,
+                        MAINTAINERS.get(tool, ()),
+                        relevant_pr_number,
+                        relevant_pr_user,
+                        LABELS.get(tool, []),
                         github_token,
                     )
                 except HTTPError as e:
                     # network errors will simply end up not creating an issue, but that's better
                     # than failing the entire build job
-                    print("HTTPError when creating issue for status regression: {0}\n{1!r}"
-                          .format(e, e.read()))
+                    print(
+                        "HTTPError when creating issue for status regression: {0}\n{1!r}".format(
+                            e, e.read()
+                        )
+                    )
                 except IOError as e:
-                    print("I/O error when creating issue for status regression: {0}".format(e))
+                    print(
+                        "I/O error when creating issue for status regression: {0}".format(
+                            e
+                        )
+                    )
                 except:
-                    print("Unexpected error when creating issue for status regression: {0}"
-                          .format(sys.exc_info()[0]))
+                    print(
+                        "Unexpected error when creating issue for status regression: {0}".format(
+                            sys.exc_info()[0]
+                        )
+                    )
                     raise
 
             if changed:
-                status['commit'] = current_commit
-                status['datetime'] = current_datetime
+                status["commit"] = current_commit
+                status["datetime"] = current_datetime
                 anything_changed = True
 
         if not anything_changed:
-            return ''
+            return ""
 
         f.seek(0)
         f.truncate(0)
-        json.dump(latest, f, indent=4, separators=(',', ': '))
+        json.dump(latest, f, indent=4, separators=(",", ": "))
         return message
 
 
@@ -231,12 +253,12 @@ def update_latest(
 # There are variables declared within that are implicitly global; it is unknown
 # which ones precisely but at least this is true for `github_token`.
 try:
-    if __name__ != '__main__':
+    if __name__ != "__main__":
         exit(0)
 
     cur_commit = sys.argv[1]
     cur_datetime = datetime.datetime.now(datetime.timezone.utc).strftime(
-        '%Y-%m-%dT%H:%M:%SZ'
+        "%Y-%m-%dT%H:%M:%SZ"
     )
     cur_commit_msg = sys.argv[2]
     save_message_to_path = sys.argv[3]
@@ -244,21 +266,21 @@ try:
 
     # assume that PR authors are also owners of the repo where the branch lives
     relevant_pr_match = re.search(
-        r'Auto merge of #([0-9]+) - ([^:]+):[^,]+, r=(\S+)',
+        r"Auto merge of #([0-9]+) - ([^:]+):[^,]+, r=(\S+)",
         cur_commit_msg,
     )
     if relevant_pr_match:
         number = relevant_pr_match.group(1)
         relevant_pr_user = relevant_pr_match.group(2)
-        relevant_pr_number = 'rust-lang/rust#' + number
-        relevant_pr_url = 'https://github.com/rust-lang/rust/pull/' + number
+        relevant_pr_number = "rust-lang/rust#" + number
+        relevant_pr_url = "https://github.com/rust-lang/rust/pull/" + number
         pr_reviewer = relevant_pr_match.group(3)
     else:
-        number = '-1'
-        relevant_pr_user = 'ghost'
-        relevant_pr_number = '<unknown PR>'
-        relevant_pr_url = '<unknown>'
-        pr_reviewer = 'ghost'
+        number = "-1"
+        relevant_pr_user = "ghost"
+        relevant_pr_number = "<unknown PR>"
+        relevant_pr_url = "<unknown>"
+        pr_reviewer = "ghost"
 
     message = update_latest(
         cur_commit,
@@ -270,28 +292,30 @@ try:
         github_token,
     )
     if not message:
-        print('<Nothing changed>')
+        print("<Nothing changed>")
         sys.exit(0)
 
     print(message)
 
     if not github_token:
-        print('Dry run only, not committing anything')
+        print("Dry run only, not committing anything")
         sys.exit(0)
 
-    with open(save_message_to_path, 'w') as f:
+    with open(save_message_to_path, "w") as f:
         f.write(message)
 
     # Write the toolstate comment on the PR as well.
-    issue_url = gh_url() + '/{}/comments'.format(number)
-    response = urllib2.urlopen(urllib2.Request(
-        issue_url,
-        json.dumps({'body': maybe_remove_mention(message)}).encode(),
-        {
-            'Authorization': 'token ' + github_token,
-            'Content-Type': 'application/json',
-        }
-    ))
+    issue_url = gh_url() + "/{}/comments".format(number)
+    response = urllib2.urlopen(
+        urllib2.Request(
+            issue_url,
+            json.dumps({"body": maybe_remove_mention(message)}).encode(),
+            {
+                "Authorization": "token " + github_token,
+                "Content-Type": "application/json",
+            },
+        )
+    )
     response.read()
 except HTTPError as e:
     print("HTTPError: %s\n%r" % (e, e.read()))
diff --git a/src/tools/run-make-support/Cargo.toml b/src/tools/run-make-support/Cargo.toml
index 3c172b2d956..15ed03ad5c2 100644
--- a/src/tools/run-make-support/Cargo.toml
+++ b/src/tools/run-make-support/Cargo.toml
@@ -7,7 +7,7 @@ edition = "2021"
 bstr = "1.6.0"
 object = "0.36.2"
 similar = "2.5.0"
-wasmparser = { version = "0.216", default-features = false, features = ["std"] }
+wasmparser = { version = "0.219", default-features = false, features = ["std"] }
 regex = "1.8" # 1.8 to avoid memchr 2.6.0, as 2.5.0 is pinned in the workspace
 gimli = "0.31.0"
 build_helper = { path = "../../build_helper" }
diff --git a/src/tools/run-make-support/src/command.rs b/src/tools/run-make-support/src/command.rs
index 9e09527d6d0..e73413085fa 100644
--- a/src/tools/run-make-support/src/command.rs
+++ b/src/tools/run-make-support/src/command.rs
@@ -329,7 +329,7 @@ impl CompletedProcess {
     /// Checks that `stderr` does not contain the regex pattern `unexpected`.
     #[track_caller]
     pub fn assert_stderr_not_contains_regex<S: AsRef<str>>(&self, unexpected: S) -> &Self {
-        assert_not_contains_regex(&self.stdout_utf8(), unexpected);
+        assert_not_contains_regex(&self.stderr_utf8(), unexpected);
         self
     }
 
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 494daeca963..ffe10092cc2 100644
--- a/src/tools/run-make-support/src/external_deps/rustc.rs
+++ b/src/tools/run-make-support/src/external_deps/rustc.rs
@@ -227,6 +227,12 @@ impl Rustc {
         self
     }
 
+    /// Normalize the line number in the stderr output
+    pub fn ui_testing(&mut self) -> &mut Self {
+        self.cmd.arg(format!("-Zui-testing"));
+        self
+    }
+
     /// Specify the target triple, or a path to a custom target json spec file.
     pub fn target<S: AsRef<str>>(&mut self, target: S) -> &mut Self {
         let target = target.as_ref();
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/inert_attr_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/inert_attr_macro.rs
index 95dfe56ff54..9a7a1a01a09 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/inert_attr_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/inert_attr_macro.rs
@@ -695,7 +695,6 @@ pub const INERT_ATTRIBUTES: &[BuiltinAttribute] = &[
         template!(List: r#"cfg = "...", module = "...", kind = "...""#), DuplicatesOk,
     ),
     rustc_attr!(TEST, rustc_symbol_name, Normal, template!(Word), WarnFollowing),
-    rustc_attr!(TEST, rustc_polymorphize_error, Normal, template!(Word), WarnFollowing),
     rustc_attr!(TEST, rustc_def_path, Normal, template!(Word), WarnFollowing),
     rustc_attr!(TEST, rustc_mir, Normal, template!(List: "arg1, arg2, ..."), DuplicatesOk),
     gated!(
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs
index 266109765ab..b97dfb3b8ef 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs
@@ -3936,17 +3936,8 @@ The tracking issue for this feature is: [#117693]
 "##,
     },
     Lint {
-        label: "core_pattern_type",
-        description: r##"# `core_pattern_type`
-
-This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
-
-------------------------
-"##,
-    },
-    Lint {
-        label: "core_pattern_types",
-        description: r##"# `core_pattern_types`
+        label: "pattern_type_macro",
+        description: r##"# `pattern_type_macro`
 
 This feature has no tracking issue, and is therefore likely internal to the compiler, not being intended for general use.
 
diff --git a/src/tools/rust-analyzer/crates/profile/src/memory_usage.rs b/src/tools/rust-analyzer/crates/profile/src/memory_usage.rs
index 660afff3dca..006748ddb08 100644
--- a/src/tools/rust-analyzer/crates/profile/src/memory_usage.rs
+++ b/src/tools/rust-analyzer/crates/profile/src/memory_usage.rs
@@ -62,15 +62,13 @@ fn memusage_linux() -> MemoryUsage {
     // mallinfo2 is very recent, so its presence needs to be detected at runtime.
     // Both are abysmally slow.
 
-    use std::ffi::CStr;
     use std::sync::atomic::{AtomicUsize, Ordering};
 
     static MALLINFO2: AtomicUsize = AtomicUsize::new(1);
 
     let mut mallinfo2 = MALLINFO2.load(Ordering::Relaxed);
     if mallinfo2 == 1 {
-        let cstr = CStr::from_bytes_with_nul(b"mallinfo2\0").unwrap();
-        mallinfo2 = unsafe { libc::dlsym(libc::RTLD_DEFAULT, cstr.as_ptr()) } as usize;
+        mallinfo2 = unsafe { libc::dlsym(libc::RTLD_DEFAULT, c"mallinfo2".as_ptr()) } as usize;
         // NB: races don't matter here, since they'll always store the same value
         MALLINFO2.store(mallinfo2, Ordering::Relaxed);
     }
diff --git a/src/tools/rustbook/Cargo.lock b/src/tools/rustbook/Cargo.lock
index 400eb7c5e0d..e8c9c4f4cd1 100644
--- a/src/tools/rustbook/Cargo.lock
+++ b/src/tools/rustbook/Cargo.lock
@@ -47,9 +47,9 @@ dependencies = [
 
 [[package]]
 name = "anstream"
-version = "0.6.17"
+version = "0.6.18"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "23a1e53f0f5d86382dafe1cf314783b2044280f406e7e1506368220ad11b1338"
+checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b"
 dependencies = [
  "anstyle",
  "anstyle-parse",
@@ -96,9 +96,9 @@ dependencies = [
 
 [[package]]
 name = "anyhow"
-version = "1.0.92"
+version = "1.0.93"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "74f37166d7d48a0284b99dd824694c26119c700b53bf0d1540cdb147dbdaaf13"
+checksum = "4c95c10ba0b00a02636238b814946408b1322d5ac4760326e6fb8ec956d85775"
 
 [[package]]
 name = "autocfg"
@@ -138,9 +138,9 @@ dependencies = [
 
 [[package]]
 name = "bstr"
-version = "1.10.0"
+version = "1.11.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "40723b8fb387abc38f4f4a37c09073622e41dd12327033091ef8950659e6dc0c"
+checksum = "1a68f1f47cdf0ec8ee4b941b2eee2a80cb796db73118c0dd09ac63fbe405be22"
 dependencies = [
  "memchr",
  "regex-automata",
@@ -190,9 +190,9 @@ dependencies = [
 
 [[package]]
 name = "clap"
-version = "4.5.20"
+version = "4.5.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8"
+checksum = "fb3b4b9e5a7c7514dfa52869339ee98b3156b0bfb4e8a77c4ff4babb64b1604f"
 dependencies = [
  "clap_builder",
  "clap_derive",
@@ -200,9 +200,9 @@ dependencies = [
 
 [[package]]
 name = "clap_builder"
-version = "4.5.20"
+version = "4.5.21"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54"
+checksum = "b17a95aa67cc7b5ebd32aa5370189aa0d79069ef1c64ce893bd30fb24bff20ec"
 dependencies = [
  "anstream",
  "anstyle",
@@ -213,9 +213,9 @@ dependencies = [
 
 [[package]]
 name = "clap_complete"
-version = "4.5.36"
+version = "4.5.38"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "86bc73de94bc81e52f3bebec71bc4463e9748f7a59166663e32044669577b0e2"
+checksum = "d9647a559c112175f17cf724dc72d3645680a883c58481332779192b0d8e7a01"
 dependencies = [
  "clap",
 ]
@@ -234,9 +234,9 @@ dependencies = [
 
 [[package]]
 name = "clap_lex"
-version = "0.7.2"
+version = "0.7.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
+checksum = "afb84c814227b90d6895e01398aee0d8033c00e7466aca416fb6a8e0eb19d8a7"
 
 [[package]]
 name = "colorchoice"
@@ -252,9 +252,9 @@ checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
 
 [[package]]
 name = "cpufeatures"
-version = "0.2.14"
+version = "0.2.16"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0"
+checksum = "16b80225097f2e5ae4e7179dd2266824648f3e2f49d9134d584b76389d31c4c3"
 dependencies = [
  "libc",
 ]
@@ -312,6 +312,17 @@ dependencies = [
 ]
 
 [[package]]
+name = "displaydoc"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
 name = "doc-comment"
 version = "0.3.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -360,25 +371,25 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
 
 [[package]]
 name = "errno"
-version = "0.3.9"
+version = "0.3.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
+checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
 dependencies = [
  "libc",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
 name = "fastrand"
-version = "2.1.1"
+version = "2.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
+checksum = "486f806e73c5707928240ddc295403b1b93c96a02038563881c4a2fd84b81ac4"
 
 [[package]]
 name = "flate2"
-version = "1.0.34"
+version = "1.0.35"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1b589b4dc103969ad3cf85c950899926ec64300a1a46d76c03a6072957036f0"
+checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c"
 dependencies = [
  "crc32fast",
  "miniz_oxide",
@@ -456,9 +467,9 @@ dependencies = [
 
 [[package]]
 name = "hashbrown"
-version = "0.15.0"
+version = "0.15.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb"
+checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
 
 [[package]]
 name = "heck"
@@ -525,20 +536,149 @@ dependencies = [
 ]
 
 [[package]]
+name = "icu_collections"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
+dependencies = [
+ "displaydoc",
+ "yoke",
+ "zerofrom",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locid"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637"
+dependencies = [
+ "displaydoc",
+ "litemap",
+ "tinystr",
+ "writeable",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locid_transform"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e"
+dependencies = [
+ "displaydoc",
+ "icu_locid",
+ "icu_locid_transform_data",
+ "icu_provider",
+ "tinystr",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locid_transform_data"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e"
+
+[[package]]
+name = "icu_normalizer"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f"
+dependencies = [
+ "displaydoc",
+ "icu_collections",
+ "icu_normalizer_data",
+ "icu_properties",
+ "icu_provider",
+ "smallvec",
+ "utf16_iter",
+ "utf8_iter",
+ "write16",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer_data"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516"
+
+[[package]]
+name = "icu_properties"
+version = "1.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5"
+dependencies = [
+ "displaydoc",
+ "icu_collections",
+ "icu_locid_transform",
+ "icu_properties_data",
+ "icu_provider",
+ "tinystr",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_properties_data"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569"
+
+[[package]]
+name = "icu_provider"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9"
+dependencies = [
+ "displaydoc",
+ "icu_locid",
+ "icu_provider_macros",
+ "stable_deref_trait",
+ "tinystr",
+ "writeable",
+ "yoke",
+ "zerofrom",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_provider_macros"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
 name = "idna"
-version = "0.5.0"
+version = "1.0.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e"
+dependencies = [
+ "idna_adapter",
+ "smallvec",
+ "utf8_iter",
+]
+
+[[package]]
+name = "idna_adapter"
+version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6"
+checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71"
 dependencies = [
- "unicode-bidi",
- "unicode-normalization",
+ "icu_normalizer",
+ "icu_properties",
 ]
 
 [[package]]
 name = "indexmap"
-version = "2.6.0"
+version = "2.7.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
+checksum = "62f822373a4fe84d4bb149bf54e584a7f4abec90e072ed49cda0edea5b95471f"
 dependencies = [
  "equivalent",
  "hashbrown",
@@ -552,16 +692,17 @@ checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
 
 [[package]]
 name = "itoa"
-version = "1.0.11"
+version = "1.0.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
+checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674"
 
 [[package]]
 name = "js-sys"
-version = "0.3.72"
+version = "0.3.74"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9"
+checksum = "a865e038f7f6ed956f788f0d7d60c541fff74c7bd74272c5d4cf15c63743e705"
 dependencies = [
+ "once_cell",
  "wasm-bindgen",
 ]
 
@@ -573,9 +714,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
 
 [[package]]
 name = "libc"
-version = "0.2.161"
+version = "0.2.167"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
+checksum = "09d6582e104315a817dff97f75133544b2e094ee22447d2acf4a74e189ba06fc"
 
 [[package]]
 name = "libdbus-sys"
@@ -603,6 +744,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89"
 
 [[package]]
+name = "litemap"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104"
+
+[[package]]
 name = "lock_api"
 version = "0.4.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -646,9 +793,9 @@ dependencies = [
 
 [[package]]
 name = "mdbook"
-version = "0.4.42"
+version = "0.4.43"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7624879735513024d323e7267a0b3a7176aceb0db537939beb4ee31d9e8945e3"
+checksum = "fe1f98b8d66e537d2f0ba06e7dec4f44001deec539a2d18bfc102d6a86189148"
 dependencies = [
  "ammonia",
  "anyhow",
@@ -709,31 +856,21 @@ dependencies = [
 ]
 
 [[package]]
-name = "mdbook-trpl-listing"
+name = "mdbook-trpl"
 version = "0.1.0"
 dependencies = [
+ "anyhow",
  "clap",
  "html_parser",
  "mdbook",
- "pulldown-cmark 0.10.3",
- "pulldown-cmark-to-cmark 13.0.0",
+ "pulldown-cmark 0.12.2",
+ "pulldown-cmark-to-cmark 19.0.0",
  "serde_json",
  "thiserror",
  "toml 0.8.19",
 ]
 
 [[package]]
-name = "mdbook-trpl-note"
-version = "1.0.0"
-dependencies = [
- "clap",
- "mdbook",
- "pulldown-cmark 0.10.3",
- "pulldown-cmark-to-cmark 13.0.0",
- "serde_json",
-]
-
-[[package]]
 name = "memchr"
 version = "2.7.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -852,9 +989,9 @@ dependencies = [
 
 [[package]]
 name = "pathdiff"
-version = "0.2.2"
+version = "0.2.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d61c5ce1153ab5b689d0c074c4e7fc613e942dfb7dd9eea5ab202d2ad91fe361"
+checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
 
 [[package]]
 name = "percent-encoding"
@@ -996,9 +1133,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.89"
+version = "1.0.92"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
+checksum = "37d3544b3f2748c54e147655edb5025752e2303145b5aefb3c3ea2c78b973bb0"
 dependencies = [
  "unicode-ident",
 ]
@@ -1010,7 +1147,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "76979bea66e7875e7509c4ec5300112b316af87fa7a252ca91c448b32dfe3993"
 dependencies = [
  "bitflags 2.6.0",
- "getopts",
  "memchr",
  "pulldown-cmark-escape 0.10.1",
  "unicase",
@@ -1029,6 +1165,19 @@ dependencies = [
 ]
 
 [[package]]
+name = "pulldown-cmark"
+version = "0.12.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f86ba2052aebccc42cbbb3ed234b8b13ce76f75c3551a303cb2bcffcff12bb14"
+dependencies = [
+ "bitflags 2.6.0",
+ "getopts",
+ "memchr",
+ "pulldown-cmark-escape 0.11.0",
+ "unicase",
+]
+
+[[package]]
 name = "pulldown-cmark-escape"
 version = "0.10.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1042,20 +1191,20 @@ checksum = "007d8adb5ddab6f8e3f491ac63566a7d5002cc7ed73901f72057943fa71ae1ae"
 
 [[package]]
 name = "pulldown-cmark-to-cmark"
-version = "13.0.0"
+version = "15.0.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f609795c8d835f79dcfcf768415b9fb57ef1b74891e99f86e73f43a7a257163b"
+checksum = "b9c77db841443d89a57ae94f22d29c022f6d9f41b00bddbf1f4024dbaf4bdce1"
 dependencies = [
- "pulldown-cmark 0.10.3",
+ "pulldown-cmark 0.11.3",
 ]
 
 [[package]]
 name = "pulldown-cmark-to-cmark"
-version = "15.0.1"
+version = "19.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9c77db841443d89a57ae94f22d29c022f6d9f41b00bddbf1f4024dbaf4bdce1"
+checksum = "7d742adcc7b655dba3e9ebab47954ca229fc0fa1df01fdc94349b6f3a2e6d257"
 dependencies = [
- "pulldown-cmark 0.11.3",
+ "pulldown-cmark 0.12.2",
 ]
 
 [[package]]
@@ -1120,9 +1269,9 @@ dependencies = [
 
 [[package]]
 name = "regex-automata"
-version = "0.4.8"
+version = "0.4.9"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
+checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -1144,15 +1293,14 @@ dependencies = [
  "mdbook",
  "mdbook-i18n-helpers",
  "mdbook-spec",
- "mdbook-trpl-listing",
- "mdbook-trpl-note",
+ "mdbook-trpl",
 ]
 
 [[package]]
 name = "rustix"
-version = "0.38.38"
+version = "0.38.41"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa260229e6538e52293eeb577aabd09945a09d6d9cc0fc550ed7529056c2e32a"
+checksum = "d7f649912bc1495e167a6edee79151c84b1bad49748cb4f1f1167f459f6224f6"
 dependencies = [
  "bitflags 2.6.0",
  "errno",
@@ -1190,18 +1338,18 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
 
 [[package]]
 name = "serde"
-version = "1.0.214"
+version = "1.0.215"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5"
+checksum = "6513c1ad0b11a9376da888e3e0baa0077f1aed55c17f50e7b2397136129fb88f"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.214"
+version = "1.0.215"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766"
+checksum = "ad1e866f866923f252f05c889987993144fb74e722403468a4ebd70c3cd756c0"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1210,9 +1358,9 @@ dependencies = [
 
 [[package]]
 name = "serde_json"
-version = "1.0.132"
+version = "1.0.133"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
+checksum = "c7fceb2473b9166b2294ef05efcb65a3db80803f0b03ef86a5fc88a2b85ee377"
 dependencies = [
  "itoa",
  "memchr",
@@ -1259,6 +1407,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67"
 
 [[package]]
+name = "stable_deref_trait"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
+
+[[package]]
 name = "string_cache"
 version = "0.8.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1292,9 +1446,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
 
 [[package]]
 name = "syn"
-version = "2.0.87"
+version = "2.0.90"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
+checksum = "919d3b74a5dd0ccd15aeb8f93e7006bd9e14c295087c9896a110f490752bcf31"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1302,6 +1456,17 @@ dependencies = [
 ]
 
 [[package]]
+name = "synstructure"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
 name = "syntect"
 version = "5.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1323,9 +1488,9 @@ dependencies = [
 
 [[package]]
 name = "tempfile"
-version = "3.13.0"
+version = "3.14.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b"
+checksum = "28cce251fcbc87fac86a866eeb0d6c2d536fc16d06f184bb61aeae11aa4cee0c"
 dependencies = [
  "cfg-if",
  "fastrand",
@@ -1347,9 +1512,9 @@ dependencies = [
 
 [[package]]
 name = "terminal_size"
-version = "0.4.0"
+version = "0.4.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4f599bd7ca042cfdf8f4512b277c02ba102247820f9d9d4a9f521f496751a6ef"
+checksum = "5352447f921fda68cf61b4101566c0bdb5104eff6804d0678e5227580ab6a4e9"
 dependencies = [
  "rustix",
  "windows-sys 0.59.0",
@@ -1363,18 +1528,18 @@ checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9"
 
 [[package]]
 name = "thiserror"
-version = "1.0.66"
+version = "1.0.69"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5d171f59dbaa811dbbb1aee1e73db92ec2b122911a48e1390dfe327a821ddede"
+checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52"
 dependencies = [
  "thiserror-impl",
 ]
 
 [[package]]
 name = "thiserror-impl"
-version = "1.0.66"
+version = "1.0.69"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b08be0f17bd307950653ce45db00cd31200d82b624b36e181337d9c7d92765b5"
+checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1382,21 +1547,16 @@ dependencies = [
 ]
 
 [[package]]
-name = "tinyvec"
-version = "1.8.0"
+name = "tinystr"
+version = "0.7.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "445e881f4f6d382d5f27c034e25eb92edd7c784ceab92a0937db7f2e9471b938"
+checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
 dependencies = [
- "tinyvec_macros",
+ "displaydoc",
+ "zerovec",
 ]
 
 [[package]]
-name = "tinyvec_macros"
-version = "0.1.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
-
-[[package]]
 name = "toml"
 version = "0.5.11"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1464,25 +1624,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df"
 
 [[package]]
-name = "unicode-bidi"
-version = "0.3.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893"
-
-[[package]]
 name = "unicode-ident"
-version = "1.0.13"
+version = "1.0.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
-
-[[package]]
-name = "unicode-normalization"
-version = "0.1.24"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956"
-dependencies = [
- "tinyvec",
-]
+checksum = "adb9e6ca4f869e1180728b7950e35922a7fc6397f7b641499e8f3ef06e50dc83"
 
 [[package]]
 name = "unicode-width"
@@ -1492,9 +1637,9 @@ checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
 
 [[package]]
 name = "url"
-version = "2.5.2"
+version = "2.5.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "22784dbdf76fdde8af1aeda5622b546b422b6fc585325248a2bf9f5e41e94d6c"
+checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60"
 dependencies = [
  "form_urlencoded",
  "idna",
@@ -1508,6 +1653,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
 
 [[package]]
+name = "utf16_iter"
+version = "1.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
+
+[[package]]
+name = "utf8_iter"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
+
+[[package]]
 name = "utf8parse"
 version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1537,9 +1694,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
 [[package]]
 name = "wasm-bindgen"
-version = "0.2.95"
+version = "0.2.97"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e"
+checksum = "d15e63b4482863c109d70a7b8706c1e364eb6ea449b201a76c5b89cedcec2d5c"
 dependencies = [
  "cfg-if",
  "once_cell",
@@ -1548,9 +1705,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-backend"
-version = "0.2.95"
+version = "0.2.97"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358"
+checksum = "8d36ef12e3aaca16ddd3f67922bc63e48e953f126de60bd33ccc0101ef9998cd"
 dependencies = [
  "bumpalo",
  "log",
@@ -1563,9 +1720,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.95"
+version = "0.2.97"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56"
+checksum = "705440e08b42d3e4b36de7d66c944be628d579796b8090bfa3471478a2260051"
 dependencies = [
  "quote",
  "wasm-bindgen-macro-support",
@@ -1573,9 +1730,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.95"
+version = "0.2.97"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68"
+checksum = "98c9ae5a76e46f4deecd0f0255cc223cfa18dc9b261213b8aa0c7b36f61b3f1d"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1586,9 +1743,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.95"
+version = "0.2.97"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d"
+checksum = "6ee99da9c5ba11bd675621338ef6fa52296b76b83305e9b6e5c77d4c286d6d49"
 
 [[package]]
 name = "winapi"
@@ -1722,6 +1879,42 @@ dependencies = [
 ]
 
 [[package]]
+name = "write16"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
+
+[[package]]
+name = "writeable"
+version = "0.5.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
+
+[[package]]
+name = "yoke"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40"
+dependencies = [
+ "serde",
+ "stable_deref_trait",
+ "yoke-derive",
+ "zerofrom",
+]
+
+[[package]]
+name = "yoke-derive"
+version = "0.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "synstructure",
+]
+
+[[package]]
 name = "zerocopy"
 version = "0.7.35"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1741,3 +1934,46 @@ dependencies = [
  "quote",
  "syn",
 ]
+
+[[package]]
+name = "zerofrom"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e"
+dependencies = [
+ "zerofrom-derive",
+]
+
+[[package]]
+name = "zerofrom-derive"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "synstructure",
+]
+
+[[package]]
+name = "zerovec"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079"
+dependencies = [
+ "yoke",
+ "zerofrom",
+ "zerovec-derive",
+]
+
+[[package]]
+name = "zerovec-derive"
+version = "0.10.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
diff --git a/src/tools/rustbook/Cargo.toml b/src/tools/rustbook/Cargo.toml
index 854c4547337..c2ce8fef4d0 100644
--- a/src/tools/rustbook/Cargo.toml
+++ b/src/tools/rustbook/Cargo.toml
@@ -9,8 +9,7 @@ edition = "2021"
 [dependencies]
 clap = "4.0.32"
 env_logger = "0.11"
-mdbook-trpl-listing = { path = "../../doc/book/packages/mdbook-trpl-listing" }
-mdbook-trpl-note = { path = "../../doc/book/packages/mdbook-trpl-note" }
+mdbook-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/rustbook/src/main.rs b/src/tools/rustbook/src/main.rs
index a1ef18610b0..33f2a51215d 100644
--- a/src/tools/rustbook/src/main.rs
+++ b/src/tools/rustbook/src/main.rs
@@ -6,8 +6,7 @@ use mdbook::MDBook;
 use mdbook::errors::Result as Result3;
 use mdbook_i18n_helpers::preprocessors::Gettext;
 use mdbook_spec::Spec;
-use mdbook_trpl_listing::TrplListing;
-use mdbook_trpl_note::TrplNote;
+use mdbook_trpl::{Figure, Listing, Note};
 
 fn main() {
     let crate_version = concat!("v", crate_version!());
@@ -109,11 +108,15 @@ pub fn build(args: &ArgMatches) -> Result3<()> {
     // preprocessor, or this should modify the config and use
     // MDBook::load_with_config.
     if book.config.get_preprocessor("trpl-note").is_some() {
-        book.with_preprocessor(TrplNote);
+        book.with_preprocessor(Note);
     }
 
     if book.config.get_preprocessor("trpl-listing").is_some() {
-        book.with_preprocessor(TrplListing);
+        book.with_preprocessor(Listing);
+    }
+
+    if book.config.get_preprocessor("trpl-figure").is_some() {
+        book.with_preprocessor(Figure);
     }
 
     if book.config.get_preprocessor("spec").is_some() {
diff --git a/src/tools/rustfmt/src/parse/session.rs b/src/tools/rustfmt/src/parse/session.rs
index 54ad56ed3f3..63cc8794cea 100644
--- a/src/tools/rustfmt/src/parse/session.rs
+++ b/src/tools/rustfmt/src/parse/session.rs
@@ -3,6 +3,7 @@ use std::sync::atomic::{AtomicBool, Ordering};
 
 use rustc_data_structures::sync::{IntoDynSyncSend, Lrc};
 use rustc_errors::emitter::{DynEmitter, Emitter, HumanEmitter, SilentEmitter, stderr_destination};
+use rustc_errors::registry::Registry;
 use rustc_errors::translation::Translate;
 use rustc_errors::{ColorConfig, Diag, DiagCtxt, DiagInner, Level as DiagnosticLevel};
 use rustc_session::parse::ParseSess as RawParseSess;
@@ -38,10 +39,10 @@ struct SilentOnIgnoredFilesEmitter {
 }
 
 impl SilentOnIgnoredFilesEmitter {
-    fn handle_non_ignoreable_error(&mut self, diag: DiagInner) {
+    fn handle_non_ignoreable_error(&mut self, diag: DiagInner, registry: &Registry) {
         self.has_non_ignorable_parser_errors = true;
         self.can_reset.store(false, Ordering::Release);
-        self.emitter.emit_diagnostic(diag);
+        self.emitter.emit_diagnostic(diag, registry);
     }
 }
 
@@ -60,9 +61,9 @@ impl Emitter for SilentOnIgnoredFilesEmitter {
         None
     }
 
-    fn emit_diagnostic(&mut self, diag: DiagInner) {
+    fn emit_diagnostic(&mut self, diag: DiagInner, registry: &Registry) {
         if diag.level() == DiagnosticLevel::Fatal {
-            return self.handle_non_ignoreable_error(diag);
+            return self.handle_non_ignoreable_error(diag, registry);
         }
         if let Some(primary_span) = &diag.span.primary_span() {
             let file_name = self.source_map.span_to_filename(*primary_span);
@@ -80,7 +81,7 @@ impl Emitter for SilentOnIgnoredFilesEmitter {
                 }
             };
         }
-        self.handle_non_ignoreable_error(diag);
+        self.handle_non_ignoreable_error(diag, registry);
     }
 }
 
@@ -358,7 +359,7 @@ mod tests {
                 None
             }
 
-            fn emit_diagnostic(&mut self, _diag: DiagInner) {
+            fn emit_diagnostic(&mut self, _diag: DiagInner, _registry: &Registry) {
                 self.num_emitted_errors.fetch_add(1, Ordering::Release);
             }
         }
@@ -412,6 +413,7 @@ mod tests {
                 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
                 source,
             );
+            let registry = Registry::new(&[]);
             let mut emitter = build_emitter(
                 Lrc::clone(&num_emitted_errors),
                 Lrc::clone(&can_reset_errors),
@@ -420,7 +422,7 @@ mod tests {
             );
             let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
             let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, Some(span));
-            emitter.emit_diagnostic(fatal_diagnostic);
+            emitter.emit_diagnostic(fatal_diagnostic, &registry);
             assert_eq!(num_emitted_errors.load(Ordering::Acquire), 1);
             assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
         }
@@ -437,6 +439,7 @@ mod tests {
                 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
                 source,
             );
+            let registry = Registry::new(&[]);
             let mut emitter = build_emitter(
                 Lrc::clone(&num_emitted_errors),
                 Lrc::clone(&can_reset_errors),
@@ -445,7 +448,7 @@ mod tests {
             );
             let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
             let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span));
-            emitter.emit_diagnostic(non_fatal_diagnostic);
+            emitter.emit_diagnostic(non_fatal_diagnostic, &registry);
             assert_eq!(num_emitted_errors.load(Ordering::Acquire), 0);
             assert_eq!(can_reset_errors.load(Ordering::Acquire), true);
         }
@@ -461,6 +464,7 @@ mod tests {
                 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("foo.rs"))),
                 source,
             );
+            let registry = Registry::new(&[]);
             let mut emitter = build_emitter(
                 Lrc::clone(&num_emitted_errors),
                 Lrc::clone(&can_reset_errors),
@@ -469,7 +473,7 @@ mod tests {
             );
             let span = MultiSpan::from_span(mk_sp(BytePos(0), BytePos(1)));
             let non_fatal_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(span));
-            emitter.emit_diagnostic(non_fatal_diagnostic);
+            emitter.emit_diagnostic(non_fatal_diagnostic, &registry);
             assert_eq!(num_emitted_errors.load(Ordering::Acquire), 1);
             assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
         }
@@ -497,6 +501,7 @@ mod tests {
                 SourceMapFileName::Real(RealFileName::LocalPath(PathBuf::from("fatal.rs"))),
                 fatal_source,
             );
+            let registry = Registry::new(&[]);
             let mut emitter = build_emitter(
                 Lrc::clone(&num_emitted_errors),
                 Lrc::clone(&can_reset_errors),
@@ -508,9 +513,9 @@ mod tests {
             let bar_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(bar_span));
             let foo_diagnostic = build_diagnostic(DiagnosticLevel::Warning, Some(foo_span));
             let fatal_diagnostic = build_diagnostic(DiagnosticLevel::Fatal, None);
-            emitter.emit_diagnostic(bar_diagnostic);
-            emitter.emit_diagnostic(foo_diagnostic);
-            emitter.emit_diagnostic(fatal_diagnostic);
+            emitter.emit_diagnostic(bar_diagnostic, &registry);
+            emitter.emit_diagnostic(foo_diagnostic, &registry);
+            emitter.emit_diagnostic(fatal_diagnostic, &registry);
             assert_eq!(num_emitted_errors.load(Ordering::Acquire), 2);
             assert_eq!(can_reset_errors.load(Ordering::Acquire), false);
         }
diff --git a/src/tools/rustfmt/src/patterns.rs b/src/tools/rustfmt/src/patterns.rs
index 6fe2d4a8520..7b4730eadc8 100644
--- a/src/tools/rustfmt/src/patterns.rs
+++ b/src/tools/rustfmt/src/patterns.rs
@@ -48,7 +48,8 @@ fn is_short_pattern_inner(pat: &ast::Pat) -> bool {
         | ast::PatKind::MacCall(..)
         | ast::PatKind::Slice(..)
         | ast::PatKind::Path(..)
-        | ast::PatKind::Range(..) => false,
+        | ast::PatKind::Range(..)
+        | ast::PatKind::Guard(..) => false,
         ast::PatKind::Tuple(ref subpats) => subpats.len() <= 1,
         ast::PatKind::TupleStruct(_, ref path, ref subpats) => {
             path.segments.len() <= 1 && subpats.len() <= 1
@@ -338,8 +339,9 @@ impl Rewrite for Pat {
                         .max_width_error(shape.width, self.span)?,
                 )
                 .map(|inner_pat| format!("({})", inner_pat)),
-            PatKind::Err(_) => Err(RewriteError::Unknown),
+            PatKind::Guard(..) => Ok(context.snippet(self.span).to_string()),
             PatKind::Deref(_) => Err(RewriteError::Unknown),
+            PatKind::Err(_) => Err(RewriteError::Unknown),
         }
     }
 }
diff --git a/src/tools/rustfmt/tests/target/guard_patterns.rs b/src/tools/rustfmt/tests/target/guard_patterns.rs
new file mode 100644
index 00000000000..2e4667b916c
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/guard_patterns.rs
@@ -0,0 +1,12 @@
+#![feature(guard_patterns)]
+
+fn main() {
+    match user.subscription_plan() {
+        (Plan::Regular if user.credit() >= 100) | (Plan::Premium if user.credit() >= 80) => {
+            // Complete the transaction.
+        }
+        _ => {
+            // The user doesn't have enough credit, return an error message.
+        }
+    }
+}
diff --git a/src/tools/tidy/config/black.toml b/src/tools/tidy/config/black.toml
deleted file mode 100644
index d5b8b198afb..00000000000
--- a/src/tools/tidy/config/black.toml
+++ /dev/null
@@ -1,18 +0,0 @@
-[tool.black]
-# Ignore all submodules
-extend-exclude = """(\
-    src/doc/nomicon|\
-    src/tools/cargo/|\
-    src/doc/reference/|\
-    src/doc/book/|\
-    src/doc/rust-by-example/|\
-    library/stdarch/|\
-    src/doc/rustc-dev-guide/|\
-    src/doc/edition-guide/|\
-    src/llvm-project/|\
-    src/doc/embedded-book/|\
-    src/tools/rustc-perf/|\
-    src/tools/enzyme/|\
-    library/backtrace/|\
-    src/gcc/
-    )"""
diff --git a/src/tools/tidy/config/requirements.in b/src/tools/tidy/config/requirements.in
index 8938dc03243..1b2c38f2b5d 100644
--- a/src/tools/tidy/config/requirements.in
+++ b/src/tools/tidy/config/requirements.in
@@ -6,6 +6,5 @@
 # Note: this generation step should be run with the oldest supported python
 # version (currently 3.9) to ensure backward compatibility
 
-black==24.4.2
 ruff==0.4.9
 clang-format==18.1.7
diff --git a/src/tools/tidy/config/requirements.txt b/src/tools/tidy/config/requirements.txt
index 790eabf5cf8..938179d5b3e 100644
--- a/src/tools/tidy/config/requirements.txt
+++ b/src/tools/tidy/config/requirements.txt
@@ -4,30 +4,6 @@
 #
 #    pip-compile --generate-hashes --strip-extras src/tools/tidy/config/requirements.in
 #
-black==24.4.2 \
-    --hash=sha256:257d724c2c9b1660f353b36c802ccece186a30accc7742c176d29c146df6e474 \
-    --hash=sha256:37aae07b029fa0174d39daf02748b379399b909652a806e5708199bd93899da1 \
-    --hash=sha256:415e686e87dbbe6f4cd5ef0fbf764af7b89f9057b97c908742b6008cc554b9c0 \
-    --hash=sha256:48a85f2cb5e6799a9ef05347b476cce6c182d6c71ee36925a6c194d074336ef8 \
-    --hash=sha256:7768a0dbf16a39aa5e9a3ded568bb545c8c2727396d063bbaf847df05b08cd96 \
-    --hash=sha256:7e122b1c4fb252fd85df3ca93578732b4749d9be076593076ef4d07a0233c3e1 \
-    --hash=sha256:88c57dc656038f1ab9f92b3eb5335ee9b021412feaa46330d5eba4e51fe49b04 \
-    --hash=sha256:8e537d281831ad0e71007dcdcbe50a71470b978c453fa41ce77186bbe0ed6021 \
-    --hash=sha256:98e123f1d5cfd42f886624d84464f7756f60ff6eab89ae845210631714f6db94 \
-    --hash=sha256:accf49e151c8ed2c0cdc528691838afd217c50412534e876a19270fea1e28e2d \
-    --hash=sha256:b1530ae42e9d6d5b670a34db49a94115a64596bc77710b1d05e9801e62ca0a7c \
-    --hash=sha256:b9176b9832e84308818a99a561e90aa479e73c523b3f77afd07913380ae2eab7 \
-    --hash=sha256:bdde6f877a18f24844e381d45e9947a49e97933573ac9d4345399be37621e26c \
-    --hash=sha256:be8bef99eb46d5021bf053114442914baeb3649a89dc5f3a555c88737e5e98fc \
-    --hash=sha256:bf10f7310db693bb62692609b397e8d67257c55f949abde4c67f9cc574492cc7 \
-    --hash=sha256:c872b53057f000085da66a19c55d68f6f8ddcac2642392ad3a355878406fbd4d \
-    --hash=sha256:d36ed1124bb81b32f8614555b34cc4259c3fbc7eec17870e8ff8ded335b58d8c \
-    --hash=sha256:da33a1a5e49c4122ccdfd56cd021ff1ebc4a1ec4e2d01594fef9b6f267a9e741 \
-    --hash=sha256:dd1b5a14e417189db4c7b64a6540f31730713d173f0b63e55fabd52d61d8fdce \
-    --hash=sha256:e151054aa00bad1f4e1f04919542885f89f5f7d086b8a59e5000e6c616896ffb \
-    --hash=sha256:eaea3008c281f1038edb473c1aa8ed8143a5535ff18f978a318f10302b254063 \
-    --hash=sha256:ef703f83fc32e131e9bcc0a5094cfe85599e7109f896fe8bc96cc402f3eb4b6e
-    # via -r src/tools/tidy/config/requirements.in
 clang-format==18.1.7 \
     --hash=sha256:035204410f65d03f98cb81c9c39d6d193f9987917cc88de9d0dbd01f2aa9c302 \
     --hash=sha256:05c482a854287a5d21f7567186c0bd4b8dbd4a871751e655a45849185f30b931 \
@@ -45,26 +21,6 @@ clang-format==18.1.7 \
     --hash=sha256:f4f77ac0f4f9a659213fedda0f2d216886c410132e6e7dd4b13f92b34e925554 \
     --hash=sha256:f935d34152a2e11e55120eb9182862f432bc9789ab819f680c9f6db4edebf9e3
     # via -r src/tools/tidy/config/requirements.in
-click==8.1.3 \
-    --hash=sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e \
-    --hash=sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48
-    # via black
-mypy-extensions==1.0.0 \
-    --hash=sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d \
-    --hash=sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782
-    # via black
-packaging==23.1 \
-    --hash=sha256:994793af429502c4ea2ebf6bf664629d07c1a9fe974af92966e4b8d2df7edc61 \
-    --hash=sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f
-    # via black
-pathspec==0.11.1 \
-    --hash=sha256:2798de800fa92780e33acca925945e9a19a133b715067cf165b8866c15a31687 \
-    --hash=sha256:d8af70af76652554bd134c22b3e8a1cc46ed7d91edcdd721ef1a0c51a84a5293
-    # via black
-platformdirs==4.2.2 \
-    --hash=sha256:2d7a1657e36a80ea911db832a8a6ece5ee53d8de21edd5cc5879af6530b1bfee \
-    --hash=sha256:38b7b51f512eed9e84a22788b4bce1de17c0adb134d6becb09836e37d8654cd3
-    # via black
 ruff==0.4.9 \
     --hash=sha256:06b60f91bfa5514bb689b500a25ba48e897d18fea14dce14b48a0c40d1635893 \
     --hash=sha256:0e8e7b95673f22e0efd3571fb5b0cf71a5eaaa3cc8a776584f3b2cc878e46bff \
@@ -84,11 +40,3 @@ ruff==0.4.9 \
     --hash=sha256:e91175fbe48f8a2174c9aad70438fe9cb0a5732c4159b2a10a3565fea2d94cde \
     --hash=sha256:f1cb0828ac9533ba0135d148d214e284711ede33640465e706772645483427e3
     # via -r src/tools/tidy/config/requirements.in
-tomli==2.0.1 \
-    --hash=sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc \
-    --hash=sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f
-    # via black
-typing-extensions==4.12.2 \
-    --hash=sha256:04e5ca0351e0f3f85c6853954072df659d0d13fac324d0072316b67d7794700d \
-    --hash=sha256:1a7ead55c7e559dd4dee8856e3a88b41225abfe1ce8df57b7c13915fe121ffb8
-    # via black
diff --git a/src/tools/tidy/config/ruff.toml b/src/tools/tidy/config/ruff.toml
index de23d93593c..4a5aa618be1 100644
--- a/src/tools/tidy/config/ruff.toml
+++ b/src/tools/tidy/config/ruff.toml
@@ -19,6 +19,9 @@ extend-exclude = [
     "src/tools/enzyme/",
     "src/tools/rustc-perf/",
     "src/gcc/",
+    "compiler/rustc_codegen_gcc",
+    "src/tools/clippy",
+    "src/tools/miri",
     # Hack: CI runs from a subdirectory under the main checkout
     "../src/doc/nomicon/",
     "../src/tools/cargo/",
@@ -34,6 +37,9 @@ extend-exclude = [
     "../src/tools/enzyme/",
     "../src/tools/rustc-perf/",
     "../src/gcc/",
+    "../compiler/rustc_codegen_gcc",
+    "../src/tools/clippy",
+    "../src/tools/miri",
 ]
 
 [lint]
diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs
index e065f01ebba..beeb33b46ff 100644
--- a/src/tools/tidy/src/deps.rs
+++ b/src/tools/tidy/src/deps.rs
@@ -12,7 +12,8 @@ use cargo_metadata::{Metadata, Package, PackageId};
 #[rustfmt::skip]
 const LICENSES: &[&str] = &[
     // tidy-alphabetical-start
-    "(MIT OR Apache-2.0) AND Unicode-DFS-2016",            // unicode_ident
+    "(MIT OR Apache-2.0) AND Unicode-3.0",                 // unicode_ident (1.0.14)
+    "(MIT OR Apache-2.0) AND Unicode-DFS-2016",            // unicode_ident (1.0.12)
     "0BSD OR MIT OR Apache-2.0",                           // adler license
     "0BSD",
     "Apache-2.0 / MIT",
@@ -94,7 +95,6 @@ const EXCEPTIONS: ExceptionList = &[
     ("dissimilar", "Apache-2.0"),                            // rustdoc, rustc_lexer (few tests) via expect-test, (dev deps)
     ("fluent-langneg", "Apache-2.0"),                        // rustc (fluent translations)
     ("foldhash", "Zlib"),                                    // rustc
-    ("instant", "BSD-3-Clause"),                             // rustc_driver/tracing-subscriber/parking_lot
     ("mdbook", "MPL-2.0"),                                   // mdbook
     ("option-ext", "MPL-2.0"),                               // cargo-miri (via `directories`)
     ("rustc_apfloat", "Apache-2.0 WITH LLVM-exception"),     // rustc (license is the same as LLVM uses)
@@ -307,7 +307,6 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
     "intl_pluralrules",
     "itertools",
     "itoa",
-    "jemalloc-sys",
     "jobserver",
     "lazy_static",
     "leb128",
@@ -392,6 +391,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
     "thiserror-impl",
     "thorin-dwp",
     "thread_local",
+    "tikv-jemalloc-sys",
     "time",
     "time-core",
     "time-macros",
@@ -535,6 +535,8 @@ const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[
     "regalloc2",
     "region",
     "rustc-hash",
+    "serde",
+    "serde_derive",
     "slice-group-by",
     "smallvec",
     "stable_deref_trait",
diff --git a/src/tools/tidy/src/ext_tool_checks.rs b/src/tools/tidy/src/ext_tool_checks.rs
index 8f21338c7db..9792650d37d 100644
--- a/src/tools/tidy/src/ext_tool_checks.rs
+++ b/src/tools/tidy/src/ext_tool_checks.rs
@@ -32,9 +32,8 @@ const REL_PY_PATH: &[&str] = &["Scripts", "python3.exe"];
 const REL_PY_PATH: &[&str] = &["bin", "python3"];
 
 const RUFF_CONFIG_PATH: &[&str] = &["src", "tools", "tidy", "config", "ruff.toml"];
-const BLACK_CONFIG_PATH: &[&str] = &["src", "tools", "tidy", "config", "black.toml"];
 /// Location within build directory
-const RUFF_CACH_PATH: &[&str] = &["cache", "ruff_cache"];
+const RUFF_CACHE_PATH: &[&str] = &["cache", "ruff_cache"];
 const PIP_REQ_PATH: &[&str] = &["src", "tools", "tidy", "config", "requirements.txt"];
 
 pub fn check(
@@ -96,7 +95,7 @@ fn check_impl(
         let mut cfg_path = root_path.to_owned();
         cfg_path.extend(RUFF_CONFIG_PATH);
         let mut cache_dir = outdir.to_owned();
-        cache_dir.extend(RUFF_CACH_PATH);
+        cache_dir.extend(RUFF_CACHE_PATH);
 
         cfg_args_ruff.extend([
             "--config".as_ref(),
@@ -124,33 +123,36 @@ fn check_impl(
     }
 
     if python_fmt {
-        let mut cfg_args_black = cfg_args.clone();
-        let mut file_args_black = file_args.clone();
+        let mut cfg_args_ruff = cfg_args.clone();
+        let mut file_args_ruff = file_args.clone();
 
         if bless {
             eprintln!("formatting python files");
         } else {
             eprintln!("checking python file formatting");
-            cfg_args_black.push("--check".as_ref());
+            cfg_args_ruff.push("--check".as_ref());
         }
 
         let mut cfg_path = root_path.to_owned();
-        cfg_path.extend(BLACK_CONFIG_PATH);
+        cfg_path.extend(RUFF_CONFIG_PATH);
+        let mut cache_dir = outdir.to_owned();
+        cache_dir.extend(RUFF_CACHE_PATH);
 
-        cfg_args_black.extend(["--config".as_ref(), cfg_path.as_os_str()]);
+        cfg_args_ruff.extend(["--config".as_ref(), cfg_path.as_os_str()]);
 
-        if file_args_black.is_empty() {
-            file_args_black.push(root_path.as_os_str());
+        if file_args_ruff.is_empty() {
+            file_args_ruff.push(root_path.as_os_str());
         }
 
-        let mut args = merge_args(&cfg_args_black, &file_args_black);
-        let res = py_runner(py_path.as_ref().unwrap(), true, None, "black", &args);
+        let mut args = merge_args(&cfg_args_ruff, &file_args_ruff);
+        args.insert(0, "format".as_ref());
+        let res = py_runner(py_path.as_ref().unwrap(), true, None, "ruff", &args);
 
         if res.is_err() && show_diff {
             eprintln!("\npython formatting does not match! Printing diff:");
 
             args.insert(0, "--diff".as_ref());
-            let _ = py_runner(py_path.as_ref().unwrap(), true, None, "black", &args);
+            let _ = py_runner(py_path.as_ref().unwrap(), true, None, "ruff", &args);
         }
         // Rethrow error
         let _ = res?;
@@ -445,7 +447,7 @@ fn shellcheck_runner(args: &[&OsStr]) -> Result<(), Error> {
     }
 
     let status = Command::new("shellcheck").args(args).status()?;
-    if status.success() { Ok(()) } else { Err(Error::FailedCheck("black")) }
+    if status.success() { Ok(()) } else { Err(Error::FailedCheck("shellcheck")) }
 }
 
 /// Check git for tracked files matching an extension
diff --git a/src/tools/tidy/src/fluent_period.rs b/src/tools/tidy/src/fluent_period.rs
index 8bc404dc858..6a136e5aec6 100644
--- a/src/tools/tidy/src/fluent_period.rs
+++ b/src/tools/tidy/src/fluent_period.rs
@@ -18,7 +18,6 @@ const ALLOWLIST: &[&str] = &[
     "const_eval_validation_failure_note",
     "driver_impl_ice",
     "incremental_corrupt_file",
-    "mir_build_pointer_pattern",
 ];
 
 fn check_period(filename: &str, contents: &str, bad: &mut bool) {
diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt
index ac82a17e145..25cd32063aa 100644
--- a/src/tools/tidy/src/issues.txt
+++ b/src/tools/tidy/src/issues.txt
@@ -510,7 +510,6 @@ ui/confuse-field-and-method/issue-2392.rs
 ui/confuse-field-and-method/issue-32128.rs
 ui/confuse-field-and-method/issue-33784.rs
 ui/const-generics/generic_arg_infer/issue-91614.rs
-ui/const-generics/generic_const_exprs/auxiliary/issue-94287-aux.rs
 ui/const-generics/generic_const_exprs/const_kind_expr/issue_114151.rs
 ui/const-generics/generic_const_exprs/issue-100217.rs
 ui/const-generics/generic_const_exprs/issue-100360.rs
@@ -540,7 +539,6 @@ ui/const-generics/generic_const_exprs/issue-85848.rs
 ui/const-generics/generic_const_exprs/issue-86710.rs
 ui/const-generics/generic_const_exprs/issue-89851.rs
 ui/const-generics/generic_const_exprs/issue-90847.rs
-ui/const-generics/generic_const_exprs/issue-94287.rs
 ui/const-generics/generic_const_exprs/issue-94293.rs
 ui/const-generics/generic_const_exprs/issue-96699.rs
 ui/const-generics/generic_const_exprs/issue-97047-ice-1.rs
@@ -2297,8 +2295,6 @@ ui/issues/issue-43853.rs
 ui/issues/issue-4387.rs
 ui/issues/issue-43910.rs
 ui/issues/issue-43923.rs
-ui/issues/issue-43925.rs
-ui/issues/issue-43926.rs
 ui/issues/issue-43988.rs
 ui/issues/issue-44023.rs
 ui/issues/issue-44056.rs
@@ -2547,8 +2543,6 @@ ui/issues/issue-6936.rs
 ui/issues/issue-69455.rs
 ui/issues/issue-69602-type-err-during-codegen-ice.rs
 ui/issues/issue-69683.rs
-ui/issues/issue-70093/issue-70093-link-directives.rs
-ui/issues/issue-70093/issue-70093.rs
 ui/issues/issue-7012.rs
 ui/issues/issue-70381.rs
 ui/issues/issue-7044.rs
@@ -2713,11 +2707,15 @@ ui/limits/issue-17913.rs
 ui/limits/issue-55878.rs
 ui/limits/issue-69485-var-size-diffs-too-large.rs
 ui/limits/issue-75158-64.rs
+ui/link-native-libs/issue-109144.rs
+ui/link-native-libs/issue-43925.rs
+ui/link-native-libs/issue-43926.rs
+ui/link-native-libs/issue-70093/issue-70093-link-directives.rs
+ui/link-native-libs/issue-70093/issue-70093.rs
 ui/linkage-attr/auxiliary/issue-12133-dylib.rs
 ui/linkage-attr/auxiliary/issue-12133-dylib2.rs
 ui/linkage-attr/auxiliary/issue-12133-rlib.rs
 ui/linkage-attr/issue-10755.rs
-ui/linkage-attr/issue-109144.rs
 ui/linkage-attr/issue-12133-1.rs
 ui/linkage-attr/issue-12133-2.rs
 ui/linkage-attr/issue-12133-3.rs
@@ -3481,8 +3479,6 @@ ui/pattern/usefulness/issue-80501-or-pat-and-macro.rs
 ui/pattern/usefulness/issue-82772-match-box-as-struct.rs
 ui/pattern/usefulness/issue-85222-types-containing-non-exhaustive-types.rs
 ui/pattern/usefulness/issue-88747.rs
-ui/polymorphization/issue-74614.rs
-ui/polymorphization/issue-74636.rs
 ui/privacy/auxiliary/issue-117997.rs
 ui/privacy/auxiliary/issue-119463-extern.rs
 ui/privacy/auxiliary/issue-17718-const-privacy.rs
diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs
index 11f9d5bb03d..401169c838f 100644
--- a/src/tools/tidy/src/ui_tests.rs
+++ b/src/tools/tidy/src/ui_tests.rs
@@ -17,7 +17,7 @@ use ignore::Walk;
 const ENTRY_LIMIT: u32 = 901;
 // FIXME: The following limits should be reduced eventually.
 
-const ISSUES_ENTRY_LIMIT: u32 = 1672;
+const ISSUES_ENTRY_LIMIT: u32 = 1667;
 
 const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[
     "rs",     // test source files
diff --git a/src/tools/unicode-table-generator/src/range_search.rs b/src/tools/unicode-table-generator/src/range_search.rs
index 14da876eda7..9a51979a2f0 100644
--- a/src/tools/unicode-table-generator/src/range_search.rs
+++ b/src/tools/unicode-table-generator/src/range_search.rs
@@ -1,5 +1,4 @@
 #[inline(always)]
-#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_unicode_case_lookup", since = "1.84.0"))]
 const fn bitset_search<
     const N: usize,
     const CHUNK_SIZE: usize,
diff --git a/src/tools/unicode-table-generator/src/raw_emitter.rs b/src/tools/unicode-table-generator/src/raw_emitter.rs
index dd064c59283..46010692fe5 100644
--- a/src/tools/unicode-table-generator/src/raw_emitter.rs
+++ b/src/tools/unicode-table-generator/src/raw_emitter.rs
@@ -97,10 +97,6 @@ impl RawEmitter {
 
         self.blank_line();
 
-        writeln!(
-            &mut self.file,
-            r#"#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_unicode_case_lookup", since = "1.84.0"))]"#
-        ).unwrap();
         writeln!(&mut self.file, "pub const fn lookup(c: char) -> bool {{").unwrap();
         if first_code_point > 0x7f {
             writeln!(&mut self.file, "    (c as u32) >= {first_code_point:#04x} &&").unwrap();
diff --git a/src/tools/wasm-component-ld/Cargo.toml b/src/tools/wasm-component-ld/Cargo.toml
index acdb1aa1ab7..965e9b01a44 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.10"
+wasm-component-ld = "0.5.11"