about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/bootstrap/Cargo.lock8
-rw-r--r--src/bootstrap/Cargo.toml2
-rwxr-xr-xsrc/bootstrap/configure.py26
-rw-r--r--src/bootstrap/defaults/bootstrap.library.toml4
-rw-r--r--src/bootstrap/src/bin/main.rs16
-rw-r--r--src/bootstrap/src/core/build_steps/check.rs416
-rw-r--r--src/bootstrap/src/core/build_steps/clippy.rs4
-rw-r--r--src/bootstrap/src/core/build_steps/compile.rs97
-rw-r--r--src/bootstrap/src/core/build_steps/dist.rs2
-rw-r--r--src/bootstrap/src/core/build_steps/doc.rs10
-rw-r--r--src/bootstrap/src/core/build_steps/llvm.rs9
-rw-r--r--src/bootstrap/src/core/build_steps/mod.rs1
-rw-r--r--src/bootstrap/src/core/build_steps/setup.rs4
-rw-r--r--src/bootstrap/src/core/build_steps/suggest.rs68
-rw-r--r--src/bootstrap/src/core/build_steps/test.rs42
-rw-r--r--src/bootstrap/src/core/build_steps/tool.rs86
-rw-r--r--src/bootstrap/src/core/builder/cargo.rs23
-rw-r--r--src/bootstrap/src/core/builder/mod.rs20
-rw-r--r--src/bootstrap/src/core/builder/tests.rs354
-rw-r--r--src/bootstrap/src/core/config/config.rs33
-rw-r--r--src/bootstrap/src/core/config/flags.rs16
-rw-r--r--src/bootstrap/src/core/config/toml/llvm.rs16
-rw-r--r--src/bootstrap/src/core/config/toml/rust.rs25
-rw-r--r--src/bootstrap/src/core/sanity.rs1
-rw-r--r--src/bootstrap/src/lib.rs22
-rw-r--r--src/bootstrap/src/utils/cc_detect.rs14
-rw-r--r--src/bootstrap/src/utils/change_tracker.rs35
-rw-r--r--src/bootstrap/src/utils/exec.rs276
-rw-r--r--src/bootstrap/src/utils/helpers.rs25
-rw-r--r--src/bootstrap/src/utils/metrics.rs2
-rw-r--r--src/bootstrap/src/utils/render_tests.rs2
-rw-r--r--src/bootstrap/src/utils/tests/mod.rs30
-rw-r--r--src/bootstrap/src/utils/tracing.rs4
-rw-r--r--src/build_helper/src/ci.rs2
-rw-r--r--src/build_helper/src/git.rs32
-rw-r--r--src/build_helper/src/lib.rs1
-rw-r--r--src/build_helper/src/metrics.rs2
-rw-r--r--src/build_helper/src/npm.rs30
-rw-r--r--src/build_helper/src/util.rs15
-rw-r--r--src/ci/docker/host-aarch64/aarch64-gnu-llvm-19/Dockerfile8
-rw-r--r--src/ci/docker/host-x86_64/dist-powerpc64le-linux-gnu/Dockerfile14
-rw-r--r--src/ci/docker/host-x86_64/dist-powerpc64le-linux-gnu/powerpc64le-unknown-linux-gnu.defconfig3
-rw-r--r--src/ci/docker/host-x86_64/dist-powerpc64le-linux-musl/Dockerfile6
-rw-r--r--src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile4
-rw-r--r--src/ci/docker/host-x86_64/i686-gnu-nopt/Dockerfile4
-rw-r--r--src/ci/docker/host-x86_64/i686-gnu/Dockerfile3
-rw-r--r--src/ci/docker/host-x86_64/pr-check-1/Dockerfile6
-rw-r--r--src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile14
-rw-r--r--src/ci/docker/host-x86_64/x86_64-gnu-llvm-20/Dockerfile14
-rw-r--r--src/ci/docker/host-x86_64/x86_64-gnu-miri/Dockerfile8
-rw-r--r--src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile11
-rw-r--r--src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version1
-rwxr-xr-xsrc/ci/docker/run.sh14
-rwxr-xr-xsrc/ci/docker/scripts/build-powerpc64le-toolchain.sh74
-rwxr-xr-xsrc/ci/docker/scripts/i686-gnu-nopt-2.sh6
-rwxr-xr-xsrc/ci/docker/scripts/x86_64-gnu-llvm2.sh2
-rw-r--r--src/ci/github-actions/jobs.yml27
-rwxr-xr-xsrc/ci/scripts/install-clang.sh16
-rwxr-xr-xsrc/ci/scripts/install-rust.sh15
m---------src/doc/book0
m---------src/doc/embedded-book0
m---------src/doc/nomicon0
m---------src/doc/reference0
m---------src/doc/rust-by-example0
-rw-r--r--src/doc/rustc-dev-guide/src/SUMMARY.md1
-rw-r--r--src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md11
-rw-r--r--src/doc/rustc-dev-guide/src/building/quickstart.md3
-rw-r--r--src/doc/rustc-dev-guide/src/building/suggested.md17
-rw-r--r--src/doc/rustc-dev-guide/src/effects.md9
-rw-r--r--src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-test-suite.md3
-rw-r--r--src/doc/rustc-dev-guide/src/tests/suggest-tests.md59
-rw-r--r--src/doc/rustc/src/SUMMARY.md2
-rw-r--r--src/doc/rustc/src/codegen-options/index.md59
-rw-r--r--src/doc/rustc/src/platform-support.md4
-rw-r--r--src/doc/rustc/src/platform-support/aarch64-unknown-linux-musl.md49
-rw-r--r--src/doc/rustc/src/platform-support/mips64-unknown-linux-muslabi64.md49
-rw-r--r--src/doc/rustc/src/platform-support/netbsd.md31
-rw-r--r--src/doc/rustc/src/platform-support/wasm32-wasip1.md49
-rw-r--r--src/doc/rustdoc.md2
-rw-r--r--src/doc/unstable-book/src/compiler-flags/codegen-options.md8
-rw-r--r--src/doc/unstable-book/src/compiler-flags/linker-features.md35
-rw-r--r--src/etc/completions/x.fish62
-rw-r--r--src/etc/completions/x.ps167
-rw-r--r--src/etc/completions/x.py.fish62
-rw-r--r--src/etc/completions/x.py.ps167
-rw-r--r--src/etc/completions/x.py.sh233
-rw-r--r--src/etc/completions/x.py.zsh74
-rw-r--r--src/etc/completions/x.sh233
-rw-r--r--src/etc/completions/x.zsh74
-rwxr-xr-xsrc/etc/htmldocck.py59
-rw-r--r--src/etc/rust_analyzer_eglot.el3
-rw-r--r--src/etc/rust_analyzer_helix.toml1
-rw-r--r--src/etc/rust_analyzer_settings.json3
-rw-r--r--src/etc/rust_analyzer_zed.json2
-rw-r--r--src/librustdoc/clean/inline.rs12
-rw-r--r--src/librustdoc/clean/mod.rs59
-rw-r--r--src/librustdoc/clean/types.rs79
-rw-r--r--src/librustdoc/clean/utils.rs11
-rw-r--r--src/librustdoc/formats/cache.rs4
-rw-r--r--src/librustdoc/html/format.rs23
-rw-r--r--src/librustdoc/html/render/context.rs4
-rw-r--r--src/librustdoc/html/render/mod.rs19
-rw-r--r--src/librustdoc/html/render/print_item.rs12
-rw-r--r--src/librustdoc/html/render/search_index.rs14
-rw-r--r--src/librustdoc/html/render/write_shared.rs4
-rw-r--r--src/librustdoc/html/url_parts_builder.rs12
-rw-r--r--src/librustdoc/json/conversions.rs116
-rw-r--r--src/librustdoc/visit_ast.rs68
m---------src/llvm-project0
-rw-r--r--src/rustdoc-json-types/lib.rs89
m---------src/tools/cargo0
-rw-r--r--src/tools/clippy/.github/workflows/feature_freeze.yml14
-rw-r--r--src/tools/clippy/.github/workflows/lintcheck.yml28
-rw-r--r--src/tools/clippy/.github/workflows/lintcheck_summary.yml106
-rw-r--r--src/tools/clippy/Cargo.toml12
-rw-r--r--src/tools/clippy/book/src/development/infrastructure/backport.md2
-rw-r--r--src/tools/clippy/book/src/development/infrastructure/changelog_update.md32
-rw-r--r--src/tools/clippy/book/src/lint_configuration.md1
-rw-r--r--src/tools/clippy/clippy_config/src/conf.rs1
-rw-r--r--src/tools/clippy/clippy_dev/src/fmt.rs2
-rw-r--r--src/tools/clippy/clippy_dev/src/lib.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs87
-rw-r--r--src/tools/clippy/clippy_lints/src/booleans.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_possible_truncation.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/mod.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/utils.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/coerce_container_to_any.rs44
-rw-r--r--src/tools/clippy/clippy_lints/src/cognitive_complexity.rs21
-rw-r--r--src/tools/clippy/clippy_lints/src/derivable_impls.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/doc/missing_headers.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/doc/mod.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/empty_drop.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/enum_clike.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/escape.rs22
-rw-r--r--src/tools/clippy/clippy_lints/src/exhaustive_items.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/exit.rs35
-rw-r--r--src/tools/clippy/clippy_lints/src/fallible_impl_from.rs64
-rw-r--r--src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/format_args.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/from_over_into.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs15
-rw-r--r--src/tools/clippy/clippy_lints/src/if_not_else.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/implicit_hasher.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/infallible_try_from.rs39
-rw-r--r--src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/len_zero.rs30
-rw-r--r--src/tools/clippy/clippy_lints/src/let_if_seq.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/lifetimes.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/empty_loop.rs15
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_let_else.rs39
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/manual_is_variant_and.rs168
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/return_and_then.rs29
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/swap_with_temporary.rs52
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_inline.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_trait_methods.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_bool.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/neg_multiply.rs40
-rw-r--r--src/tools/clippy/clippy_lints/src/new_without_default.rs19
-rw-r--r--src/tools/clippy/clippy_lints/src/no_effect.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/op_ref.rs30
-rw-r--r--src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_closure_call.rs22
-rw-r--r--src/tools/clippy/clippy_lints/src/same_name_method.rs32
-rw-r--r--src/tools/clippy/clippy_lints/src/serde_api.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/std_instead_of_core.rs109
-rw-r--r--src/tools/clippy/clippy_lints/src/trait_bounds.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs24
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_async.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs4
-rw-r--r--src/tools/clippy/clippy_lints_internal/src/derive_deserialize_allowing_unknown.rs2
-rw-r--r--src/tools/clippy/clippy_lints_internal/src/symbols.rs2
-rw-r--r--src/tools/clippy/clippy_test_deps/Cargo.lock505
-rw-r--r--src/tools/clippy/clippy_test_deps/Cargo.toml20
-rw-r--r--src/tools/clippy/clippy_test_deps/src/main.rs1
-rw-r--r--src/tools/clippy/clippy_utils/README.md2
-rw-r--r--src/tools/clippy/clippy_utils/src/ast_utils/mod.rs5
-rw-r--r--src/tools/clippy/clippy_utils/src/attrs.rs10
-rw-r--r--src/tools/clippy/clippy_utils/src/check_proc_macro.rs4
-rw-r--r--src/tools/clippy/clippy_utils/src/consts.rs14
-rw-r--r--src/tools/clippy/clippy_utils/src/higher.rs49
-rw-r--r--src/tools/clippy/clippy_utils/src/hir_utils.rs86
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs91
-rw-r--r--src/tools/clippy/clippy_utils/src/msrvs.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/paths.rs28
-rw-r--r--src/tools/clippy/clippy_utils/src/source.rs7
-rw-r--r--src/tools/clippy/clippy_utils/src/ty/mod.rs4
-rw-r--r--src/tools/clippy/clippy_utils/src/visitors.rs16
-rw-r--r--src/tools/clippy/lintcheck/src/config.rs3
-rw-r--r--src/tools/clippy/lintcheck/src/json.rs105
-rw-r--r--src/tools/clippy/lintcheck/src/main.rs7
-rw-r--r--src/tools/clippy/rust-toolchain.toml2
-rw-r--r--src/tools/clippy/tests/compile-test.rs87
-rw-r--r--src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default.stderr8
-rw-r--r--src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default_exp.stderr8
-rw-r--r--src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.ord_within.stderr8
-rw-r--r--src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.var_1.stderr14
-rw-r--r--src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.only_impl.stderr8
-rw-r--r--src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.only_trait.stderr4
-rw-r--r--src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr10
-rw-r--r--src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs11
-rw-r--r--src/tools/clippy/tests/ui/assign_ops.fixed3
-rw-r--r--src/tools/clippy/tests/ui/assign_ops.rs3
-rw-r--r--src/tools/clippy/tests/ui/author/if.stdout3
-rw-r--r--src/tools/clippy/tests/ui/author/struct.stdout3
-rw-r--r--src/tools/clippy/tests/ui/borrow_as_ptr.fixed6
-rw-r--r--src/tools/clippy/tests/ui/borrow_as_ptr.rs6
-rw-r--r--src/tools/clippy/tests/ui/cast_size.32bit.stderr10
-rw-r--r--src/tools/clippy/tests/ui/cast_size.64bit.stderr10
-rw-r--r--src/tools/clippy/tests/ui/cast_size.rs6
-rw-r--r--src/tools/clippy/tests/ui/coerce_container_to_any.fixed13
-rw-r--r--src/tools/clippy/tests/ui/coerce_container_to_any.rs13
-rw-r--r--src/tools/clippy/tests/ui/coerce_container_to_any.stderr14
-rw-r--r--src/tools/clippy/tests/ui/disallowed_script_idents.rs14
-rw-r--r--src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.fixed6
-rw-r--r--src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.rs2
-rw-r--r--src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.stderr26
-rw-r--r--src/tools/clippy/tests/ui/empty_loop_intrinsic.rs13
-rw-r--r--src/tools/clippy/tests/ui/exit1_compile_flag_test.rs17
-rw-r--r--src/tools/clippy/tests/ui/exit1_compile_flag_test.stderr11
-rw-r--r--src/tools/clippy/tests/ui/exit2_compile_flag_test.rs15
-rw-r--r--src/tools/clippy/tests/ui/exit2_compile_flag_test.stderr11
-rw-r--r--src/tools/clippy/tests/ui/exit3_compile_flag_test.rs11
-rw-r--r--src/tools/clippy/tests/ui/exit4.rs8
-rw-r--r--src/tools/clippy/tests/ui/floating_point_mul_add.fixed44
-rw-r--r--src/tools/clippy/tests/ui/floating_point_mul_add.rs44
-rw-r--r--src/tools/clippy/tests/ui/floating_point_mul_add.stderr32
-rw-r--r--src/tools/clippy/tests/ui/manual_is_variant_and.fixed112
-rw-r--r--src/tools/clippy/tests/ui/manual_is_variant_and.rs110
-rw-r--r--src/tools/clippy/tests/ui/manual_is_variant_and.stderr100
-rw-r--r--src/tools/clippy/tests/ui/manual_let_else_match.fixed45
-rw-r--r--src/tools/clippy/tests/ui/manual_let_else_match.rs73
-rw-r--r--src/tools/clippy/tests/ui/manual_let_else_match.stderr72
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.fixed3
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.rs3
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.stderr2
-rw-r--r--src/tools/clippy/tests/ui/missing_panics_doc.rs28
-rw-r--r--src/tools/clippy/tests/ui/missing_panics_doc.stderr38
-rw-r--r--src/tools/clippy/tests/ui/needless_bool_assign.fixed9
-rw-r--r--src/tools/clippy/tests/ui/needless_bool_assign.rs13
-rw-r--r--src/tools/clippy/tests/ui/needless_bool_assign.stderr13
-rw-r--r--src/tools/clippy/tests/ui/neg_multiply.fixed12
-rw-r--r--src/tools/clippy/tests/ui/neg_multiply.rs12
-rw-r--r--src/tools/clippy/tests/ui/neg_multiply.stderr8
-rw-r--r--src/tools/clippy/tests/ui/op_ref.fixed34
-rw-r--r--src/tools/clippy/tests/ui/op_ref.rs34
-rw-r--r--src/tools/clippy/tests/ui/op_ref.stderr22
-rw-r--r--src/tools/clippy/tests/ui/or_fun_call.fixed20
-rw-r--r--src/tools/clippy/tests/ui/or_fun_call.rs20
-rw-r--r--src/tools/clippy/tests/ui/or_fun_call.stderr14
-rw-r--r--src/tools/clippy/tests/ui/partialeq_ne_impl.stderr8
-rw-r--r--src/tools/clippy/tests/ui/ptr_arg.rs2
-rw-r--r--src/tools/clippy/tests/ui/ptr_arg.stderr11
-rw-r--r--src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed14
-rw-r--r--src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs12
-rw-r--r--src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr28
-rw-r--r--src/tools/clippy/tests/ui/return_and_then.fixed131
-rw-r--r--src/tools/clippy/tests/ui/return_and_then.rs114
-rw-r--r--src/tools/clippy/tests/ui/return_and_then.stderr96
-rw-r--r--src/tools/clippy/tests/ui/same_name_method.stderr16
-rw-r--r--src/tools/clippy/tests/ui/serde.stderr5
-rw-r--r--src/tools/clippy/tests/ui/std_instead_of_core.fixed7
-rw-r--r--src/tools/clippy/tests/ui/std_instead_of_core.rs7
-rw-r--r--src/tools/clippy/tests/ui/std_instead_of_core.stderr30
-rw-r--r--src/tools/clippy/tests/ui/std_instead_of_core_unfixable.rs18
-rw-r--r--src/tools/clippy/tests/ui/std_instead_of_core_unfixable.stderr30
-rw-r--r--src/tools/clippy/tests/ui/swap_with_temporary.fixed46
-rw-r--r--src/tools/clippy/tests/ui/swap_with_temporary.rs46
-rw-r--r--src/tools/clippy/tests/ui/swap_with_temporary.stderr38
-rw-r--r--src/tools/clippy/tests/ui/track-diagnostics-clippy.rs3
-rw-r--r--src/tools/clippy/tests/ui/track-diagnostics-clippy.stderr4
-rw-r--r--src/tools/clippy/tests/ui/trait_duplication_in_bounds.fixed3
-rw-r--r--src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs3
-rw-r--r--src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr6
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_map_or.fixed10
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_map_or.rs10
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_map_or.stderr26
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_operation.fixed13
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_operation.rs13
-rw-r--r--src/tools/clippy/tests/ui/zero_ptr.fixed8
-rw-r--r--src/tools/clippy/tests/ui/zero_ptr.rs8
-rw-r--r--src/tools/clippy/tests/ui/zero_ptr.stderr8
-rw-r--r--src/tools/clippy/triagebot.toml7
-rw-r--r--src/tools/clippy/util/gh-pages/index_template.html40
-rw-r--r--src/tools/clippy/util/gh-pages/script.js6
-rw-r--r--src/tools/clippy/util/gh-pages/style.css26
-rw-r--r--src/tools/compiletest/src/common.rs211
-rw-r--r--src/tools/compiletest/src/debuggers.rs11
-rw-r--r--src/tools/compiletest/src/directive-list.rs260
-rw-r--r--src/tools/compiletest/src/directives.rs339
-rw-r--r--src/tools/compiletest/src/directives/tests.rs4
-rw-r--r--src/tools/compiletest/src/lib.rs22
-rw-r--r--src/tools/compiletest/src/runtest.rs124
-rw-r--r--src/tools/compiletest/src/runtest/coverage.rs4
-rw-r--r--src/tools/compiletest/src/runtest/run_make.rs13
-rw-r--r--src/tools/compiletest/src/runtest/rustdoc.rs2
-rw-r--r--src/tools/compiletest/src/runtest/rustdoc_json.rs2
-rw-r--r--src/tools/compiletest/src/tests.rs6
-rw-r--r--src/tools/compiletest/src/util.rs39
-rw-r--r--src/tools/jsondocck/src/directive.rs15
-rw-r--r--src/tools/jsondocck/src/main.rs16
-rw-r--r--src/tools/lint-docs/Cargo.toml2
-rw-r--r--src/tools/lint-docs/src/groups.rs4
-rw-r--r--src/tools/miri/README.md6
-rw-r--r--src/tools/miri/miri-script/Cargo.toml1
-rw-r--r--src/tools/miri/miri-script/src/commands.rs2
-rw-r--r--src/tools/miri/rust-version2
-rw-r--r--src/tools/miri/src/alloc/isolated_alloc.rs21
-rw-r--r--src/tools/miri/src/bin/log/tracing_chrome.rs72
-rw-r--r--src/tools/miri/src/bin/miri.rs30
-rw-r--r--src/tools/miri/src/borrow_tracker/mod.rs18
-rw-r--r--src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs4
-rw-r--r--src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs2
-rw-r--r--src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs6
-rw-r--r--src/tools/miri/src/concurrency/data_race.rs4
-rw-r--r--src/tools/miri/src/concurrency/mod.rs1
-rw-r--r--src/tools/miri/src/concurrency/thread.rs14
-rw-r--r--src/tools/miri/src/concurrency/weak_memory.rs2
-rw-r--r--src/tools/miri/src/data_structures/dedup_range_map.rs (renamed from src/tools/miri/src/range_map.rs)22
-rw-r--r--src/tools/miri/src/data_structures/mod.rs3
-rw-r--r--src/tools/miri/src/data_structures/mono_hash_map.rs (renamed from src/tools/miri/src/mono_hash_map.rs)0
-rw-r--r--src/tools/miri/src/data_structures/range_object_map.rs (renamed from src/tools/miri/src/concurrency/range_object_map.rs)0
-rw-r--r--src/tools/miri/src/eval.rs88
-rw-r--r--src/tools/miri/src/helpers.rs56
-rw-r--r--src/tools/miri/src/lib.rs23
-rw-r--r--src/tools/miri/src/machine.rs21
-rw-r--r--src/tools/miri/src/shims/global_ctor.rs98
-rw-r--r--src/tools/miri/src/shims/mod.rs6
-rw-r--r--src/tools/miri/src/shims/native_lib/mod.rs208
-rw-r--r--src/tools/miri/src/shims/native_lib/trace/child.rs104
-rw-r--r--src/tools/miri/src/shims/native_lib/trace/messages.rs47
-rw-r--r--src/tools/miri/src/shims/native_lib/trace/mod.rs2
-rw-r--r--src/tools/miri/src/shims/native_lib/trace/parent.rs319
-rw-r--r--src/tools/miri/src/shims/native_lib/trace/stub.rs34
-rw-r--r--src/tools/miri/src/shims/panic.rs162
-rw-r--r--src/tools/miri/src/shims/tls.rs8
-rw-r--r--src/tools/miri/src/shims/unix/fs.rs26
-rw-r--r--src/tools/miri/src/shims/unwind.rs160
-rw-r--r--src/tools/miri/test_dependencies/Cargo.toml1
-rw-r--r--src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_read_neg_offset.rs6
-rw-r--r--src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_read_neg_offset.stderr21
-rw-r--r--src/tools/miri/tests/fail/intrinsics/ptr_offset_out_of_bounds_neg2.rs6
-rw-r--r--src/tools/miri/tests/fail/intrinsics/ptr_offset_out_of_bounds_neg2.stderr20
-rw-r--r--src/tools/miri/tests/native-lib/fail/tracing/partial_init.rs25
-rw-r--r--src/tools/miri/tests/native-lib/fail/tracing/partial_init.stderr39
-rw-r--r--src/tools/miri/tests/native-lib/fail/tracing/unexposed_reachable_alloc.rs29
-rw-r--r--src/tools/miri/tests/native-lib/fail/tracing/unexposed_reachable_alloc.stderr39
-rw-r--r--src/tools/miri/tests/native-lib/pass/ptr_read_access.notrace.stderr (renamed from src/tools/miri/tests/native-lib/pass/ptr_read_access.stderr)0
-rw-r--r--src/tools/miri/tests/native-lib/pass/ptr_read_access.notrace.stdout (renamed from src/tools/miri/tests/native-lib/pass/ptr_read_access.stdout)0
-rw-r--r--src/tools/miri/tests/native-lib/pass/ptr_read_access.rs4
-rw-r--r--src/tools/miri/tests/native-lib/pass/ptr_read_access.trace.stderr19
-rw-r--r--src/tools/miri/tests/native-lib/pass/ptr_read_access.trace.stdout1
-rw-r--r--src/tools/miri/tests/native-lib/pass/ptr_write_access.notrace.stderr (renamed from src/tools/miri/tests/native-lib/pass/ptr_write_access.stderr)0
-rw-r--r--src/tools/miri/tests/native-lib/pass/ptr_write_access.rs3
-rw-r--r--src/tools/miri/tests/native-lib/pass/ptr_write_access.trace.stderr19
-rw-r--r--src/tools/miri/tests/native-lib/ptr_read_access.c6
-rw-r--r--src/tools/miri/tests/native-lib/ptr_write_access.c8
-rw-r--r--src/tools/miri/tests/pass/0weak_memory_consistency.rs22
-rw-r--r--src/tools/miri/tests/pass/0weak_memory_consistency_sc.rs16
-rw-r--r--src/tools/miri/tests/pass/alloc-access-tracking.rs4
-rw-r--r--src/tools/miri/tests/pass/shims/ctor.rs46
-rw-r--r--src/tools/miri/tests/pass/shims/random.rs2
-rw-r--r--src/tools/miri/tests/pass/weak_memory/weak.rs8
-rw-r--r--src/tools/miri/tests/ui.rs2
-rw-r--r--src/tools/opt-dist/Cargo.toml2
-rw-r--r--src/tools/opt-dist/src/tests.rs3
-rw-r--r--src/tools/remote-test-client/src/main.rs4
-rw-r--r--src/tools/run-make-support/CHANGELOG.md83
-rw-r--r--src/tools/run-make-support/Cargo.toml21
-rw-r--r--src/tools/run-make-support/src/artifact_names.rs6
-rw-r--r--src/tools/run-make-support/src/env.rs15
-rw-r--r--src/tools/run-make-support/src/external_deps/c_build.rs23
-rw-r--r--src/tools/run-make-support/src/external_deps/c_cxx_compiler/cc.rs6
-rw-r--r--src/tools/run-make-support/src/external_deps/c_cxx_compiler/extras.rs6
-rw-r--r--src/tools/run-make-support/src/external_deps/rustc.rs4
-rw-r--r--src/tools/run-make-support/src/lib.rs108
-rw-r--r--src/tools/run-make-support/src/linker.rs4
-rw-r--r--src/tools/run-make-support/src/symbols.rs184
-rw-r--r--src/tools/run-make-support/src/targets.rs12
-rw-r--r--src/tools/rust-analyzer/Cargo.lock52
-rw-r--r--src/tools/rust-analyzer/Cargo.toml6
-rw-r--r--src/tools/rust-analyzer/crates/base-db/src/input.rs45
-rw-r--r--src/tools/rust-analyzer/crates/base-db/src/lib.rs11
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs58
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expr_store/path.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs80
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs140
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs49
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/db.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/files.rs20
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/lib.rs28
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/name.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs14
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/db.rs40
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs84
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/display.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer.rs48
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs7
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs35
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/layout.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs5
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs21
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs9
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs28
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs40
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs28
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs16
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/variance.rs18
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/diagnostics.rs12
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/lib.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs25
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs22
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs21
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs26
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_match_arm.rs63
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs86
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/utils.rs15
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs12
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/search.rs8
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs18
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs9
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs13
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/doc_links.rs27
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/file_structure.rs50
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/goto_definition.rs76
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover/tests.rs31
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs108
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/navigation_target.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/runnables.rs20
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs2
-rw-r--r--src/tools/rust-analyzer/crates/load-cargo/src/lib.rs95
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/lexed_str.rs10
-rw-r--r--src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs2
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs43
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/sysroot.rs29
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/tests.rs2
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/workspace.rs16
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs8
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs2
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs6
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs7
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs2
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs5
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/capabilities.rs6
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs11
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs37
-rw-r--r--src/tools/rust-analyzer/crates/span/src/ast_id.rs15
-rw-r--r--src/tools/rust-analyzer/crates/span/src/hygiene.rs16
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/make.rs7
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs41
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs2
-rw-r--r--src/tools/rust-analyzer/crates/test-utils/src/fixture.rs22
-rw-r--r--src/tools/rust-analyzer/crates/test-utils/src/minicore.rs41
-rw-r--r--src/tools/rust-analyzer/crates/tt/src/iter.rs1
-rw-r--r--src/tools/rust-analyzer/crates/tt/src/lib.rs2
-rw-r--r--src/tools/rust-analyzer/crates/vfs/src/file_set.rs6
-rw-r--r--src/tools/rust-analyzer/docs/book/src/contributing/lsp-extensions.md18
-rw-r--r--src/tools/rustbook/Cargo.lock4
-rw-r--r--src/tools/rustbook/Cargo.toml2
-rw-r--r--src/tools/rustdoc-gui-test/src/main.rs99
-rw-r--r--src/tools/rustfmt/src/items.rs4
-rw-r--r--src/tools/suggest-tests/Cargo.toml8
-rw-r--r--src/tools/suggest-tests/src/dynamic_suggestions.rs32
-rw-r--r--src/tools/suggest-tests/src/lib.rs96
-rw-r--r--src/tools/suggest-tests/src/main.rs40
-rw-r--r--src/tools/suggest-tests/src/static_suggestions.rs40
-rw-r--r--src/tools/suggest-tests/src/tests.rs21
-rw-r--r--src/tools/test-float-parse/src/lib.rs2
-rw-r--r--src/tools/tidy/Cargo.toml2
-rw-r--r--src/tools/tidy/src/alphabetical.rs2
-rw-r--r--src/tools/tidy/src/bins.rs11
-rw-r--r--src/tools/tidy/src/deps.rs2
-rw-r--r--src/tools/tidy/src/error_codes.rs20
-rw-r--r--src/tools/tidy/src/ext_tool_checks.rs219
-rw-r--r--src/tools/tidy/src/extdeps.rs2
-rw-r--r--src/tools/tidy/src/features.rs21
-rw-r--r--src/tools/tidy/src/features/version.rs2
-rw-r--r--src/tools/tidy/src/features/version/tests.rs4
-rw-r--r--src/tools/tidy/src/filenames.rs40
-rw-r--r--src/tools/tidy/src/fluent_alphabetical.rs8
-rw-r--r--src/tools/tidy/src/fluent_period.rs4
-rw-r--r--src/tools/tidy/src/fluent_used.rs4
-rw-r--r--src/tools/tidy/src/gcc_submodule.rs7
-rw-r--r--src/tools/tidy/src/issues.txt1
-rw-r--r--src/tools/tidy/src/lib.rs35
-rw-r--r--src/tools/tidy/src/main.rs21
-rw-r--r--src/tools/tidy/src/mir_opt_tests.rs2
-rw-r--r--src/tools/tidy/src/pal.rs2
-rw-r--r--src/tools/tidy/src/rustdoc_gui_tests.rs2
-rw-r--r--src/tools/tidy/src/rustdoc_js.rs2
-rw-r--r--src/tools/tidy/src/rustdoc_json.rs22
-rw-r--r--src/tools/tidy/src/rustdoc_templates.rs4
-rw-r--r--src/tools/tidy/src/style.rs15
-rw-r--r--src/tools/tidy/src/target_specific_tests.rs11
-rw-r--r--src/tools/tidy/src/tests_revision_unpaired_stdout_stderr.rs12
-rw-r--r--src/tools/tidy/src/ui_tests.rs17
-rw-r--r--src/tools/tidy/src/unstable_book.rs4
-rw-r--r--src/tools/tidy/src/walk.rs4
-rw-r--r--src/tools/tidy/src/x_version.rs10
-rw-r--r--src/tools/tier-check/Cargo.toml2
-rw-r--r--src/tools/tier-check/src/main.rs14
-rw-r--r--src/tools/unicode-table-generator/src/range_search.rs2
-rw-r--r--src/tools/x/Cargo.toml2
-rw-r--r--src/tools/x/src/main.rs22
530 files changed, 9430 insertions, 5316 deletions
diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock
index 2434a278d55..e091c94eb53 100644
--- a/src/bootstrap/Cargo.lock
+++ b/src/bootstrap/Cargo.lock
@@ -386,9 +386,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
 
 [[package]]
 name = "libc"
-version = "0.2.172"
+version = "0.2.174"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa"
+checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
 
 [[package]]
 name = "libredox"
@@ -730,9 +730,9 @@ dependencies = [
 
 [[package]]
 name = "sysinfo"
-version = "0.35.0"
+version = "0.36.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b897c8ea620e181c7955369a31be5f48d9a9121cb59fd33ecef9ff2a34323422"
+checksum = "aab138f5c1bb35231de19049060a87977ad23e04f2303e953bc5c2947ac7dec4"
 dependencies = [
  "libc",
  "memchr",
diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml
index 073cebdcae2..8dc41d1dec6 100644
--- a/src/bootstrap/Cargo.toml
+++ b/src/bootstrap/Cargo.toml
@@ -58,7 +58,7 @@ walkdir = "2.4"
 xz2 = "0.1"
 
 # Dependencies needed by the build-metrics feature
-sysinfo = { version = "0.35.0", default-features = false, optional = true, features = ["system"] }
+sysinfo = { version = "0.36.0", default-features = false, optional = true, features = ["system"] }
 
 # Dependencies needed by the `tracing` feature
 tracing = { version = "0.1", optional = true, features = ["attributes"] }
diff --git a/src/bootstrap/configure.py b/src/bootstrap/configure.py
index c077555b906..b05a5cc8b81 100755
--- a/src/bootstrap/configure.py
+++ b/src/bootstrap/configure.py
@@ -739,19 +739,29 @@ def configure_file(sections, top_level_keys, targets, config):
 
 
 def write_uncommented(target, f):
+    """Writes each block in 'target' that is not composed entirely of comments to 'f'.
+
+    A block is a sequence of non-empty lines separated by empty lines.
+    """
     block = []
-    is_comment = True
+
+    def flush(last):
+        # If the block is entirely made of comments, ignore it
+        entire_block_comments = all(ln.startswith("#") or ln == "" for ln in block)
+        if not entire_block_comments and len(block) > 0:
+            for line in block:
+                f.write(line + "\n")
+            # Required to output a newline before the start of a new section
+            if last:
+                f.write("\n")
+        block.clear()
 
     for line in target:
         block.append(line)
         if len(line) == 0:
-            if not is_comment:
-                for ln in block:
-                    f.write(ln + "\n")
-            block = []
-            is_comment = True
-            continue
-        is_comment = is_comment and line.startswith("#")
+            flush(last=False)
+
+    flush(last=True)
     return f
 
 
diff --git a/src/bootstrap/defaults/bootstrap.library.toml b/src/bootstrap/defaults/bootstrap.library.toml
index 6a867093b78..3f811402b26 100644
--- a/src/bootstrap/defaults/bootstrap.library.toml
+++ b/src/bootstrap/defaults/bootstrap.library.toml
@@ -1,7 +1,6 @@
 # These defaults are meant for contributors to the standard library and documentation.
 [build]
 bench-stage = 1
-check-stage = 1
 test-stage = 1
 
 [rust]
@@ -13,7 +12,8 @@ lto = "off"
 #
 # If compiler-affecting directories are not modified, use precompiled rustc to speed up
 # library development by skipping compiler builds.
-download-rustc = "if-unchanged"
+# FIXME: download-rustc is currently broken: https://github.com/rust-lang/rust/issues/142505
+download-rustc = false
 
 [llvm]
 # Will download LLVM from CI if available on your platform.
diff --git a/src/bootstrap/src/bin/main.rs b/src/bootstrap/src/bin/main.rs
index e1862a451f2..181d71f63c2 100644
--- a/src/bootstrap/src/bin/main.rs
+++ b/src/bootstrap/src/bin/main.rs
@@ -8,6 +8,7 @@
 use std::fs::{self, OpenOptions};
 use std::io::{self, BufRead, BufReader, IsTerminal, Write};
 use std::str::FromStr;
+use std::time::Instant;
 use std::{env, process};
 
 use bootstrap::{
@@ -17,11 +18,17 @@ use bootstrap::{
 #[cfg(feature = "tracing")]
 use tracing::instrument;
 
+fn is_bootstrap_profiling_enabled() -> bool {
+    env::var("BOOTSTRAP_PROFILE").is_ok_and(|v| v == "1")
+}
+
 #[cfg_attr(feature = "tracing", instrument(level = "trace", name = "main"))]
 fn main() {
     #[cfg(feature = "tracing")]
     let _guard = setup_tracing();
 
+    let start_time = Instant::now();
+
     let args = env::args().skip(1).collect::<Vec<_>>();
 
     if Flags::try_parse_verbose_help(&args) {
@@ -96,7 +103,8 @@ fn main() {
     let out_dir = config.out.clone();
 
     debug!("creating new build based on config");
-    Build::new(config).build();
+    let mut build = Build::new(config);
+    build.build();
 
     if suggest_setup {
         println!("WARNING: you have not made a `bootstrap.toml`");
@@ -147,6 +155,10 @@ fn main() {
             t!(file.write_all(lines.join("\n").as_bytes()));
         }
     }
+
+    if is_bootstrap_profiling_enabled() {
+        build.report_summary(start_time);
+    }
 }
 
 fn check_version(config: &Config) -> Option<String> {
@@ -226,7 +238,7 @@ fn setup_tracing() -> impl Drop {
     let mut chrome_layer = tracing_chrome::ChromeLayerBuilder::new().include_args(true);
 
     // Writes the Chrome profile to trace-<unix-timestamp>.json if enabled
-    if !env::var("BOOTSTRAP_PROFILE").is_ok_and(|v| v == "1") {
+    if !is_bootstrap_profiling_enabled() {
         chrome_layer = chrome_layer.writer(io::sink());
     }
 
diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs
index 6c5f70b2f43..3278b55305c 100644
--- a/src/bootstrap/src/core/build_steps/check.rs
+++ b/src/bootstrap/src/core/build_steps/check.rs
@@ -3,16 +3,19 @@
 use crate::core::build_steps::compile::{
     add_to_sysroot, run_cargo, rustc_cargo, rustc_cargo_env, std_cargo, std_crates_for_run_make,
 };
+use crate::core::build_steps::tool;
 use crate::core::build_steps::tool::{COMPILETEST_ALLOW_FEATURES, SourceType, prepare_tool_cargo};
 use crate::core::builder::{
     self, Alias, Builder, Kind, RunConfig, ShouldRun, Step, StepMetadata, crate_description,
 };
 use crate::core::config::TargetSelection;
 use crate::utils::build_stamp::{self, BuildStamp};
-use crate::{Mode, Subcommand};
+use crate::{Compiler, Mode, Subcommand};
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct Std {
+    /// Compiler that will check this std.
+    pub build_compiler: Compiler,
     pub target: TargetSelection,
     /// Whether to build only a subset of crates.
     ///
@@ -20,16 +23,13 @@ pub struct Std {
     ///
     /// [`compile::Rustc`]: crate::core::build_steps::compile::Rustc
     crates: Vec<String>,
-    /// Never use this from outside calls. It is intended for internal use only within `check::Std::make_run`
-    /// and `check::Std::run`.
-    custom_stage: Option<u32>,
 }
 
 impl Std {
     const CRATE_OR_DEPS: &[&str] = &["sysroot", "coretests", "alloctests"];
 
-    pub fn new(target: TargetSelection) -> Self {
-        Self { target, crates: vec![], custom_stage: None }
+    pub fn new(build_compiler: Compiler, target: TargetSelection) -> Self {
+        Self { build_compiler, target, crates: vec![] }
     }
 }
 
@@ -47,59 +47,41 @@ impl Step for Std {
     }
 
     fn make_run(run: RunConfig<'_>) {
-        let crates = std_crates_for_run_make(&run);
-
-        let stage = if run.builder.config.is_explicit_stage() || run.builder.top_stage >= 1 {
-            run.builder.top_stage
-        } else {
-            1
-        };
-
-        run.builder.ensure(Std { target: run.target, crates, custom_stage: Some(stage) });
-    }
-
-    fn run(self, builder: &Builder<'_>) {
-        if !builder.download_rustc() && builder.config.skip_std_check_if_no_download_rustc {
+        if !run.builder.download_rustc() && run.builder.config.skip_std_check_if_no_download_rustc {
             eprintln!(
                 "WARNING: `--skip-std-check-if-no-download-rustc` flag was passed and `rust.download-rustc` is not available. Skipping."
             );
             return;
         }
 
-        let stage = self.custom_stage.unwrap_or(builder.top_stage);
-
-        let target = self.target;
-        let compiler = builder.compiler(stage, builder.config.host_target);
-
-        if stage == 0 {
-            let mut is_explicitly_called =
-                builder.paths.iter().any(|p| p.starts_with("library") || p.starts_with("std"));
-
-            if !is_explicitly_called {
-                for c in Std::CRATE_OR_DEPS {
-                    is_explicitly_called = builder.paths.iter().any(|p| p.starts_with(c));
-                }
-            }
-
-            if is_explicitly_called {
-                eprintln!("WARNING: stage 0 std is precompiled and does nothing during `x check`.");
-            }
-
-            // Reuse the stage0 libstd
-            builder.std(compiler, target);
+        if run.builder.config.compile_time_deps {
+            // libstd doesn't have any important build scripts and can't have any proc macros
             return;
         }
 
+        let crates = std_crates_for_run_make(&run);
+        run.builder.ensure(Std {
+            build_compiler: prepare_compiler_for_check(run.builder, run.target, Mode::Std),
+            target: run.target,
+            crates,
+        });
+    }
+
+    fn run(self, builder: &Builder<'_>) {
+        let build_compiler = self.build_compiler;
+        let stage = build_compiler.stage;
+        let target = self.target;
+
         let mut cargo = builder::Cargo::new(
             builder,
-            compiler,
+            build_compiler,
             Mode::Std,
             SourceType::InTree,
             target,
             Kind::Check,
         );
 
-        std_cargo(builder, target, compiler.stage, &mut cargo);
+        std_cargo(builder, target, stage, &mut cargo);
         if matches!(builder.config.cmd, Subcommand::Fix) {
             // By default, cargo tries to fix all targets. Tell it not to fix tests until we've added `test` to the sysroot.
             cargo.arg("--lib");
@@ -115,16 +97,9 @@ impl Step for Std {
             Some(stage),
         );
 
-        let stamp = build_stamp::libstd_stamp(builder, compiler, target).with_prefix("check");
+        let stamp = build_stamp::libstd_stamp(builder, build_compiler, target).with_prefix("check");
         run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false);
 
-        // We skip populating the sysroot in non-zero stage because that'll lead
-        // to rlib/rmeta conflicts if std gets built during this session.
-        if compiler.stage == 0 {
-            let libdir = builder.sysroot_target_libdir(compiler, target);
-            let hostdir = builder.sysroot_target_libdir(compiler, compiler.host);
-            add_to_sysroot(builder, &libdir, &hostdir, &stamp);
-        }
         drop(_guard);
 
         // don't check test dependencies if we haven't built libtest
@@ -140,21 +115,14 @@ impl Step for Std {
         // Currently only the "libtest" tree of crates does this.
         let mut cargo = builder::Cargo::new(
             builder,
-            compiler,
+            build_compiler,
             Mode::Std,
             SourceType::InTree,
             target,
             Kind::Check,
         );
 
-        // If we're not in stage 0, tests and examples will fail to compile
-        // from `core` definitions being loaded from two different `libcore`
-        // .rmeta and .rlib files.
-        if compiler.stage == 0 {
-            cargo.arg("--all-targets");
-        }
-
-        std_cargo(builder, target, compiler.stage, &mut cargo);
+        std_cargo(builder, target, build_compiler.stage, &mut cargo);
 
         // Explicitly pass -p for all dependencies krates -- this will force cargo
         // to also check the tests/benches/examples for these crates, rather
@@ -163,18 +131,23 @@ impl Step for Std {
             cargo.arg("-p").arg(krate);
         }
 
-        let stamp = build_stamp::libstd_stamp(builder, compiler, target).with_prefix("check-test");
+        let stamp =
+            build_stamp::libstd_stamp(builder, build_compiler, target).with_prefix("check-test");
         let _guard = builder.msg_check("library test/bench/example targets", target, Some(stage));
         run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false);
     }
 
     fn metadata(&self) -> Option<StepMetadata> {
-        Some(StepMetadata::check("std", self.target))
+        Some(StepMetadata::check("std", self.target).built_by(self.build_compiler))
     }
 }
 
+/// Checks rustc using `build_compiler` and copies the built
+/// .rmeta files into the sysroot of `build_compiler`.
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct Rustc {
+    /// Compiler that will check this rustc.
+    pub build_compiler: Compiler,
     pub target: TargetSelection,
     /// Whether to build only a subset of crates.
     ///
@@ -185,13 +158,13 @@ pub struct Rustc {
 }
 
 impl Rustc {
-    pub fn new(target: TargetSelection, builder: &Builder<'_>) -> Self {
+    pub fn new(builder: &Builder<'_>, build_compiler: Compiler, target: TargetSelection) -> Self {
         let crates = builder
             .in_tree_crates("rustc-main", Some(target))
             .into_iter()
             .map(|krate| krate.name.to_string())
             .collect();
-        Self { target, crates }
+        Self { build_compiler, target, crates }
     }
 }
 
@@ -206,40 +179,43 @@ impl Step for Rustc {
 
     fn make_run(run: RunConfig<'_>) {
         let crates = run.make_run_crates(Alias::Compiler);
-        run.builder.ensure(Rustc { target: run.target, crates });
+        run.builder.ensure(Rustc {
+            target: run.target,
+            build_compiler: prepare_compiler_for_check(run.builder, run.target, Mode::Rustc),
+            crates,
+        });
     }
 
-    /// Builds the compiler.
+    /// Check the compiler.
     ///
-    /// This will build the compiler for a particular stage of the build using
+    /// This will check the compiler for a particular stage of the build using
     /// the `compiler` targeting the `target` architecture. The artifacts
     /// created will also be linked into the sysroot directory.
+    ///
+    /// If we check a stage 2 compiler, we will have to first build a stage 1 compiler to check it.
     fn run(self, builder: &Builder<'_>) {
-        let compiler = builder.compiler(builder.top_stage, builder.config.host_target);
+        let build_compiler = self.build_compiler;
         let target = self.target;
 
-        if compiler.stage != 0 {
-            // If we're not in stage 0, then we won't have a std from the beta
-            // compiler around. That means we need to make sure there's one in
-            // the sysroot for the compiler to find. Otherwise, we're going to
-            // fail when building crates that need to generate code (e.g., build
-            // scripts and their dependencies).
-            builder.std(compiler, compiler.host);
-            builder.std(compiler, target);
-        } else {
-            builder.ensure(Std::new(target));
-        }
+        // Build host std for compiling build scripts
+        builder.std(build_compiler, build_compiler.host);
+
+        // Build target std so that the checked rustc can link to it during the check
+        // FIXME: maybe we can a way to only do a check of std here?
+        // But for that we would have to copy the stdlib rmetas to the sysroot of the build
+        // compiler, which conflicts with std rlibs, if we also build std.
+        builder.std(build_compiler, target);
 
         let mut cargo = builder::Cargo::new(
             builder,
-            compiler,
+            build_compiler,
             Mode::Rustc,
             SourceType::InTree,
             target,
             Kind::Check,
         );
 
-        rustc_cargo(builder, &mut cargo, target, &compiler, &self.crates);
+        rustc_cargo(builder, &mut cargo, target, &build_compiler, &self.crates);
 
         // Explicitly pass -p for all compiler crates -- this will force cargo
         // to also check the tests/benches/examples for these crates, rather
@@ -254,22 +230,82 @@ impl Step for Rustc {
             None,
         );
 
-        let stamp = build_stamp::librustc_stamp(builder, compiler, target).with_prefix("check");
+        let stamp =
+            build_stamp::librustc_stamp(builder, build_compiler, target).with_prefix("check");
 
         run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false);
 
-        let libdir = builder.sysroot_target_libdir(compiler, target);
-        let hostdir = builder.sysroot_target_libdir(compiler, compiler.host);
+        let libdir = builder.sysroot_target_libdir(build_compiler, target);
+        let hostdir = builder.sysroot_target_libdir(build_compiler, build_compiler.host);
         add_to_sysroot(builder, &libdir, &hostdir, &stamp);
     }
 
     fn metadata(&self) -> Option<StepMetadata> {
-        Some(StepMetadata::check("rustc", self.target))
+        Some(StepMetadata::check("rustc", self.target).built_by(self.build_compiler))
+    }
+}
+
+/// Prepares a compiler that will check something with the given `mode`.
+fn prepare_compiler_for_check(
+    builder: &Builder<'_>,
+    target: TargetSelection,
+    mode: Mode,
+) -> Compiler {
+    let host = builder.host_target;
+    match mode {
+        Mode::ToolBootstrap => builder.compiler(0, host),
+        Mode::ToolStd => {
+            if builder.config.compile_time_deps {
+                // When --compile-time-deps is passed, we can't use any rustc
+                // other than the bootstrap compiler. Luckily build scripts and
+                // proc macros for tools are unlikely to need nightly.
+                return builder.compiler(0, host);
+            }
+
+            // These tools require the local standard library to be checked
+            let build_compiler = builder.compiler(builder.top_stage, host);
+
+            // We need to build the host stdlib to check the tool itself.
+            // We need to build the target stdlib so that the tool can link to it.
+            builder.std(build_compiler, host);
+            // We could only check this library in theory, but `check::Std` doesn't copy rmetas
+            // into `build_compiler`'s sysroot to avoid clashes with `.rlibs`, so we build it
+            // instead.
+            builder.std(build_compiler, target);
+            build_compiler
+        }
+        Mode::ToolRustc | Mode::Codegen => {
+            // FIXME: this is a hack, see description of Mode::Rustc below
+            let stage = if host == target { builder.top_stage - 1 } else { builder.top_stage };
+            // When checking tool stage N, we check it with compiler stage N-1
+            let build_compiler = builder.compiler(stage, host);
+            builder.ensure(Rustc::new(builder, build_compiler, target));
+            build_compiler
+        }
+        Mode::Rustc => {
+            // This is a horrible hack, because we actually change the compiler stage numbering
+            // here. If you do `x check --stage 1 --host FOO`, we build stage 1 host rustc,
+            // and use that to check stage 1 FOO rustc (which actually makes that stage 2 FOO
+            // rustc).
+            //
+            // FIXME: remove this and either fix cross-compilation check on stage 2 (which has a
+            // myriad of other problems) or disable cross-checking on stage 1.
+            let stage = if host == target { builder.top_stage - 1 } else { builder.top_stage };
+            builder.compiler(stage, host)
+        }
+        Mode::Std => {
+            // When checking std stage N, we want to do it with the stage N compiler
+            // Note: we don't need to build the host stdlib here, because when compiling std, the
+            // stage 0 stdlib is used to compile build scripts and proc macros.
+            builder.compiler(builder.top_stage, host)
+        }
     }
 }
 
+/// Checks a single codegen backend.
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct CodegenBackend {
+    pub build_compiler: Compiler,
     pub target: TargetSelection,
     pub backend: &'static str,
 }
@@ -284,8 +320,10 @@ impl Step for CodegenBackend {
     }
 
     fn make_run(run: RunConfig<'_>) {
+        // FIXME: only check the backend(s) that were actually selected in run.paths
+        let build_compiler = prepare_compiler_for_check(run.builder, run.target, Mode::Codegen);
         for &backend in &["cranelift", "gcc"] {
-            run.builder.ensure(CodegenBackend { target: run.target, backend });
+            run.builder.ensure(CodegenBackend { build_compiler, target: run.target, backend });
         }
     }
 
@@ -296,15 +334,13 @@ impl Step for CodegenBackend {
             return;
         }
 
-        let compiler = builder.compiler(builder.top_stage, builder.config.host_target);
+        let build_compiler = self.build_compiler;
         let target = self.target;
         let backend = self.backend;
 
-        builder.ensure(Rustc::new(target, builder));
-
         let mut cargo = builder::Cargo::new(
             builder,
-            compiler,
+            build_compiler,
             Mode::Codegen,
             SourceType::InTree,
             target,
@@ -314,23 +350,25 @@ impl Step for CodegenBackend {
         cargo
             .arg("--manifest-path")
             .arg(builder.src.join(format!("compiler/rustc_codegen_{backend}/Cargo.toml")));
-        rustc_cargo_env(builder, &mut cargo, target, compiler.stage);
+        rustc_cargo_env(builder, &mut cargo, target, build_compiler.stage);
 
-        let _guard = builder.msg_check(backend, target, None);
+        let _guard = builder.msg_check(format!("rustc_codegen_{backend}"), target, None);
 
-        let stamp = build_stamp::codegen_backend_stamp(builder, compiler, target, backend)
+        let stamp = build_stamp::codegen_backend_stamp(builder, build_compiler, target, backend)
             .with_prefix("check");
 
         run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false);
     }
 
     fn metadata(&self) -> Option<StepMetadata> {
-        Some(StepMetadata::check(self.backend, self.target))
+        Some(StepMetadata::check(self.backend, self.target).built_by(self.build_compiler))
     }
 }
 
+/// Checks Rust analyzer that links to .rmetas from a checked rustc.
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub struct RustAnalyzer {
+    pub build_compiler: Compiler,
     pub target: TargetSelection,
 }
 
@@ -351,18 +389,17 @@ impl Step for RustAnalyzer {
     }
 
     fn make_run(run: RunConfig<'_>) {
-        run.builder.ensure(RustAnalyzer { target: run.target });
+        let build_compiler = prepare_compiler_for_check(run.builder, run.target, Mode::ToolRustc);
+        run.builder.ensure(RustAnalyzer { build_compiler, target: run.target });
     }
 
     fn run(self, builder: &Builder<'_>) {
-        let compiler = builder.compiler(builder.top_stage, builder.config.host_target);
+        let build_compiler = self.build_compiler;
         let target = self.target;
 
-        builder.ensure(Rustc::new(target, builder));
-
         let mut cargo = prepare_tool_cargo(
             builder,
-            compiler,
+            build_compiler,
             Mode::ToolRustc,
             target,
             builder.kind,
@@ -379,7 +416,7 @@ impl Step for RustAnalyzer {
 
         // Cargo's output path in a given stage, compiled by a particular
         // compiler for the specified target.
-        let stamp = BuildStamp::new(&builder.cargo_out(compiler, Mode::ToolRustc, target))
+        let stamp = BuildStamp::new(&builder.cargo_out(build_compiler, Mode::ToolRustc, target))
             .with_prefix("rust-analyzer-check");
 
         let _guard = builder.msg_check("rust-analyzer artifacts", target, None);
@@ -387,7 +424,7 @@ impl Step for RustAnalyzer {
     }
 
     fn metadata(&self) -> Option<StepMetadata> {
-        Some(StepMetadata::check("rust-analyzer", self.target))
+        Some(StepMetadata::check("rust-analyzer", self.target).built_by(self.build_compiler))
     }
 }
 
@@ -417,19 +454,11 @@ impl Step for Compiletest {
         } else {
             Mode::ToolStd
         };
-
-        let compiler = builder.compiler(
-            if mode == Mode::ToolBootstrap { 0 } else { builder.top_stage },
-            builder.config.host_target,
-        );
-
-        if mode != Mode::ToolBootstrap {
-            builder.ensure(Rustc::new(self.target, builder));
-        }
+        let build_compiler = prepare_compiler_for_check(builder, self.target, mode);
 
         let mut cargo = prepare_tool_cargo(
             builder,
-            compiler,
+            build_compiler,
             mode,
             self.target,
             builder.kind,
@@ -442,7 +471,7 @@ impl Step for Compiletest {
 
         cargo.arg("--all-targets");
 
-        let stamp = BuildStamp::new(&builder.cargo_out(compiler, mode, self.target))
+        let stamp = BuildStamp::new(&builder.cargo_out(build_compiler, mode, self.target))
             .with_prefix("compiletest-check");
 
         let _guard = builder.msg_check("compiletest artifacts", self.target, None);
@@ -460,12 +489,15 @@ macro_rules! tool_check_step {
             // The part of this path after the final '/' is also used as a display name.
             path: $path:literal
             $(, alt_path: $alt_path:literal )*
+            , mode: $mode:path
+            $(, allow_features: $allow_features:expr )?
             $(, default: $default:literal )?
             $( , )?
         }
     ) => {
         #[derive(Debug, Clone, PartialEq, Eq, Hash)]
         pub struct $name {
+            pub build_compiler: Compiler,
             pub target: TargetSelection,
         }
 
@@ -480,16 +512,30 @@ macro_rules! tool_check_step {
             }
 
             fn make_run(run: RunConfig<'_>) {
-                run.builder.ensure($name { target: run.target });
+                let target = run.target;
+                let build_compiler = prepare_compiler_for_check(run.builder, target, $mode);
+
+                // It doesn't make sense to cross-check bootstrap tools
+                if $mode == Mode::ToolBootstrap && target != run.builder.host_target {
+                    println!("WARNING: not checking bootstrap tool {} for target {target} as it is a bootstrap (host-only) tool", stringify!($path));
+                    return;
+                };
+
+                run.builder.ensure($name { target, build_compiler });
             }
 
             fn run(self, builder: &Builder<'_>) {
-                let Self { target } = self;
-                run_tool_check_step(builder, target, stringify!($name), $path);
+                let Self { target, build_compiler } = self;
+                let allow_features = {
+                    let mut _value = "";
+                    $( _value = $allow_features; )?
+                    _value
+                };
+                run_tool_check_step(builder, build_compiler, target, $path, $mode, allow_features);
             }
 
             fn metadata(&self) -> Option<StepMetadata> {
-                Some(StepMetadata::check(stringify!($name), self.target))
+                Some(StepMetadata::check(stringify!($name), self.target).built_by(self.build_compiler))
             }
         }
     }
@@ -498,19 +544,18 @@ macro_rules! tool_check_step {
 /// Used by the implementation of `Step::run` in `tool_check_step!`.
 fn run_tool_check_step(
     builder: &Builder<'_>,
+    build_compiler: Compiler,
     target: TargetSelection,
-    step_type_name: &str,
     path: &str,
+    mode: Mode,
+    allow_features: &str,
 ) {
     let display_name = path.rsplit('/').next().unwrap();
-    let compiler = builder.compiler(builder.top_stage, builder.config.host_target);
-
-    builder.ensure(Rustc::new(target, builder));
 
     let mut cargo = prepare_tool_cargo(
         builder,
-        compiler,
-        Mode::ToolRustc,
+        build_compiler,
+        mode,
         target,
         builder.kind,
         path,
@@ -521,98 +566,65 @@ fn run_tool_check_step(
         SourceType::InTree,
         &[],
     );
+    cargo.allow_features(allow_features);
 
+    // FIXME: check bootstrap doesn't currently work with --all-targets
     cargo.arg("--all-targets");
 
-    let stamp = BuildStamp::new(&builder.cargo_out(compiler, Mode::ToolRustc, target))
-        .with_prefix(&format!("{}-check", step_type_name.to_lowercase()));
+    let stamp = BuildStamp::new(&builder.cargo_out(build_compiler, mode, target))
+        .with_prefix(&format!("{display_name}-check"));
+
+    let stage = match mode {
+        // Mode::ToolRustc is included here because of how msg_sysroot_tool prints stages
+        Mode::Std | Mode::ToolRustc => build_compiler.stage,
+        _ => build_compiler.stage + 1,
+    };
 
-    let _guard = builder.msg_check(format!("{display_name} artifacts"), target, None);
+    let _guard =
+        builder.msg_tool(builder.kind, mode, display_name, stage, &build_compiler.host, &target);
     run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false);
 }
 
-tool_check_step!(Rustdoc { path: "src/tools/rustdoc", alt_path: "src/librustdoc" });
+tool_check_step!(Rustdoc {
+    path: "src/tools/rustdoc",
+    alt_path: "src/librustdoc",
+    mode: Mode::ToolRustc
+});
 // Clippy, miri and Rustfmt are hybrids. They are external tools, but use a git subtree instead
 // of a submodule. Since the SourceType only drives the deny-warnings
 // behavior, treat it as in-tree so that any new warnings in clippy will be
 // rejected.
-tool_check_step!(Clippy { path: "src/tools/clippy" });
-tool_check_step!(Miri { path: "src/tools/miri" });
-tool_check_step!(CargoMiri { path: "src/tools/miri/cargo-miri" });
-tool_check_step!(Rustfmt { path: "src/tools/rustfmt" });
-tool_check_step!(MiroptTestTools { path: "src/tools/miropt-test-tools" });
-tool_check_step!(TestFloatParse { path: "src/tools/test-float-parse" });
-tool_check_step!(FeaturesStatusDump { path: "src/tools/features-status-dump" });
-
-tool_check_step!(Bootstrap { path: "src/bootstrap", default: false });
+tool_check_step!(Clippy { path: "src/tools/clippy", mode: Mode::ToolRustc });
+tool_check_step!(Miri { path: "src/tools/miri", mode: Mode::ToolRustc });
+tool_check_step!(CargoMiri { path: "src/tools/miri/cargo-miri", mode: Mode::ToolRustc });
+tool_check_step!(Rustfmt { path: "src/tools/rustfmt", mode: Mode::ToolRustc });
+tool_check_step!(MiroptTestTools {
+    path: "src/tools/miropt-test-tools",
+    mode: Mode::ToolBootstrap
+});
+// We want to test the local std
+tool_check_step!(TestFloatParse {
+    path: "src/tools/test-float-parse",
+    mode: Mode::ToolStd,
+    allow_features: tool::TestFloatParse::ALLOW_FEATURES
+});
+tool_check_step!(FeaturesStatusDump {
+    path: "src/tools/features-status-dump",
+    mode: Mode::ToolBootstrap
+});
+
+tool_check_step!(Bootstrap { path: "src/bootstrap", mode: Mode::ToolBootstrap, default: false });
 
 // `run-make-support` will be built as part of suitable run-make compiletest test steps, but support
 // check to make it easier to work on.
-tool_check_step!(RunMakeSupport { path: "src/tools/run-make-support", default: false });
-
-/// Check step for the `coverage-dump` bootstrap tool. The coverage-dump tool
-/// is used internally by coverage tests.
-///
-/// FIXME(Zalathar): This is temporarily separate from the other tool check
-/// steps so that it can use the stage 0 compiler instead of `top_stage`,
-/// without introducing conflicts with the stage 0 redesign (#119899).
-///
-/// After the stage 0 redesign lands, we can look into using the stage 0
-/// compiler to check all bootstrap tools (#139170).
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub(crate) struct CoverageDump;
-
-impl CoverageDump {
-    const PATH: &str = "src/tools/coverage-dump";
-}
-
-impl Step for CoverageDump {
-    type Output = ();
-
-    /// Most contributors won't care about coverage-dump, so don't make their
-    /// check builds slower unless they opt in and check it explicitly.
-    const DEFAULT: bool = false;
-    const ONLY_HOSTS: bool = true;
-
-    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
-        run.path(Self::PATH)
-    }
-
-    fn make_run(run: RunConfig<'_>) {
-        run.builder.ensure(Self {});
-    }
-
-    fn run(self, builder: &Builder<'_>) -> Self::Output {
-        // Make sure we haven't forgotten any fields, if there are any.
-        let Self {} = self;
-        let display_name = "coverage-dump";
-        let host = builder.config.host_target;
-        let target = host;
-        let mode = Mode::ToolBootstrap;
-
-        let compiler = builder.compiler(0, host);
-        let cargo = prepare_tool_cargo(
-            builder,
-            compiler,
-            mode,
-            target,
-            builder.kind,
-            Self::PATH,
-            SourceType::InTree,
-            &[],
-        );
-
-        let stamp = BuildStamp::new(&builder.cargo_out(compiler, mode, target))
-            .with_prefix(&format!("{display_name}-check"));
-
-        let _guard = builder.msg_tool(
-            builder.kind,
-            mode,
-            display_name,
-            compiler.stage,
-            &compiler.host,
-            &target,
-        );
-        run_cargo(builder, cargo, builder.config.free_args.clone(), &stamp, vec![], true, false);
-    }
-}
+tool_check_step!(RunMakeSupport {
+    path: "src/tools/run-make-support",
+    mode: Mode::ToolBootstrap,
+    default: false
+});
+
+tool_check_step!(CoverageDump {
+    path: "src/tools/coverage-dump",
+    mode: Mode::ToolBootstrap,
+    default: false
+});
diff --git a/src/bootstrap/src/core/build_steps/clippy.rs b/src/bootstrap/src/core/build_steps/clippy.rs
index 1e44b5b67a4..a0371eb7155 100644
--- a/src/bootstrap/src/core/build_steps/clippy.rs
+++ b/src/bootstrap/src/core/build_steps/clippy.rs
@@ -215,7 +215,7 @@ impl Step for Rustc {
                 builder.std(compiler, compiler.host);
                 builder.std(compiler, target);
             } else {
-                builder.ensure(check::Std::new(target));
+                builder.ensure(check::Std::new(compiler, target));
             }
         }
 
@@ -287,7 +287,7 @@ macro_rules! lint_any {
                 let target = self.target;
 
                 if !builder.download_rustc() {
-                    builder.ensure(check::Rustc::new(target, builder));
+                    builder.ensure(check::Rustc::new(builder, compiler, target));
                 };
 
                 let cargo = prepare_tool_cargo(
diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs
index 431c242608b..09bb2e35bda 100644
--- a/src/bootstrap/src/core/build_steps/compile.rs
+++ b/src/bootstrap/src/core/build_steps/compile.rs
@@ -449,26 +449,24 @@ fn copy_self_contained_objects(
     target_deps
 }
 
-/// Resolves standard library crates for `Std::run_make` for any build kind (like check, build, clippy, etc.).
+/// Resolves standard library crates for `Std::run_make` for any build kind (like check, doc,
+/// build, clippy, etc.).
 pub fn std_crates_for_run_make(run: &RunConfig<'_>) -> Vec<String> {
-    // FIXME: Extend builder tests to cover the `crates` field of `Std` instances.
-    if cfg!(test) {
-        return vec![];
-    }
-
-    let has_alias = run.paths.iter().any(|set| set.assert_single_path().path.ends_with("library"));
+    let mut crates = run.make_run_crates(builder::Alias::Library);
+
+    // For no_std targets, we only want to check core and alloc
+    // Regardless of core/alloc being selected explicitly or via the "library" default alias,
+    // we only want to keep these two crates.
+    // The set of no_std crates should be kept in sync with what `Builder::std_cargo` does.
+    // Note: an alternative design would be to return an enum from this function (Default vs Subset)
+    // of crates. However, several steps currently pass `-p <package>` even if all crates are
+    // selected, because Cargo behaves differently in that case. To keep that behavior without
+    // making further changes, we pre-filter the no-std crates here.
     let target_is_no_std = run.builder.no_std(run.target).unwrap_or(false);
-
-    // For no_std targets, do not add any additional crates to the compilation other than what `compile::std_cargo` already adds for no_std targets.
     if target_is_no_std {
-        vec![]
-    }
-    // If the paths include "library", build the entire standard library.
-    else if has_alias {
-        run.make_run_crates(builder::Alias::Library)
-    } else {
-        run.cargo_crates_in_set()
+        crates.retain(|c| c == "core" || c == "alloc");
     }
+    crates
 }
 
 /// Tries to find LLVM's `compiler-rt` source directory, for building `library/profiler_builtins`.
@@ -1369,9 +1367,7 @@ pub fn rustc_cargo_env(
     }
 
     // Enable rustc's env var for `rust-lld` when requested.
-    if builder.config.lld_enabled
-        && (builder.config.channel == "dev" || builder.config.channel == "nightly")
-    {
+    if builder.config.lld_enabled {
         cargo.env("CFG_USE_SELF_CONTAINED_LINKER", "1");
     }
 
@@ -1741,17 +1737,19 @@ fn copy_codegen_backends_to_sysroot(
         }
 
         let stamp = build_stamp::codegen_backend_stamp(builder, compiler, target, backend);
-        let dylib = t!(fs::read_to_string(stamp.path()));
-        let file = Path::new(&dylib);
-        let filename = file.file_name().unwrap().to_str().unwrap();
-        // change `librustc_codegen_cranelift-xxxxxx.so` to
-        // `librustc_codegen_cranelift-release.so`
-        let target_filename = {
-            let dash = filename.find('-').unwrap();
-            let dot = filename.find('.').unwrap();
-            format!("{}-{}{}", &filename[..dash], builder.rust_release(), &filename[dot..])
-        };
-        builder.copy_link(file, &dst.join(target_filename), FileType::NativeLibrary);
+        if stamp.path().exists() {
+            let dylib = t!(fs::read_to_string(stamp.path()));
+            let file = Path::new(&dylib);
+            let filename = file.file_name().unwrap().to_str().unwrap();
+            // change `librustc_codegen_cranelift-xxxxxx.so` to
+            // `librustc_codegen_cranelift-release.so`
+            let target_filename = {
+                let dash = filename.find('-').unwrap();
+                let dot = filename.find('.').unwrap();
+                format!("{}-{}{}", &filename[..dash], builder.rust_release(), &filename[dot..])
+            };
+            builder.copy_link(file, &dst.join(target_filename), FileType::NativeLibrary);
+        }
     }
 }
 
@@ -2057,14 +2055,20 @@ impl Step for Assemble {
                 trace!("llvm-bitcode-linker enabled, installing");
                 let llvm_bitcode_linker =
                     builder.ensure(crate::core::build_steps::tool::LlvmBitcodeLinker {
-                        compiler,
+                        build_compiler: compiler,
                         target: target_compiler.host,
-                        extra_features: vec![],
                     });
+
+                // Copy the llvm-bitcode-linker to the self-contained binary directory
+                let bindir_self_contained = builder
+                    .sysroot(compiler)
+                    .join(format!("lib/rustlib/{}/bin/self-contained", compiler.host));
                 let tool_exe = exe("llvm-bitcode-linker", target_compiler.host);
+
+                t!(fs::create_dir_all(&bindir_self_contained));
                 builder.copy_link(
                     &llvm_bitcode_linker.tool_path,
-                    &libdir_bin.join(tool_exe),
+                    &bindir_self_contained.join(tool_exe),
                     FileType::Executable,
                 );
             }
@@ -2162,6 +2166,25 @@ impl Step for Assemble {
                 continue; // Already built as part of rustc
             }
 
+            // FIXME: this is a horrible hack used to make `x check` work when other codegen
+            // backends are enabled.
+            // `x check` will check stage 1 rustc, which copies its rmetas to the stage0 sysroot.
+            // Then it checks codegen backends, which correctly use these rmetas.
+            // Then it needs to check std, but for that it needs to build stage 1 rustc.
+            // This copies the build rmetas into the stage0 sysroot, effectively poisoning it,
+            // because we then have both check and build rmetas in the same sysroot.
+            // That would be fine on its own. However, when another codegen backend is enabled,
+            // then building stage 1 rustc implies also building stage 1 codegen backend (even if
+            // it isn't used for anything). And since that tries to use the poisoned
+            // rmetas, it fails to build.
+            // We don't actually need to build rustc-private codegen backends for checking std,
+            // so instead we skip that.
+            // Note: this would be also an issue for other rustc-private tools, but that is "solved"
+            // by check::Std being last in the list of checked things (see
+            // `Builder::get_step_descriptions`).
+            if builder.kind == Kind::Check && builder.top_stage == 1 {
+                continue;
+            }
             builder.ensure(CodegenBackend {
                 compiler: build_compiler,
                 target: target_compiler.host,
@@ -2377,10 +2400,10 @@ pub fn run_cargo(
                 keep = true;
             } else if rlib_only_metadata {
                 if filename.contains("jemalloc_sys")
-                    || filename.contains("rustc_smir")
-                    || filename.contains("stable_mir")
+                    || filename.contains("rustc_public_bridge")
+                    || filename.contains("rustc_public")
                 {
-                    // jemalloc_sys and rustc_smir are not linked into librustc_driver.so,
+                    // jemalloc_sys and rustc_public_bridge are not linked into librustc_driver.so,
                     // so we need to distribute them as rlib to be able to use them.
                     keep |= filename.ends_with(".rlib");
                 } else {
@@ -2551,7 +2574,7 @@ pub fn stream_cargo(
     }
 
     // Make sure Cargo actually succeeded after we read all of its stdout.
-    let status = t!(streaming_command.wait());
+    let status = t!(streaming_command.wait(&builder.config.exec_ctx));
     if builder.is_verbose() && !status.success() {
         eprintln!(
             "command did not execute successfully: {cmd:?}\n\
diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs
index 25b7e5a1b5d..8b2d65ace50 100644
--- a/src/bootstrap/src/core/build_steps/dist.rs
+++ b/src/bootstrap/src/core/build_steps/dist.rs
@@ -2375,7 +2375,7 @@ impl Step for LlvmBitcodeLinker {
         builder.ensure(compile::Rustc::new(compiler, target));
 
         let llbc_linker =
-            builder.ensure(tool::LlvmBitcodeLinker { compiler, target, extra_features: vec![] });
+            builder.ensure(tool::LlvmBitcodeLinker { build_compiler: compiler, target });
 
         let self_contained_bin_dir = format!("lib/rustlib/{}/bin/self-contained", target.triple);
 
diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs
index f7c4c5ad0bb..37418f640ac 100644
--- a/src/bootstrap/src/core/build_steps/doc.rs
+++ b/src/bootstrap/src/core/build_steps/doc.rs
@@ -665,7 +665,11 @@ impl Step for Std {
     }
 
     fn metadata(&self) -> Option<StepMetadata> {
-        Some(StepMetadata::doc("std", self.target).stage(self.stage))
+        Some(
+            StepMetadata::doc("std", self.target)
+                .stage(self.stage)
+                .with_metadata(format!("crates=[{}]", self.crates.join(","))),
+        )
     }
 }
 
@@ -739,10 +743,6 @@ fn doc_std(
     }
 
     for krate in requested_crates {
-        if krate == "sysroot" {
-            // The sysroot crate is an implementation detail, don't include it in public docs.
-            continue;
-        }
         cargo.arg("-p").arg(krate);
     }
 
diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs
index de67a5f77e6..b2056f5cf37 100644
--- a/src/bootstrap/src/core/build_steps/llvm.rs
+++ b/src/bootstrap/src/core/build_steps/llvm.rs
@@ -263,6 +263,15 @@ impl Step for Llvm {
     }
 
     /// Compile LLVM for `target`.
+    #[cfg_attr(
+        feature = "tracing",
+        instrument(
+            level = "debug",
+            name = "Llvm::run",
+            skip_all,
+            fields(target = ?self.target),
+        ),
+    )]
     fn run(self, builder: &Builder<'_>) -> LlvmResult {
         let target = self.target;
         let target_native = if self.target.starts_with("riscv") {
diff --git a/src/bootstrap/src/core/build_steps/mod.rs b/src/bootstrap/src/core/build_steps/mod.rs
index fcb6abea434..c2ad9a4df59 100644
--- a/src/bootstrap/src/core/build_steps/mod.rs
+++ b/src/bootstrap/src/core/build_steps/mod.rs
@@ -11,7 +11,6 @@ pub(crate) mod llvm;
 pub(crate) mod perf;
 pub(crate) mod run;
 pub(crate) mod setup;
-pub(crate) mod suggest;
 pub(crate) mod synthetic_targets;
 pub(crate) mod test;
 pub(crate) mod tool;
diff --git a/src/bootstrap/src/core/build_steps/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs
index 37fc85518e0..9f9af1d9abe 100644
--- a/src/bootstrap/src/core/build_steps/setup.rs
+++ b/src/bootstrap/src/core/build_steps/setup.rs
@@ -586,12 +586,14 @@ Select which editor you would like to set up [default: None]: ";
                 "b5dd299b93dca3ceeb9b335f929293cb3d4bf4977866fbe7ceeac2a8a9f99088",
                 "631c837b0e98ae35fd48b0e5f743b1ca60adadf2d0a2b23566ba25df372cf1a9",
                 "080955765db84bb6cbf178879f489c4e2369397626a6ecb3debedb94a9d0b3ce",
+                "f501475c6654187091c924ae26187fa5791d74d4a8ab3fb61fbbe4c0275aade1",
             ],
             EditorKind::Helix => &[
                 "2d3069b8cf1b977e5d4023965eb6199597755e6c96c185ed5f2854f98b83d233",
                 "6736d61409fbebba0933afd2e4c44ff2f97c1cb36cf0299a7f4a7819b8775040",
                 "f252dcc30ca85a193a699581e5e929d5bd6c19d40d7a7ade5e257a9517a124a5",
                 "198c195ed0c070d15907b279b8b4ea96198ca71b939f5376454f3d636ab54da5",
+                "1c43ead340b20792b91d02b08494ee68708e7e09f56b6766629b4b72079208f1",
             ],
             EditorKind::Vim | EditorKind::VsCode => &[
                 "ea67e259dedf60d4429b6c349a564ffcd1563cf41c920a856d1f5b16b4701ac8",
@@ -607,12 +609,14 @@ Select which editor you would like to set up [default: None]: ";
                 "e53e9129ca5ee5dcbd6ec8b68c2d87376474eb154992deba3c6d9ab1703e0717",
                 "f954316090936c7e590c253ca9d524008375882fa13c5b41d7e2547a896ff893",
                 "701b73751efd7abd6487f2c79348dab698af7ac4427b79fa3d2087c867144b12",
+                "a61df796c0c007cb6512127330564e49e57d558dec715703916a928b072a1054",
             ],
             EditorKind::Zed => &[
                 "bbce727c269d1bd0c98afef4d612eb4ce27aea3c3a8968c5f10b31affbc40b6c",
                 "a5380cf5dd9328731aecc5dfb240d16dac46ed272126b9728006151ef42f5909",
                 "2e96bf0d443852b12f016c8fc9840ab3d0a2b4fe0b0fb3a157e8d74d5e7e0e26",
                 "4fadd4c87389a601a27db0d3d74a142fa3a2e656ae78982e934dbe24bee32ad6",
+                "f0bb3d23ab1a49175ab0ef5c4071af95bb03d01d460776cdb716d91333443382",
             ],
         }
     }
diff --git a/src/bootstrap/src/core/build_steps/suggest.rs b/src/bootstrap/src/core/build_steps/suggest.rs
deleted file mode 100644
index fd4918961ad..00000000000
--- a/src/bootstrap/src/core/build_steps/suggest.rs
+++ /dev/null
@@ -1,68 +0,0 @@
-//! Attempt to magically identify good tests to run
-
-use std::path::PathBuf;
-use std::str::FromStr;
-
-use clap::Parser;
-
-use crate::core::build_steps::tool::Tool;
-use crate::core::builder::Builder;
-
-/// Suggests a list of possible `x.py` commands to run based on modified files in branch.
-pub fn suggest(builder: &Builder<'_>, run: bool) {
-    let git_config = builder.config.git_config();
-    let suggestions = builder
-        .tool_cmd(Tool::SuggestTests)
-        .env("SUGGEST_TESTS_NIGHTLY_BRANCH", git_config.nightly_branch)
-        .env("SUGGEST_TESTS_MERGE_COMMIT_EMAIL", git_config.git_merge_commit_email)
-        .run_capture_stdout(builder)
-        .stdout();
-
-    let suggestions = suggestions
-        .lines()
-        .map(|line| {
-            let mut sections = line.split_ascii_whitespace();
-
-            // this code expects one suggestion per line in the following format:
-            // <x_subcommand> {some number of flags} [optional stage number]
-            let cmd = sections.next().unwrap();
-            let stage = sections.next_back().and_then(|s| str::parse(s).ok());
-            let paths: Vec<PathBuf> = sections.map(|p| PathBuf::from_str(p).unwrap()).collect();
-
-            (cmd, stage, paths)
-        })
-        .collect::<Vec<_>>();
-
-    if !suggestions.is_empty() {
-        println!("==== SUGGESTIONS ====");
-        for sug in &suggestions {
-            print!("x {} ", sug.0);
-            if let Some(stage) = sug.1 {
-                print!("--stage {stage} ");
-            }
-
-            for path in &sug.2 {
-                print!("{} ", path.display());
-            }
-            println!();
-        }
-        println!("=====================");
-    } else {
-        println!("No suggestions found!");
-        return;
-    }
-
-    if run {
-        for sug in suggestions {
-            let mut build: crate::Build = builder.build.clone();
-            build.config.paths = sug.2;
-            build.config.cmd = crate::core::config::flags::Flags::parse_from(["x.py", sug.0]).cmd;
-            if let Some(stage) = sug.1 {
-                build.config.stage = stage;
-            }
-            build.build();
-        }
-    } else {
-        println!("HELP: consider using the `--run` flag to automatically run suggested tests");
-    }
-}
diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs
index a5b7b22aba8..9e7ea5c115f 100644
--- a/src/bootstrap/src/core/build_steps/test.rs
+++ b/src/bootstrap/src/core/build_steps/test.rs
@@ -47,12 +47,11 @@ impl Step for CrateBootstrap {
     const DEFAULT: bool = true;
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
-        // This step is responsible for several different tool paths. By default
-        // it will test all of them, but requesting specific tools on the
-        // command-line (e.g. `./x test suggest-tests`) will test only the
-        // specified tools.
+        // This step is responsible for several different tool paths.
+        //
+        // By default, it will test all of them, but requesting specific tools on the command-line
+        // (e.g. `./x test src/tools/coverage-dump`) will test only the specified tools.
         run.path("src/tools/jsondoclint")
-            .path("src/tools/suggest-tests")
             .path("src/tools/replace-version-placeholder")
             .path("src/tools/coverage-dump")
             // We want `./x test tidy` to _run_ the tidy tool, not its tests.
@@ -262,7 +261,13 @@ impl Step for Cargotest {
             .args(builder.config.test_args())
             .env("RUSTC", builder.rustc(compiler))
             .env("RUSTDOC", builder.rustdoc(compiler));
-        add_rustdoc_cargo_linker_args(&mut cmd, builder, compiler.host, LldThreads::No);
+        add_rustdoc_cargo_linker_args(
+            &mut cmd,
+            builder,
+            compiler.host,
+            LldThreads::No,
+            compiler.stage,
+        );
         cmd.delay_failure().run(builder);
     }
 }
@@ -550,8 +555,13 @@ impl Step for Miri {
         // Miri has its own "target dir" for ui test dependencies. Make sure it gets cleared when
         // the sysroot gets rebuilt, to avoid "found possibly newer version of crate `std`" errors.
         if !builder.config.dry_run() {
-            let ui_test_dep_dir =
-                builder.stage_out(miri.build_compiler, Mode::ToolStd).join("miri_ui");
+            // This has to match `CARGO_TARGET_TMPDIR` in Miri's `ui.rs`.
+            // This means we need `host` here as that's the target `ui.rs` is built for.
+            let ui_test_dep_dir = builder
+                .stage_out(miri.build_compiler, Mode::ToolStd)
+                .join(host)
+                .join("tmp")
+                .join("miri_ui");
             // The mtime of `miri_sysroot` changes when the sysroot gets rebuilt (also see
             // <https://github.com/RalfJung/rustc-build-sysroot/commit/10ebcf60b80fe2c3dc765af0ff19fdc0da4b7466>).
             // We can hence use that directly as a signal to clear the ui test dir.
@@ -674,9 +684,9 @@ impl Step for CargoMiri {
                 cargo.arg("--doc");
             }
         }
-
-        // Finally, pass test-args and run everything.
         cargo.arg("--").args(builder.config.test_args());
+
+        // Finally, run everything.
         let mut cargo = BootstrapCommand::from(cargo);
         {
             let _guard = builder.msg_sysroot_tool(Kind::Test, stage, "cargo-miri", host, target);
@@ -857,7 +867,7 @@ impl Step for RustdocTheme {
             .env("CFG_RELEASE_CHANNEL", &builder.config.channel)
             .env("RUSTDOC_REAL", builder.rustdoc(self.compiler))
             .env("RUSTC_BOOTSTRAP", "1");
-        cmd.args(linker_args(builder, self.compiler.host, LldThreads::No));
+        cmd.args(linker_args(builder, self.compiler.host, LldThreads::No, self.compiler.stage));
 
         cmd.delay_failure().run(builder);
     }
@@ -1033,7 +1043,13 @@ impl Step for RustdocGUI {
         cmd.env("RUSTDOC", builder.rustdoc(self.compiler))
             .env("RUSTC", builder.rustc(self.compiler));
 
-        add_rustdoc_cargo_linker_args(&mut cmd, builder, self.compiler.host, LldThreads::No);
+        add_rustdoc_cargo_linker_args(
+            &mut cmd,
+            builder,
+            self.compiler.host,
+            LldThreads::No,
+            self.compiler.stage,
+        );
 
         for path in &builder.paths {
             if let Some(p) = helpers::is_valid_test_suite_arg(path, "tests/rustdoc-gui", builder) {
@@ -1812,7 +1828,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
         }
 
         let mut hostflags = flags.clone();
-        hostflags.extend(linker_flags(builder, compiler.host, LldThreads::No));
+        hostflags.extend(linker_flags(builder, compiler.host, LldThreads::No, compiler.stage));
 
         let mut targetflags = flags;
 
diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs
index ad3f8d89767..1c994b0ccfc 100644
--- a/src/bootstrap/src/core/build_steps/tool.rs
+++ b/src/bootstrap/src/core/build_steps/tool.rs
@@ -9,6 +9,7 @@
 //! Each Rust tool **MUST** utilize `ToolBuild` inside their `Step` logic,
 //! return `ToolBuildResult` and should never prepare `cargo` invocations manually.
 
+use std::ffi::OsStr;
 use std::path::PathBuf;
 use std::{env, fs};
 
@@ -77,7 +78,7 @@ impl Builder<'_> {
                 *target,
             ),
             // doesn't depend on compiler, same as host compiler
-            _ => self.msg(Kind::Build, build_stage, format_args!("tool {tool}"), *host, *target),
+            _ => self.msg(kind, build_stage, format_args!("tool {tool}"), *host, *target),
         }
     }
 }
@@ -136,19 +137,6 @@ impl Step for ToolBuild {
             _ => panic!("unexpected Mode for tool build"),
         }
 
-        // build.tool.TOOL_NAME.features in bootstrap.toml allows specifying which features to
-        // enable for a specific tool. `extra_features` instead is not controlled by the toml and
-        // provides features that are always enabled for a specific tool (e.g. "in-rust-tree" for
-        // rust-analyzer). Finally, `prepare_tool_cargo` might add more features to adapt the build
-        // to the chosen flags (e.g. "all-static" for cargo if `cargo_native_static` is true).
-        let mut features = builder
-            .config
-            .tool
-            .get(self.tool)
-            .and_then(|tool| tool.features.clone())
-            .unwrap_or_default();
-        features.extend(self.extra_features.clone());
-
         let mut cargo = prepare_tool_cargo(
             builder,
             self.compiler,
@@ -157,7 +145,7 @@ impl Step for ToolBuild {
             Kind::Build,
             path,
             self.source_type,
-            &features,
+            &self.extra_features,
         );
 
         // The stage0 compiler changes infrequently and does not directly depend on code
@@ -244,7 +232,8 @@ pub fn prepare_tool_cargo(
 ) -> CargoCommand {
     let mut cargo = builder::Cargo::new(builder, compiler, mode, source_type, target, cmd_kind);
 
-    let dir = builder.src.join(path);
+    let path = PathBuf::from(path);
+    let dir = builder.src.join(&path);
     cargo.arg("--manifest-path").arg(dir.join("Cargo.toml"));
 
     let mut features = extra_features.to_vec();
@@ -261,6 +250,18 @@ pub fn prepare_tool_cargo(
         }
     }
 
+    // build.tool.TOOL_NAME.features in bootstrap.toml allows specifying which features to enable
+    // for a specific tool. `extra_features` instead is not controlled by the toml and provides
+    // features that are always enabled for a specific tool (e.g. "in-rust-tree" for rust-analyzer).
+    // Finally, `prepare_tool_cargo` above here might add more features to adapt the build
+    // to the chosen flags (e.g. "all-static" for cargo if `cargo_native_static` is true).
+    builder
+        .config
+        .tool
+        .iter()
+        .filter(|(tool_name, _)| path.file_name().and_then(OsStr::to_str) == Some(tool_name))
+        .for_each(|(_, tool)| features.extend(tool.features.clone().unwrap_or_default()));
+
     // clippy tests need to know about the stage sysroot. Set them consistently while building to
     // avoid rebuilding when running tests.
     cargo.env("SYSROOT", builder.sysroot(compiler));
@@ -516,7 +517,6 @@ bootstrap_tool!(
     ReplaceVersionPlaceholder, "src/tools/replace-version-placeholder", "replace-version-placeholder";
     CollectLicenseMetadata, "src/tools/collect-license-metadata", "collect-license-metadata";
     GenerateCopyright, "src/tools/generate-copyright", "generate-copyright";
-    SuggestTests, "src/tools/suggest-tests", "suggest-tests";
     GenerateWindowsSys, "src/tools/generate-windows-sys", "generate-windows-sys";
     // rustdoc-gui-test has a crate dependency on compiletest, so it needs the same unstable features.
     RustdocGUITest, "src/tools/rustdoc-gui-test", "rustdoc-gui-test", is_unstable_tool = true, allow_features = COMPILETEST_ALLOW_FEATURES;
@@ -910,6 +910,13 @@ impl Step for LldWrapper {
 
         tool_result
     }
+
+    fn metadata(&self) -> Option<StepMetadata> {
+        Some(
+            StepMetadata::build("LldWrapper", self.target_compiler.host)
+                .built_by(self.build_compiler),
+        )
+    }
 }
 
 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
@@ -1014,9 +1021,8 @@ impl Step for RustAnalyzerProcMacroSrv {
 
 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct LlvmBitcodeLinker {
-    pub compiler: Compiler,
+    pub build_compiler: Compiler,
     pub target: TargetSelection,
-    pub extra_features: Vec<String>,
 }
 
 impl Step for LlvmBitcodeLinker {
@@ -1032,8 +1038,9 @@ impl Step for LlvmBitcodeLinker {
 
     fn make_run(run: RunConfig<'_>) {
         run.builder.ensure(LlvmBitcodeLinker {
-            compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.host_target),
-            extra_features: Vec::new(),
+            build_compiler: run
+                .builder
+                .compiler(run.builder.top_stage, run.builder.config.host_target),
             target: run.target,
         });
     }
@@ -1043,35 +1050,22 @@ impl Step for LlvmBitcodeLinker {
         instrument(level = "debug", name = "LlvmBitcodeLinker::run", skip_all)
     )]
     fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
-        let tool_result = builder.ensure(ToolBuild {
-            compiler: self.compiler,
+        builder.ensure(ToolBuild {
+            compiler: self.build_compiler,
             target: self.target,
             tool: "llvm-bitcode-linker",
             mode: Mode::ToolRustc,
             path: "src/tools/llvm-bitcode-linker",
             source_type: SourceType::InTree,
-            extra_features: self.extra_features,
+            extra_features: vec![],
             allow_features: "",
             cargo_args: Vec::new(),
             artifact_kind: ToolArtifactKind::Binary,
-        });
+        })
+    }
 
-        if tool_result.target_compiler.stage > 0 {
-            let bindir_self_contained = builder
-                .sysroot(tool_result.target_compiler)
-                .join(format!("lib/rustlib/{}/bin/self-contained", self.target.triple));
-            t!(fs::create_dir_all(&bindir_self_contained));
-            let bin_destination = bindir_self_contained
-                .join(exe("llvm-bitcode-linker", tool_result.target_compiler.host));
-            builder.copy_link(&tool_result.tool_path, &bin_destination, FileType::Executable);
-            ToolBuildResult {
-                tool_path: bin_destination,
-                build_compiler: tool_result.build_compiler,
-                target_compiler: tool_result.target_compiler,
-            }
-        } else {
-            tool_result
-        }
+    fn metadata(&self) -> Option<StepMetadata> {
+        Some(StepMetadata::build("LlvmBitcodeLinker", self.target).built_by(self.build_compiler))
     }
 }
 
@@ -1146,6 +1140,7 @@ macro_rules! tool_extended {
             stable: $stable:expr
             $( , add_bins_to_sysroot: $add_bins_to_sysroot:expr )?
             $( , add_features: $add_features:expr )?
+            $( , cargo_args: $cargo_args:expr )?
             $( , )?
         }
     ) => {
@@ -1186,6 +1181,7 @@ macro_rules! tool_extended {
                     $path,
                     None $( .or(Some(&$add_bins_to_sysroot)) )?,
                     None $( .or(Some($add_features)) )?,
+                    None $( .or(Some($cargo_args)) )?,
                 )
             }
 
@@ -1225,6 +1221,7 @@ fn should_run_tool_build_step<'a>(
     )
 }
 
+#[expect(clippy::too_many_arguments)] // silence overeager clippy lint
 fn run_tool_build_step(
     builder: &Builder<'_>,
     compiler: Compiler,
@@ -1233,6 +1230,7 @@ fn run_tool_build_step(
     path: &'static str,
     add_bins_to_sysroot: Option<&[&str]>,
     add_features: Option<fn(&Builder<'_>, TargetSelection, &mut Vec<String>)>,
+    cargo_args: Option<&[&'static str]>,
 ) -> ToolBuildResult {
     let mut extra_features = Vec::new();
     if let Some(func) = add_features {
@@ -1249,7 +1247,7 @@ fn run_tool_build_step(
             extra_features,
             source_type: SourceType::InTree,
             allow_features: "",
-            cargo_args: vec![],
+            cargo_args: cargo_args.unwrap_or_default().iter().map(|s| String::from(*s)).collect(),
             artifact_kind: ToolArtifactKind::Binary,
         });
 
@@ -1300,7 +1298,9 @@ tool_extended!(Miri {
     path: "src/tools/miri",
     tool_name: "miri",
     stable: false,
-    add_bins_to_sysroot: ["miri"]
+    add_bins_to_sysroot: ["miri"],
+    // Always compile also tests when building miri. Otherwise feature unification can cause rebuilds between building and testing miri.
+    cargo_args: &["--all-targets"],
 });
 tool_extended!(CargoMiri {
     path: "src/tools/miri/cargo-miri",
diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs
index deb7106f185..a3b471ca56e 100644
--- a/src/bootstrap/src/core/builder/cargo.rs
+++ b/src/bootstrap/src/core/builder/cargo.rs
@@ -113,9 +113,9 @@ impl Cargo {
 
         match cmd_kind {
             // No need to configure the target linker for these command types.
-            Kind::Clean | Kind::Check | Kind::Suggest | Kind::Format | Kind::Setup => {}
+            Kind::Clean | Kind::Check | Kind::Format | Kind::Setup => {}
             _ => {
-                cargo.configure_linker(builder);
+                cargo.configure_linker(builder, mode);
             }
         }
 
@@ -209,7 +209,7 @@ impl Cargo {
 
     // FIXME(onur-ozkan): Add coverage to make sure modifications to this function
     // doesn't cause cache invalidations (e.g., #130108).
-    fn configure_linker(&mut self, builder: &Builder<'_>) -> &mut Cargo {
+    fn configure_linker(&mut self, builder: &Builder<'_>, mode: Mode) -> &mut Cargo {
         let target = self.target;
         let compiler = self.compiler;
 
@@ -264,7 +264,12 @@ impl Cargo {
             }
         }
 
-        for arg in linker_args(builder, compiler.host, LldThreads::Yes) {
+        // We use the snapshot compiler when building host code (build scripts/proc macros) of
+        // `Mode::Std` tools, so we need to determine the current stage here to pass the proper
+        // linker args (e.g. -C vs -Z).
+        // This should stay synchronized with the [cargo] function.
+        let host_stage = if mode == Mode::Std { 0 } else { compiler.stage };
+        for arg in linker_args(builder, compiler.host, LldThreads::Yes, host_stage) {
             self.hostflags.arg(&arg);
         }
 
@@ -274,10 +279,10 @@ impl Cargo {
         }
         // We want to set -Clinker using Cargo, therefore we only call `linker_flags` and not
         // `linker_args` here.
-        for flag in linker_flags(builder, target, LldThreads::Yes) {
+        for flag in linker_flags(builder, target, LldThreads::Yes, compiler.stage) {
             self.rustflags.arg(&flag);
         }
-        for arg in linker_args(builder, target, LldThreads::Yes) {
+        for arg in linker_args(builder, target, LldThreads::Yes, compiler.stage) {
             self.rustdocflags.arg(&arg);
         }
 
@@ -742,6 +747,12 @@ impl Builder<'_> {
         // Make cargo emit diagnostics relative to the rustc src dir.
         cargo.arg(format!("-Zroot-dir={}", self.src.display()));
 
+        if self.config.compile_time_deps {
+            // Build only build scripts and proc-macros for rust-analyzer when requested.
+            cargo.arg("-Zunstable-options");
+            cargo.arg("--compile-time-deps");
+        }
+
         // FIXME: Temporary fix for https://github.com/rust-lang/cargo/issues/3005
         // Force cargo to output binaries with disambiguating hashes in the name
         let mut metadata = if compiler.stage == 0 {
diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs
index b96a988cde3..1b75d00b30e 100644
--- a/src/bootstrap/src/core/builder/mod.rs
+++ b/src/bootstrap/src/core/builder/mod.rs
@@ -146,6 +146,8 @@ pub struct StepMetadata {
     target: TargetSelection,
     built_by: Option<Compiler>,
     stage: Option<u32>,
+    /// Additional opaque string printed in the metadata
+    metadata: Option<String>,
 }
 
 impl StepMetadata {
@@ -170,7 +172,7 @@ impl StepMetadata {
     }
 
     fn new(name: &'static str, target: TargetSelection, kind: Kind) -> Self {
-        Self { name, kind, target, built_by: None, stage: None }
+        Self { name, kind, target, built_by: None, stage: None, metadata: None }
     }
 
     pub fn built_by(mut self, compiler: Compiler) -> Self {
@@ -183,6 +185,11 @@ impl StepMetadata {
         self
     }
 
+    pub fn with_metadata(mut self, metadata: String) -> Self {
+        self.metadata = Some(metadata);
+        self
+    }
+
     pub fn get_stage(&self) -> Option<u32> {
         self.stage.or(self
             .built_by
@@ -838,7 +845,6 @@ pub enum Kind {
     #[value(alias = "r")]
     Run,
     Setup,
-    Suggest,
     Vendor,
     Perf,
 }
@@ -862,7 +868,6 @@ impl Kind {
             Kind::Install => "install",
             Kind::Run => "run",
             Kind::Setup => "setup",
-            Kind::Suggest => "suggest",
             Kind::Vendor => "vendor",
             Kind::Perf => "perf",
         }
@@ -874,7 +879,6 @@ impl Kind {
             Kind::Bench => "Benchmarking",
             Kind::Doc => "Documenting",
             Kind::Run => "Running",
-            Kind::Suggest => "Suggesting",
             Kind::Clippy => "Linting",
             Kind::Perf => "Profiling & benchmarking",
             _ => {
@@ -1037,6 +1041,7 @@ impl<'a> Builder<'a> {
             Kind::Test => describe!(
                 crate::core::build_steps::toolstate::ToolStateCheck,
                 test::Tidy,
+                test::Bootstrap,
                 test::Ui,
                 test::Crashes,
                 test::Coverage,
@@ -1091,8 +1096,6 @@ impl<'a> Builder<'a> {
                 test::RustInstaller,
                 test::TestFloatParse,
                 test::CollectLicenseMetadata,
-                // Run bootstrap close to the end as it's unlikely to fail
-                test::Bootstrap,
                 // Run run-make last, since these won't pass without make on Windows
                 test::RunMake,
             ),
@@ -1195,7 +1198,7 @@ impl<'a> Builder<'a> {
             Kind::Clean => describe!(clean::CleanAll, clean::Rustc, clean::Std),
             Kind::Vendor => describe!(vendor::Vendor),
             // special-cased in Build::build()
-            Kind::Format | Kind::Suggest | Kind::Perf => vec![],
+            Kind::Format | Kind::Perf => vec![],
             Kind::MiriTest | Kind::MiriSetup => unreachable!(),
         }
     }
@@ -1263,7 +1266,6 @@ impl<'a> Builder<'a> {
             Subcommand::Run { .. } => (Kind::Run, &paths[..]),
             Subcommand::Clean { .. } => (Kind::Clean, &paths[..]),
             Subcommand::Format { .. } => (Kind::Format, &[][..]),
-            Subcommand::Suggest { .. } => (Kind::Suggest, &[][..]),
             Subcommand::Setup { profile: ref path } => (
                 Kind::Setup,
                 path.as_ref().map_or([].as_slice(), |path| std::slice::from_ref(path)),
@@ -1599,7 +1601,7 @@ You have to build a stage1 compiler for `{}` first, and then use it to build a s
             cmd.arg("-Dwarnings");
         }
         cmd.arg("-Znormalize-docs");
-        cmd.args(linker_args(self, compiler.host, LldThreads::Yes));
+        cmd.args(linker_args(self, compiler.host, LldThreads::Yes, compiler.stage));
         cmd
     }
 
diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs
index 1c5267cb75e..51a90649692 100644
--- a/src/bootstrap/src/core/builder/tests.rs
+++ b/src/bootstrap/src/core/builder/tests.rs
@@ -637,8 +637,8 @@ mod snapshot {
 
     use crate::core::build_steps::{compile, dist, doc, test, tool};
     use crate::core::builder::tests::{
-        TEST_TRIPLE_1, TEST_TRIPLE_2, TEST_TRIPLE_3, configure, configure_with_args, first,
-        host_target, render_steps, run_build,
+        RenderConfig, TEST_TRIPLE_1, TEST_TRIPLE_2, TEST_TRIPLE_3, configure, configure_with_args,
+        first, host_target, render_steps, run_build,
     };
     use crate::core::builder::{Builder, Kind, StepDescription, StepMetadata};
     use crate::core::config::TargetSelection;
@@ -758,6 +758,58 @@ mod snapshot {
     }
 
     #[test]
+    fn build_compiler_tools() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx
+                .config("build")
+                .stage(2)
+                .args(&["--set", "rust.lld=true", "--set", "rust.llvm-bitcode-linker=true"])
+                .render_steps(), @r"
+        [build] llvm <host>
+        [build] rustc 0 <host> -> rustc 1 <host>
+        [build] rustc 0 <host> -> LldWrapper 1 <host>
+        [build] rustc 1 <host> -> LlvmBitcodeLinker 2 <host>
+        [build] rustc 1 <host> -> std 1 <host>
+        [build] rustc 1 <host> -> rustc 2 <host>
+        [build] rustc 1 <host> -> LldWrapper 2 <host>
+        [build] rustc 2 <host> -> LlvmBitcodeLinker 3 <host>
+        [build] rustc 2 <host> -> std 2 <host>
+        [build] rustdoc 1 <host>
+        "
+        );
+    }
+
+    #[test]
+    fn build_compiler_tools_cross() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx
+                .config("build")
+                .stage(2)
+                .args(&["--set", "rust.lld=true", "--set", "rust.llvm-bitcode-linker=true"])
+                .hosts(&[TEST_TRIPLE_1])
+                .render_steps(), @r"
+        [build] llvm <host>
+        [build] rustc 0 <host> -> rustc 1 <host>
+        [build] rustc 0 <host> -> LldWrapper 1 <host>
+        [build] rustc 1 <host> -> LlvmBitcodeLinker 2 <host>
+        [build] rustc 1 <host> -> std 1 <host>
+        [build] rustc 1 <host> -> rustc 2 <host>
+        [build] rustc 1 <host> -> LldWrapper 2 <host>
+        [build] rustc 2 <host> -> LlvmBitcodeLinker 3 <host>
+        [build] rustc 1 <host> -> std 1 <target1>
+        [build] rustc 2 <host> -> std 2 <target1>
+        [build] llvm <target1>
+        [build] rustc 1 <host> -> rustc 2 <target1>
+        [build] rustc 1 <host> -> LldWrapper 2 <target1>
+        [build] rustc 2 <target1> -> LlvmBitcodeLinker 3 <target1>
+        [build] rustdoc 1 <target1>
+        "
+        );
+    }
+
+    #[test]
     fn build_library_no_explicit_stage() {
         let ctx = TestCtx::new();
         insta::assert_snapshot!(
@@ -942,7 +994,7 @@ mod snapshot {
         [build] llvm <host>
         [build] rustc 0 <host> -> rustc 1 <host>
         [build] rustdoc 0 <host>
-        [doc] std 1 <host>
+        [doc] std 1 <host> crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,std,sysroot,test,unwind]
         ");
     }
 
@@ -991,12 +1043,12 @@ mod snapshot {
         [build] rustc 1 <host> -> std 1 <host>
         [build] rustc 1 <host> -> rustc 2 <host>
         [build] rustdoc 1 <host>
-        [doc] std 2 <host>
+        [doc] std 2 <host> crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,std,sysroot,test,unwind]
         [build] rustc 2 <host> -> std 2 <host>
         [build] rustc 0 <host> -> LintDocs 1 <host>
         [build] rustc 0 <host> -> RustInstaller 1 <host>
         [dist] docs <host>
-        [doc] std 2 <host>
+        [doc] std 2 <host> crates=[]
         [dist] mingw <host>
         [build] rustc 0 <host> -> GenerateCopyright 1 <host>
         [dist] rustc <host>
@@ -1023,12 +1075,12 @@ mod snapshot {
         [build] rustc 1 <host> -> rustc 2 <host>
         [build] rustc 1 <host> -> WasmComponentLd 2 <host>
         [build] rustdoc 1 <host>
-        [doc] std 2 <host>
+        [doc] std 2 <host> crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,std,sysroot,test,unwind]
         [build] rustc 2 <host> -> std 2 <host>
         [build] rustc 0 <host> -> LintDocs 1 <host>
         [build] rustc 0 <host> -> RustInstaller 1 <host>
         [dist] docs <host>
-        [doc] std 2 <host>
+        [doc] std 2 <host> crates=[]
         [dist] mingw <host>
         [build] rustc 0 <host> -> GenerateCopyright 1 <host>
         [dist] rustc <host>
@@ -1040,6 +1092,7 @@ mod snapshot {
         [build] rustc 0 <host> -> cargo-clippy 1 <host>
         [build] rustc 0 <host> -> miri 1 <host>
         [build] rustc 0 <host> -> cargo-miri 1 <host>
+        [build] rustc 1 <host> -> LlvmBitcodeLinker 2 <host>
         ");
     }
 
@@ -1059,15 +1112,15 @@ mod snapshot {
         [build] rustc 1 <host> -> std 1 <host>
         [build] rustc 1 <host> -> rustc 2 <host>
         [build] rustdoc 1 <host>
-        [doc] std 2 <host>
-        [doc] std 2 <target1>
+        [doc] std 2 <host> crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,std,sysroot,test,unwind]
+        [doc] std 2 <target1> crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,std,sysroot,test,unwind]
         [build] rustc 2 <host> -> std 2 <host>
         [build] rustc 0 <host> -> LintDocs 1 <host>
         [build] rustc 0 <host> -> RustInstaller 1 <host>
         [dist] docs <host>
         [dist] docs <target1>
-        [doc] std 2 <host>
-        [doc] std 2 <target1>
+        [doc] std 2 <host> crates=[]
+        [doc] std 2 <target1> crates=[]
         [dist] mingw <host>
         [dist] mingw <target1>
         [build] rustc 0 <host> -> GenerateCopyright 1 <host>
@@ -1096,14 +1149,14 @@ mod snapshot {
         [build] rustc 1 <host> -> std 1 <host>
         [build] rustc 1 <host> -> rustc 2 <host>
         [build] rustdoc 1 <host>
-        [doc] std 2 <host>
+        [doc] std 2 <host> crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,std,sysroot,test,unwind]
         [build] rustc 2 <host> -> std 2 <host>
         [build] rustc 0 <host> -> LintDocs 1 <host>
         [build] rustc 1 <host> -> std 1 <target1>
         [build] rustc 2 <host> -> std 2 <target1>
         [build] rustc 0 <host> -> RustInstaller 1 <host>
         [dist] docs <host>
-        [doc] std 2 <host>
+        [doc] std 2 <host> crates=[]
         [dist] mingw <host>
         [build] rustc 0 <host> -> GenerateCopyright 1 <host>
         [dist] rustc <host>
@@ -1133,8 +1186,8 @@ mod snapshot {
         [build] rustc 1 <host> -> std 1 <host>
         [build] rustc 1 <host> -> rustc 2 <host>
         [build] rustdoc 1 <host>
-        [doc] std 2 <host>
-        [doc] std 2 <target1>
+        [doc] std 2 <host> crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,std,sysroot,test,unwind]
+        [doc] std 2 <target1> crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,std,sysroot,test,unwind]
         [build] rustc 2 <host> -> std 2 <host>
         [build] rustc 0 <host> -> LintDocs 1 <host>
         [build] rustc 1 <host> -> std 1 <target1>
@@ -1142,8 +1195,8 @@ mod snapshot {
         [build] rustc 0 <host> -> RustInstaller 1 <host>
         [dist] docs <host>
         [dist] docs <target1>
-        [doc] std 2 <host>
-        [doc] std 2 <target1>
+        [doc] std 2 <host> crates=[]
+        [doc] std 2 <target1> crates=[]
         [dist] mingw <host>
         [dist] mingw <target1>
         [build] rustc 0 <host> -> GenerateCopyright 1 <host>
@@ -1175,11 +1228,11 @@ mod snapshot {
         [build] rustc 1 <host> -> std 1 <host>
         [build] rustc 1 <host> -> rustc 2 <host>
         [build] rustdoc 1 <host>
-        [doc] std 2 <target1>
+        [doc] std 2 <target1> crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,std,sysroot,test,unwind]
         [build] rustc 2 <host> -> std 2 <host>
         [build] rustc 0 <host> -> RustInstaller 1 <host>
         [dist] docs <target1>
-        [doc] std 2 <target1>
+        [doc] std 2 <target1> crates=[]
         [dist] mingw <target1>
         [build] rustc 2 <host> -> std 2 <target1>
         [dist] rustc 2 <host> -> std 2 <target1>
@@ -1207,14 +1260,14 @@ mod snapshot {
         [build] rustc 1 <host> -> rustc 2 <host>
         [build] rustc 1 <host> -> WasmComponentLd 2 <host>
         [build] rustdoc 1 <host>
-        [doc] std 2 <target1>
+        [doc] std 2 <target1> crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,std,sysroot,test,unwind]
         [build] rustc 2 <host> -> std 2 <host>
         [build] rustc 1 <host> -> std 1 <target1>
         [build] rustc 2 <host> -> std 2 <target1>
         [build] rustc 0 <host> -> LintDocs 1 <host>
         [build] rustc 0 <host> -> RustInstaller 1 <host>
         [dist] docs <target1>
-        [doc] std 2 <target1>
+        [doc] std 2 <target1> crates=[]
         [dist] mingw <target1>
         [build] llvm <target1>
         [build] rustc 1 <host> -> rustc 2 <target1>
@@ -1230,6 +1283,7 @@ mod snapshot {
         [build] rustc 0 <host> -> cargo-clippy 1 <target1>
         [build] rustc 0 <host> -> miri 1 <target1>
         [build] rustc 0 <host> -> cargo-miri 1 <target1>
+        [build] rustc 1 <host> -> LlvmBitcodeLinker 2 <target1>
         ");
     }
 
@@ -1240,24 +1294,23 @@ mod snapshot {
             ctx.config("check")
                 .path("compiler")
                 .render_steps(), @r"
-        [check] std <host>
         [build] llvm <host>
-        [check] rustc <host>
-        [check] cranelift <host>
-        [check] gcc <host>
+        [check] rustc 0 <host> -> rustc 1 <host>
+        [check] rustc 0 <host> -> cranelift 1 <host>
+        [check] rustc 0 <host> -> gcc 1 <host>
         ");
 
         insta::assert_snapshot!(
             ctx.config("check")
                 .path("rustc")
                 .render_steps(), @r"
-        [check] std <host>
         [build] llvm <host>
-        [check] rustc <host>
+        [check] rustc 0 <host> -> rustc 1 <host>
         ");
     }
 
     #[test]
+    #[should_panic]
     fn check_compiler_stage_0() {
         let ctx = TestCtx::new();
         ctx.config("check").path("compiler").stage(0).run();
@@ -1272,11 +1325,9 @@ mod snapshot {
                 .stage(1)
                 .render_steps(), @r"
         [build] llvm <host>
-        [build] rustc 0 <host> -> rustc 1 <host>
-        [build] rustc 1 <host> -> std 1 <host>
-        [check] rustc <host>
-        [check] cranelift <host>
-        [check] gcc <host>
+        [check] rustc 0 <host> -> rustc 1 <host>
+        [check] rustc 0 <host> -> cranelift 1 <host>
+        [check] rustc 0 <host> -> gcc 1 <host>
         ");
     }
 
@@ -1291,11 +1342,9 @@ mod snapshot {
         [build] llvm <host>
         [build] rustc 0 <host> -> rustc 1 <host>
         [build] rustc 1 <host> -> std 1 <host>
-        [build] rustc 1 <host> -> rustc 2 <host>
-        [build] rustc 2 <host> -> std 2 <host>
-        [check] rustc <host>
-        [check] cranelift <host>
-        [check] gcc <host>
+        [check] rustc 1 <host> -> rustc 2 <host>
+        [check] rustc 1 <host> -> cranelift 2 <host>
+        [check] rustc 1 <host> -> gcc 2 <host>
         ");
     }
 
@@ -1304,30 +1353,24 @@ mod snapshot {
         let ctx = TestCtx::new();
         insta::assert_snapshot!(
             ctx.config("check")
-                .stage(2)
                 .targets(&[TEST_TRIPLE_1])
                 .hosts(&[TEST_TRIPLE_1])
                 .render_steps(), @r"
         [build] llvm <host>
         [build] rustc 0 <host> -> rustc 1 <host>
         [build] rustc 1 <host> -> std 1 <host>
-        [build] rustc 1 <host> -> rustc 2 <host>
-        [build] rustc 2 <host> -> std 2 <host>
         [build] rustc 1 <host> -> std 1 <target1>
-        [build] rustc 2 <host> -> std 2 <target1>
-        [check] rustc <target1>
-        [check] Rustdoc <target1>
-        [check] cranelift <target1>
-        [check] gcc <target1>
-        [check] Clippy <target1>
-        [check] Miri <target1>
-        [check] CargoMiri <target1>
-        [check] MiroptTestTools <target1>
-        [check] Rustfmt <target1>
-        [check] rust-analyzer <target1>
-        [check] TestFloatParse <target1>
-        [check] FeaturesStatusDump <target1>
-        [check] std <target1>
+        [check] rustc 1 <host> -> rustc 2 <target1>
+        [check] rustc 1 <host> -> Rustdoc 2 <target1>
+        [check] rustc 1 <host> -> cranelift 2 <target1>
+        [check] rustc 1 <host> -> gcc 2 <target1>
+        [check] rustc 1 <host> -> Clippy 2 <target1>
+        [check] rustc 1 <host> -> Miri 2 <target1>
+        [check] rustc 1 <host> -> CargoMiri 2 <target1>
+        [check] rustc 1 <host> -> Rustfmt 2 <target1>
+        [check] rustc 1 <host> -> rust-analyzer 2 <target1>
+        [check] rustc 1 <host> -> TestFloatParse 2 <target1>
+        [check] rustc 1 <host> -> std 1 <target1>
         ");
     }
 
@@ -1340,11 +1383,12 @@ mod snapshot {
                 .render_steps(), @r"
         [build] llvm <host>
         [build] rustc 0 <host> -> rustc 1 <host>
-        [check] std <host>
+        [check] rustc 1 <host> -> std 1 <host>
         ");
     }
 
     #[test]
+    #[should_panic]
     fn check_library_stage_0() {
         let ctx = TestCtx::new();
         ctx.config("check").path("library").stage(0).run();
@@ -1360,7 +1404,7 @@ mod snapshot {
                 .render_steps(), @r"
         [build] llvm <host>
         [build] rustc 0 <host> -> rustc 1 <host>
-        [check] std <host>
+        [check] rustc 1 <host> -> std 1 <host>
         ");
     }
 
@@ -1376,7 +1420,7 @@ mod snapshot {
         [build] rustc 0 <host> -> rustc 1 <host>
         [build] rustc 1 <host> -> std 1 <host>
         [build] rustc 1 <host> -> rustc 2 <host>
-        [check] std <host>
+        [check] rustc 2 <host> -> std 2 <host>
         ");
     }
 
@@ -1390,8 +1434,32 @@ mod snapshot {
                 .render_steps(), @r"
         [build] llvm <host>
         [build] rustc 0 <host> -> rustc 1 <host>
-        [check] std <target1>
-        [check] std <target2>
+        [check] rustc 1 <host> -> std 1 <target1>
+        [check] rustc 1 <host> -> std 1 <target2>
+        ");
+    }
+
+    /// Make sure that we don't check library when download-rustc is disabled
+    /// when `--skip-std-check-if-no-download-rustc` was passed.
+    #[test]
+    fn check_library_skip_without_download_rustc() {
+        let ctx = TestCtx::new();
+        let args = ["--set", "rust.download-rustc=false", "--skip-std-check-if-no-download-rustc"];
+        insta::assert_snapshot!(
+            ctx.config("check")
+                .paths(&["library"])
+                .args(&args)
+                .render_steps(), @"");
+
+        insta::assert_snapshot!(
+            ctx.config("check")
+                .paths(&["library", "compiler"])
+                .args(&args)
+                .render_steps(), @r"
+        [build] llvm <host>
+        [check] rustc 0 <host> -> rustc 1 <host>
+        [check] rustc 0 <host> -> cranelift 1 <host>
+        [check] rustc 0 <host> -> gcc 1 <host>
         ");
     }
 
@@ -1402,14 +1470,14 @@ mod snapshot {
             ctx.config("check")
                 .path("miri")
                 .render_steps(), @r"
-        [check] std <host>
         [build] llvm <host>
-        [check] rustc <host>
-        [check] Miri <host>
+        [check] rustc 0 <host> -> rustc 1 <host>
+        [check] rustc 0 <host> -> Miri 1 <host>
         ");
     }
 
     #[test]
+    #[should_panic]
     fn check_miri_stage_0() {
         let ctx = TestCtx::new();
         ctx.config("check").path("miri").stage(0).run();
@@ -1424,10 +1492,8 @@ mod snapshot {
                 .stage(1)
                 .render_steps(), @r"
         [build] llvm <host>
-        [build] rustc 0 <host> -> rustc 1 <host>
-        [build] rustc 1 <host> -> std 1 <host>
-        [check] rustc <host>
-        [check] Miri <host>
+        [check] rustc 0 <host> -> rustc 1 <host>
+        [check] rustc 0 <host> -> Miri 1 <host>
         ");
     }
 
@@ -1442,10 +1508,8 @@ mod snapshot {
         [build] llvm <host>
         [build] rustc 0 <host> -> rustc 1 <host>
         [build] rustc 1 <host> -> std 1 <host>
-        [build] rustc 1 <host> -> rustc 2 <host>
-        [build] rustc 2 <host> -> std 2 <host>
-        [check] rustc <host>
-        [check] Miri <host>
+        [check] rustc 1 <host> -> rustc 2 <host>
+        [check] rustc 1 <host> -> Miri 2 <host>
         ");
     }
 
@@ -1466,9 +1530,9 @@ mod snapshot {
                 .path("compiletest")
                 .args(&["--set", "build.compiletest-use-stage0-libtest=false"])
                 .render_steps(), @r"
-        [check] std <host>
         [build] llvm <host>
-        [check] rustc <host>
+        [build] rustc 0 <host> -> rustc 1 <host>
+        [build] rustc 1 <host> -> std 1 <host>
         [check] compiletest <host>
         ");
     }
@@ -1480,11 +1544,10 @@ mod snapshot {
             ctx.config("check")
                 .path("rustc_codegen_cranelift")
                 .render_steps(), @r"
-        [check] std <host>
         [build] llvm <host>
-        [check] rustc <host>
-        [check] cranelift <host>
-        [check] gcc <host>
+        [check] rustc 0 <host> -> rustc 1 <host>
+        [check] rustc 0 <host> -> cranelift 1 <host>
+        [check] rustc 0 <host> -> gcc 1 <host>
         ");
     }
 
@@ -1495,10 +1558,9 @@ mod snapshot {
             ctx.config("check")
                 .path("rust-analyzer")
                 .render_steps(), @r"
-        [check] std <host>
         [build] llvm <host>
-        [check] rustc <host>
-        [check] rust-analyzer <host>
+        [check] rustc 0 <host> -> rustc 1 <host>
+        [check] rustc 0 <host> -> rust-analyzer 1 <host>
         ");
     }
 
@@ -1508,12 +1570,7 @@ mod snapshot {
         insta::assert_snapshot!(
             ctx.config("check")
                 .path("run-make-support")
-                .render_steps(), @r"
-        [check] std <host>
-        [build] llvm <host>
-        [check] rustc <host>
-        [check] RunMakeSupport <host>
-        ");
+                .render_steps(), @"[check] rustc 0 <host> -> RunMakeSupport 1 <host>");
     }
 
     #[test]
@@ -1542,6 +1599,80 @@ mod snapshot {
         steps.assert_contains(StepMetadata::test("CrateLibrustc", host));
         steps.assert_contains_fuzzy(StepMetadata::build("rustc", host));
     }
+
+    #[test]
+    fn doc_library() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("doc")
+                .path("library")
+                .render_steps(), @r"
+        [build] llvm <host>
+        [build] rustc 0 <host> -> rustc 1 <host>
+        [build] rustdoc 0 <host>
+        [doc] std 1 <host> crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,std,sysroot,test,unwind]
+        ");
+    }
+
+    #[test]
+    fn doc_core() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("doc")
+                .path("core")
+                .render_steps(), @r"
+        [build] llvm <host>
+        [build] rustc 0 <host> -> rustc 1 <host>
+        [build] rustdoc 0 <host>
+        [doc] std 1 <host> crates=[core]
+        ");
+    }
+
+    #[test]
+    fn doc_core_no_std_target() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("doc")
+                .path("core")
+                .override_target_no_std(&host_target())
+                .render_steps(), @r"
+        [build] llvm <host>
+        [build] rustc 0 <host> -> rustc 1 <host>
+        [build] rustdoc 0 <host>
+        [doc] std 1 <host> crates=[core]
+        ");
+    }
+
+    #[test]
+    fn doc_library_no_std_target() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("doc")
+                .path("library")
+                .override_target_no_std(&host_target())
+                .render_steps(), @r"
+        [build] llvm <host>
+        [build] rustc 0 <host> -> rustc 1 <host>
+        [build] rustdoc 0 <host>
+        [doc] std 1 <host> crates=[alloc,core]
+        ");
+    }
+
+    #[test]
+    fn doc_library_no_std_target_cross_compile() {
+        let ctx = TestCtx::new();
+        insta::assert_snapshot!(
+            ctx.config("doc")
+                .path("library")
+                .targets(&[TEST_TRIPLE_1])
+                .override_target_no_std(TEST_TRIPLE_1)
+                .render_steps(), @r"
+        [build] llvm <host>
+        [build] rustc 0 <host> -> rustc 1 <host>
+        [build] rustdoc 0 <host>
+        [doc] std 1 <target1> crates=[alloc,core]
+        ");
+    }
 }
 
 struct ExecutedSteps {
@@ -1550,7 +1681,11 @@ struct ExecutedSteps {
 
 impl ExecutedSteps {
     fn render(&self) -> String {
-        render_steps(&self.steps)
+        self.render_with(RenderConfig::default())
+    }
+
+    fn render_with(&self, config: RenderConfig) -> String {
+        render_steps(&self.steps, config)
     }
 
     #[track_caller]
@@ -1559,7 +1694,7 @@ impl ExecutedSteps {
         if !self.contains(&metadata) {
             panic!(
                 "Metadata `{}` ({metadata:?}) not found in executed steps:\n{}",
-                render_metadata(&metadata),
+                render_metadata(&metadata, &RenderConfig::default()),
                 self.render()
             );
         }
@@ -1574,7 +1709,7 @@ impl ExecutedSteps {
         if !self.contains_fuzzy(&metadata) {
             panic!(
                 "Metadata `{}` ({metadata:?}) not found in executed steps:\n{}",
-                render_metadata(&metadata),
+                render_metadata(&metadata, &RenderConfig::default()),
                 self.render()
             );
         }
@@ -1586,7 +1721,7 @@ impl ExecutedSteps {
         if self.contains(&metadata) {
             panic!(
                 "Metadata `{}` ({metadata:?}) found in executed steps (it should not be there):\n{}",
-                render_metadata(&metadata),
+                render_metadata(&metadata, &RenderConfig::default()),
                 self.render()
             );
         }
@@ -1608,7 +1743,7 @@ impl ExecutedSteps {
 }
 
 fn fuzzy_metadata_eq(executed: &StepMetadata, to_match: &StepMetadata) -> bool {
-    let StepMetadata { name, kind, target, built_by: _, stage: _ } = executed;
+    let StepMetadata { name, kind, target, built_by: _, stage: _, metadata } = executed;
     *name == to_match.name && *kind == to_match.kind && *target == to_match.target
 }
 
@@ -1639,6 +1774,16 @@ impl ConfigBuilder {
     }
 }
 
+struct RenderConfig {
+    normalize_host: bool,
+}
+
+impl Default for RenderConfig {
+    fn default() -> Self {
+        Self { normalize_host: true }
+    }
+}
+
 /// Renders the executed bootstrap steps for usage in snapshot tests with insta.
 /// Only renders certain important steps.
 /// Each value in `steps` should be a tuple of (Step, step output).
@@ -1646,7 +1791,7 @@ impl ConfigBuilder {
 /// The arrow in the rendered output (`X -> Y`) means `X builds Y`.
 /// This is similar to the output printed by bootstrap to stdout, but here it is
 /// generated purely for the purpose of tests.
-fn render_steps(steps: &[ExecutedStep]) -> String {
+fn render_steps(steps: &[ExecutedStep], config: RenderConfig) -> String {
     steps
         .iter()
         .filter_map(|step| {
@@ -1656,32 +1801,35 @@ fn render_steps(steps: &[ExecutedStep]) -> String {
                 return None;
             };
 
-            Some(render_metadata(&metadata))
+            Some(render_metadata(&metadata, &config))
         })
         .collect::<Vec<_>>()
         .join("\n")
 }
 
-fn render_metadata(metadata: &StepMetadata) -> String {
+fn render_metadata(metadata: &StepMetadata, config: &RenderConfig) -> String {
     let mut record = format!("[{}] ", metadata.kind.as_str());
     if let Some(compiler) = metadata.built_by {
-        write!(record, "{} -> ", render_compiler(compiler));
+        write!(record, "{} -> ", render_compiler(compiler, config));
     }
     let stage = metadata.get_stage().map(|stage| format!("{stage} ")).unwrap_or_default();
-    write!(record, "{} {stage}<{}>", metadata.name, normalize_target(metadata.target));
+    write!(record, "{} {stage}<{}>", metadata.name, normalize_target(metadata.target, config));
+    if let Some(metadata) = &metadata.metadata {
+        write!(record, " {metadata}");
+    }
     record
 }
 
-fn normalize_target(target: TargetSelection) -> String {
-    target
-        .to_string()
-        .replace(&host_target(), "host")
-        .replace(TEST_TRIPLE_1, "target1")
-        .replace(TEST_TRIPLE_2, "target2")
+fn normalize_target(target: TargetSelection, config: &RenderConfig) -> String {
+    let mut target = target.to_string();
+    if config.normalize_host {
+        target = target.replace(&host_target(), "host");
+    }
+    target.replace(TEST_TRIPLE_1, "target1").replace(TEST_TRIPLE_2, "target2")
 }
 
-fn render_compiler(compiler: Compiler) -> String {
-    format!("rustc {} <{}>", compiler.stage, normalize_target(compiler.host))
+fn render_compiler(compiler: Compiler, config: &RenderConfig) -> String {
+    format!("rustc {} <{}>", compiler.stage, normalize_target(compiler.host, config))
 }
 
 fn host_target() -> String {
diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs
index 49b3fec4c35..22a75183404 100644
--- a/src/bootstrap/src/core/config/config.rs
+++ b/src/bootstrap/src/core/config/config.rs
@@ -110,6 +110,7 @@ pub struct Config {
     pub include_default_paths: bool,
     pub rustc_error_format: Option<String>,
     pub json_output: bool,
+    pub compile_time_deps: bool,
     pub test_compare_mode: bool,
     pub color: Color,
     pub patch_binaries_for_nix: Option<bool>,
@@ -421,6 +422,7 @@ impl Config {
             jobs: flags_jobs,
             warnings: flags_warnings,
             json_output: flags_json_output,
+            compile_time_deps: flags_compile_time_deps,
             color: flags_color,
             bypass_bootstrap_lock: flags_bypass_bootstrap_lock,
             rust_profile_generate: flags_rust_profile_generate,
@@ -468,6 +470,7 @@ impl Config {
         config.include_default_paths = flags_include_default_paths;
         config.rustc_error_format = flags_rustc_error_format;
         config.json_output = flags_json_output;
+        config.compile_time_deps = flags_compile_time_deps;
         config.on_fail = flags_on_fail;
         config.cmd = flags_cmd;
         config.incremental = flags_incremental;
@@ -694,7 +697,7 @@ impl Config {
         config.change_id = toml.change_id.inner;
 
         let Build {
-            mut description,
+            description,
             build,
             host,
             target,
@@ -746,7 +749,7 @@ impl Config {
             compiletest_diff_tool,
             compiletest_use_stage0_libtest,
             tidy_extra_checks,
-            mut ccache,
+            ccache,
             exclude,
         } = toml.build.unwrap_or_default();
 
@@ -939,7 +942,7 @@ impl Config {
         config.rust_profile_use = flags_rust_profile_use;
         config.rust_profile_generate = flags_rust_profile_generate;
 
-        config.apply_rust_config(toml.rust, flags_warnings, &mut description);
+        config.apply_rust_config(toml.rust, flags_warnings);
 
         config.reproducible_artifacts = flags_reproducible_artifact;
         config.description = description;
@@ -960,7 +963,7 @@ impl Config {
             config.channel = channel;
         }
 
-        config.apply_llvm_config(toml.llvm, &mut ccache);
+        config.apply_llvm_config(toml.llvm);
 
         config.apply_gcc_config(toml.gcc);
 
@@ -1025,7 +1028,7 @@ impl Config {
             || bench_stage.is_some();
 
         config.stage = match config.cmd {
-            Subcommand::Check { .. } => flags_stage.or(check_stage).unwrap_or(0),
+            Subcommand::Check { .. } => flags_stage.or(check_stage).unwrap_or(1),
             Subcommand::Clippy { .. } | Subcommand::Fix => flags_stage.or(check_stage).unwrap_or(1),
             // `download-rustc` only has a speed-up for stage2 builds. Default to stage2 unless explicitly overridden.
             Subcommand::Doc { .. } => {
@@ -1047,13 +1050,26 @@ impl Config {
             | Subcommand::Run { .. }
             | Subcommand::Setup { .. }
             | Subcommand::Format { .. }
-            | Subcommand::Suggest { .. }
             | Subcommand::Vendor { .. } => flags_stage.unwrap_or(0),
         };
 
         // Now check that the selected stage makes sense, and if not, print a warning and end
-        if let (0, Subcommand::Build) = (config.stage, &config.cmd) {
-            eprintln!("WARNING: cannot build anything on stage 0. Use at least stage 1.");
+        match (config.stage, &config.cmd) {
+            (0, Subcommand::Build) => {
+                eprintln!("WARNING: cannot build anything on stage 0. Use at least stage 1.");
+                exit!(1);
+            }
+            (0, Subcommand::Check { .. }) => {
+                eprintln!("WARNING: cannot check anything on stage 0. Use at least stage 1.");
+                exit!(1);
+            }
+            _ => {}
+        }
+
+        if config.compile_time_deps && !matches!(config.cmd, Subcommand::Check { .. }) {
+            eprintln!(
+                "WARNING: Can't use --compile-time-deps with any subcommand other than check."
+            );
             exit!(1);
         }
 
@@ -1081,7 +1097,6 @@ impl Config {
                 | Subcommand::Run { .. }
                 | Subcommand::Setup { .. }
                 | Subcommand::Format { .. }
-                | Subcommand::Suggest { .. }
                 | Subcommand::Vendor { .. }
                 | Subcommand::Perf { .. } => {}
             }
diff --git a/src/bootstrap/src/core/config/flags.rs b/src/bootstrap/src/core/config/flags.rs
index bfc06f90d4f..1547ca44494 100644
--- a/src/bootstrap/src/core/config/flags.rs
+++ b/src/bootstrap/src/core/config/flags.rs
@@ -132,6 +132,9 @@ pub struct Flags {
     #[arg(global = true, long)]
     /// use message-format=json
     pub json_output: bool,
+    #[arg(global = true, long)]
+    /// only build proc-macros and build scripts (for rust-analyzer)
+    pub compile_time_deps: bool,
 
     #[arg(global = true, long, value_name = "STYLE")]
     #[arg(value_enum, default_value_t = Color::Auto)]
@@ -383,7 +386,10 @@ pub enum Subcommand {
         bless: bool,
         #[arg(long)]
         /// comma-separated list of other files types to check (accepts py, py:lint,
-        /// py:fmt, shell, shell:lint, cpp, cpp:fmt, spellcheck, spellcheck:fix)
+        /// py:fmt, shell, shell:lint, cpp, cpp:fmt, spellcheck)
+        ///
+        /// Any argument can be prefixed with "auto:" to only run if
+        /// relevant files are modified (eg. "auto:py").
         extra_checks: Option<String>,
         #[arg(long)]
         /// rerun tests even if the inputs are unchanged
@@ -475,13 +481,6 @@ Arguments:
         #[arg(value_name = "<PROFILE>|hook|editor|link")]
         profile: Option<PathBuf>,
     },
-    /// Suggest a subset of tests to run, based on modified files
-    #[command(long_about = "\n")]
-    Suggest {
-        /// run suggested tests
-        #[arg(long)]
-        run: bool,
-    },
     /// Vendor dependencies
     Vendor {
         /// Additional `Cargo.toml` to sync and vendor
@@ -512,7 +511,6 @@ impl Subcommand {
             Subcommand::Install => Kind::Install,
             Subcommand::Run { .. } => Kind::Run,
             Subcommand::Setup { .. } => Kind::Setup,
-            Subcommand::Suggest { .. } => Kind::Suggest,
             Subcommand::Vendor { .. } => Kind::Vendor,
             Subcommand::Perf { .. } => Kind::Perf,
         }
diff --git a/src/bootstrap/src/core/config/toml/llvm.rs b/src/bootstrap/src/core/config/toml/llvm.rs
index 4774e202bd8..1f0cecd145c 100644
--- a/src/bootstrap/src/core/config/toml/llvm.rs
+++ b/src/bootstrap/src/core/config/toml/llvm.rs
@@ -17,8 +17,6 @@ define_config! {
         tests: Option<bool> = "tests",
         enzyme: Option<bool> = "enzyme",
         plugins: Option<bool> = "plugins",
-        // FIXME: Remove this field at Q2 2025, it has been replaced by build.ccache
-        ccache: Option<StringOrBool> = "ccache",
         static_libstdcpp: Option<bool> = "static-libstdcpp",
         libzstd: Option<bool> = "libzstd",
         ninja: Option<bool> = "ninja",
@@ -97,7 +95,6 @@ pub fn check_incompatible_options_for_ci_llvm(
         assertions: _,
         tests: _,
         plugins,
-        ccache: _,
         static_libstdcpp: _,
         libzstd,
         ninja: _,
@@ -149,11 +146,7 @@ pub fn check_incompatible_options_for_ci_llvm(
 }
 
 impl Config {
-    pub fn apply_llvm_config(
-        &mut self,
-        toml_llvm: Option<Llvm>,
-        ccache: &mut Option<StringOrBool>,
-    ) {
+    pub fn apply_llvm_config(&mut self, toml_llvm: Option<Llvm>) {
         let mut llvm_tests = None;
         let mut llvm_enzyme = None;
         let mut llvm_offload = None;
@@ -168,7 +161,6 @@ impl Config {
                 tests,
                 enzyme,
                 plugins,
-                ccache: llvm_ccache,
                 static_libstdcpp,
                 libzstd,
                 ninja,
@@ -191,13 +183,7 @@ impl Config {
                 download_ci_llvm,
                 build_config,
             } = llvm;
-            if llvm_ccache.is_some() {
-                eprintln!("Warning: llvm.ccache is deprecated. Use build.ccache instead.");
-            }
 
-            if ccache.is_none() {
-                *ccache = llvm_ccache;
-            }
             set(&mut self.ninja_in_file, ninja);
             llvm_tests = tests;
             llvm_enzyme = enzyme;
diff --git a/src/bootstrap/src/core/config/toml/rust.rs b/src/bootstrap/src/core/config/toml/rust.rs
index ac5eaea3bcb..307aa52294b 100644
--- a/src/bootstrap/src/core/config/toml/rust.rs
+++ b/src/bootstrap/src/core/config/toml/rust.rs
@@ -36,8 +36,6 @@ define_config! {
         incremental: Option<bool> = "incremental",
         default_linker: Option<String> = "default-linker",
         channel: Option<String> = "channel",
-        // FIXME: Remove this field at Q2 2025, it has been replaced by build.description
-        description: Option<String> = "description",
         musl_root: Option<String> = "musl-root",
         rpath: Option<bool> = "rpath",
         strip: Option<bool> = "strip",
@@ -320,7 +318,6 @@ pub fn check_incompatible_options_for_ci_rustc(
         jemalloc,
         rpath,
         channel,
-        description,
         default_linker,
         std_features,
 
@@ -388,7 +385,6 @@ pub fn check_incompatible_options_for_ci_rustc(
     err!(current_rust_config.std_features, std_features, "rust");
 
     warn!(current_rust_config.channel, channel, "rust");
-    warn!(current_rust_config.description, description, "rust");
 
     Ok(())
 }
@@ -415,12 +411,7 @@ pub(crate) fn validate_codegen_backends(backends: Vec<String>, section: &str) ->
 }
 
 impl Config {
-    pub fn apply_rust_config(
-        &mut self,
-        toml_rust: Option<Rust>,
-        warnings: Warnings,
-        description: &mut Option<String>,
-    ) {
+    pub fn apply_rust_config(&mut self, toml_rust: Option<Rust>, warnings: Warnings) {
         let mut debug = None;
         let mut rustc_debug_assertions = None;
         let mut std_debug_assertions = None;
@@ -459,7 +450,6 @@ impl Config {
                 randomize_layout,
                 default_linker,
                 channel: _, // already handled above
-                description: rust_description,
                 musl_root,
                 rpath,
                 verbose_tests,
@@ -552,14 +542,6 @@ impl Config {
             set(&mut self.jemalloc, jemalloc);
             set(&mut self.test_compare_mode, test_compare_mode);
             set(&mut self.backtrace, backtrace);
-            if rust_description.is_some() {
-                eprintln!(
-                    "Warning: rust.description is deprecated. Use build.description instead."
-                );
-            }
-            if description.is_none() {
-                *description = rust_description;
-            }
             set(&mut self.rust_dist_src, dist_src);
             set(&mut self.verbose_tests, verbose_tests);
             // in the case "false" is set explicitly, do not overwrite the command line args
@@ -619,7 +601,6 @@ impl Config {
         // build our internal lld and use it as the default linker, by setting the `rust.lld` config
         // to true by default:
         // - on the `x86_64-unknown-linux-gnu` target
-        // - on the `dev` and `nightly` channels
         // - when building our in-tree llvm (i.e. the target has not set an `llvm-config`), so that
         //   we're also able to build the corresponding lld
         // - or when using an external llvm that's downloaded from CI, which also contains our prebuilt
@@ -628,9 +609,7 @@ impl Config {
         //   thus, disabled
         // - similarly, lld will not be built nor used by default when explicitly asked not to, e.g.
         //   when the config sets `rust.lld = false`
-        if self.host_target.triple == "x86_64-unknown-linux-gnu"
-            && self.hosts == [self.host_target]
-            && (self.channel == "dev" || self.channel == "nightly")
+        if self.host_target.triple == "x86_64-unknown-linux-gnu" && self.hosts == [self.host_target]
         {
             let no_llvm_config = self
                 .target_config
diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs
index f2119e84cce..b39d464493e 100644
--- a/src/bootstrap/src/core/sanity.rs
+++ b/src/bootstrap/src/core/sanity.rs
@@ -216,7 +216,6 @@ than building it.
             build.config.cmd,
             Subcommand::Clean { .. }
                 | Subcommand::Check { .. }
-                | Subcommand::Suggest { .. }
                 | Subcommand::Format { .. }
                 | Subcommand::Setup { .. }
         );
diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs
index ef5c28272b8..44be51815c7 100644
--- a/src/bootstrap/src/lib.rs
+++ b/src/bootstrap/src/lib.rs
@@ -22,7 +22,7 @@ use std::collections::{BTreeSet, HashMap, HashSet};
 use std::fmt::Display;
 use std::path::{Path, PathBuf};
 use std::sync::OnceLock;
-use std::time::SystemTime;
+use std::time::{Instant, SystemTime};
 use std::{env, fs, io, str};
 
 use build_helper::ci::gha;
@@ -651,11 +651,9 @@ impl Build {
         // Handle hard-coded subcommands.
         {
             #[cfg(feature = "tracing")]
-            let _hardcoded_span = span!(
-                tracing::Level::DEBUG,
-                "handling hardcoded subcommands (Format, Suggest, Perf)"
-            )
-            .entered();
+            let _hardcoded_span =
+                span!(tracing::Level::DEBUG, "handling hardcoded subcommands (Format, Perf)")
+                    .entered();
 
             match &self.config.cmd {
                 Subcommand::Format { check, all } => {
@@ -666,9 +664,6 @@ impl Build {
                         &self.config.paths,
                     );
                 }
-                Subcommand::Suggest { run } => {
-                    return core::build_steps::suggest::suggest(&builder::Builder::new(self), *run);
-                }
                 Subcommand::Perf(args) => {
                     return core::build_steps::perf::perf(&builder::Builder::new(self), args);
                 }
@@ -775,6 +770,9 @@ impl Build {
         if self.config.rust_randomize_layout && check("rustc_randomized_layouts") {
             features.push("rustc_randomized_layouts");
         }
+        if self.config.compile_time_deps && kind == Kind::Check {
+            features.push("check_only");
+        }
 
         // If debug logging is on, then we want the default for tracing:
         // https://github.com/tokio-rs/tracing/blob/3dd5c03d907afdf2c39444a29931833335171554/tracing/src/level_filters.rs#L26
@@ -1318,7 +1316,7 @@ impl Build {
         if let Some(path) = configured {
             return Some(path.join("lib").join(target.to_string()));
         }
-        let mut env_root = PathBuf::from(std::env::var_os("WASI_SDK_PATH")?);
+        let mut env_root = self.wasi_sdk_path.clone()?;
         env_root.push("share");
         env_root.push("wasi-sysroot");
         env_root.push("lib");
@@ -1928,6 +1926,10 @@ to download LLVM rather than building it.
     pub fn exec_ctx(&self) -> &ExecutionContext {
         &self.config.exec_ctx
     }
+
+    pub fn report_summary(&self, start_time: Instant) {
+        self.config.exec_ctx.profiler().report_summary(start_time);
+    }
 }
 
 impl AsRef<ExecutionContext> for Build {
diff --git a/src/bootstrap/src/utils/cc_detect.rs b/src/bootstrap/src/utils/cc_detect.rs
index dcafeb80f90..d3926df9650 100644
--- a/src/bootstrap/src/utils/cc_detect.rs
+++ b/src/bootstrap/src/utils/cc_detect.rs
@@ -67,7 +67,6 @@ pub fn fill_compilers(build: &mut Build) {
         // We don't need to check cross targets for these commands.
         crate::Subcommand::Clean { .. }
         | crate::Subcommand::Check { .. }
-        | crate::Subcommand::Suggest { .. }
         | crate::Subcommand::Format { .. }
         | crate::Subcommand::Setup { .. } => {
             build.hosts.iter().cloned().chain(iter::once(build.host_target)).collect()
@@ -221,10 +220,15 @@ fn default_compiler(
         }
 
         t if t.contains("-wasi") => {
-            let root = build
-                .wasi_sdk_path
-                .as_ref()
-                .expect("WASI_SDK_PATH mut be configured for a -wasi target");
+            let root = if let Some(path) = build.wasi_sdk_path.as_ref() {
+                path
+            } else {
+                if build.config.is_running_on_ci {
+                    panic!("ERROR: WASI_SDK_PATH must be configured for a -wasi target on CI");
+                }
+                println!("WARNING: WASI_SDK_PATH not set, using default cc/cxx compiler");
+                return None;
+            };
             let compiler = match compiler {
                 Language::C => format!("{t}-clang"),
                 Language::CPlusPlus => format!("{t}-clang++"),
diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs
index f5a958618f9..f802640a42d 100644
--- a/src/bootstrap/src/utils/change_tracker.rs
+++ b/src/bootstrap/src/utils/change_tracker.rs
@@ -451,4 +451,39 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[
         severity: ChangeSeverity::Warning,
         summary: "The `spellcheck:fix` tidy extra check argument has been removed, use `--bless` instead",
     },
+    ChangeInfo {
+        change_id: 143048,
+        severity: ChangeSeverity::Warning,
+        summary: "The default check stage has been changed to 1. It is no longer possible to `x check` with stage 0. All check commands have to be on stage 1+. Bootstrap tools can now also only be checked for the host target.",
+    },
+    ChangeInfo {
+        change_id: 143577,
+        severity: ChangeSeverity::Warning,
+        summary: "`download-rustc` has been temporarily disabled for the library profile due to implementation bugs (see #142505).",
+    },
+    ChangeInfo {
+        change_id: 143398,
+        severity: ChangeSeverity::Info,
+        summary: "The --extra-checks flag now supports prefixing any check with `auto:` to only run it if relevant files are modified",
+    },
+    ChangeInfo {
+        change_id: 143785,
+        severity: ChangeSeverity::Info,
+        summary: "A --compile-time-deps flag has been added to reduce the time it takes rust-analyzer to start",
+    },
+    ChangeInfo {
+        change_id: 143733,
+        severity: ChangeSeverity::Info,
+        summary: "Option `tool.TOOL_NAME.features` now works on any subcommand, not just `build`.",
+    },
+    ChangeInfo {
+        change_id: 143630,
+        severity: ChangeSeverity::Warning,
+        summary: "The current `./x suggest` implementation has been removed due to it being quite broken and a lack of maintenance bandwidth, with no prejudice against re-implementing it in a more maintainable form.",
+    },
+    ChangeInfo {
+        change_id: 143926,
+        severity: ChangeSeverity::Warning,
+        summary: "Removed `rust.description` and `llvm.ccache` as it was deprecated in #137723 and #136941 long time ago.",
+    },
 ];
diff --git a/src/bootstrap/src/utils/exec.rs b/src/bootstrap/src/utils/exec.rs
index 487077835ac..209ff393973 100644
--- a/src/bootstrap/src/utils/exec.rs
+++ b/src/bootstrap/src/utils/exec.rs
@@ -10,13 +10,17 @@
 use std::collections::HashMap;
 use std::ffi::{OsStr, OsString};
 use std::fmt::{Debug, Formatter};
+use std::fs::File;
 use std::hash::Hash;
+use std::io::{BufWriter, Write};
 use std::panic::Location;
 use std::path::Path;
+use std::process;
 use std::process::{
     Child, ChildStderr, ChildStdout, Command, CommandArgs, CommandEnvs, ExitStatus, Output, Stdio,
 };
 use std::sync::{Arc, Mutex};
+use std::time::{Duration, Instant};
 
 use build_helper::ci::CiEnv;
 use build_helper::drop_bomb::DropBomb;
@@ -65,13 +69,159 @@ impl OutputMode {
 }
 
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Default)]
-pub struct CommandCacheKey {
+pub struct CommandFingerprint {
     program: OsString,
     args: Vec<OsString>,
     envs: Vec<(OsString, Option<OsString>)>,
     cwd: Option<PathBuf>,
 }
 
+impl CommandFingerprint {
+    /// Helper method to format both Command and BootstrapCommand as a short execution line,
+    /// without all the other details (e.g. environment variables).
+    pub fn format_short_cmd(&self) -> String {
+        let program = Path::new(&self.program);
+        let mut line = vec![program.file_name().unwrap().to_str().unwrap().to_owned()];
+        line.extend(self.args.iter().map(|arg| arg.to_string_lossy().into_owned()));
+        line.extend(self.cwd.iter().map(|p| p.to_string_lossy().into_owned()));
+        line.join(" ")
+    }
+}
+
+#[derive(Default, Clone)]
+pub struct CommandProfile {
+    pub traces: Vec<ExecutionTrace>,
+}
+
+#[derive(Default)]
+pub struct CommandProfiler {
+    stats: Mutex<HashMap<CommandFingerprint, CommandProfile>>,
+}
+
+impl CommandProfiler {
+    pub fn record_execution(&self, key: CommandFingerprint, start_time: Instant) {
+        let mut stats = self.stats.lock().unwrap();
+        let entry = stats.entry(key).or_default();
+        entry.traces.push(ExecutionTrace::Executed { duration: start_time.elapsed() });
+    }
+
+    pub fn record_cache_hit(&self, key: CommandFingerprint) {
+        let mut stats = self.stats.lock().unwrap();
+        let entry = stats.entry(key).or_default();
+        entry.traces.push(ExecutionTrace::CacheHit);
+    }
+
+    pub fn report_summary(&self, start_time: Instant) {
+        let pid = process::id();
+        let filename = format!("bootstrap-profile-{pid}.txt");
+
+        let file = match File::create(&filename) {
+            Ok(f) => f,
+            Err(e) => {
+                eprintln!("Failed to create profiler output file: {e}");
+                return;
+            }
+        };
+
+        let mut writer = BufWriter::new(file);
+        let stats = self.stats.lock().unwrap();
+
+        let mut entries: Vec<_> = stats
+            .iter()
+            .map(|(key, profile)| {
+                let max_duration = profile
+                    .traces
+                    .iter()
+                    .filter_map(|trace| match trace {
+                        ExecutionTrace::Executed { duration, .. } => Some(*duration),
+                        _ => None,
+                    })
+                    .max();
+
+                (key, profile, max_duration)
+            })
+            .collect();
+
+        entries.sort_by(|a, b| b.2.cmp(&a.2));
+
+        let total_bootstrap_duration = start_time.elapsed();
+
+        let total_fingerprints = entries.len();
+        let mut total_cache_hits = 0;
+        let mut total_execution_duration = Duration::ZERO;
+        let mut total_saved_duration = Duration::ZERO;
+
+        for (key, profile, max_duration) in &entries {
+            writeln!(writer, "Command: {:?}", key.format_short_cmd()).unwrap();
+
+            let mut hits = 0;
+            let mut runs = 0;
+            let mut command_total_duration = Duration::ZERO;
+
+            for trace in &profile.traces {
+                match trace {
+                    ExecutionTrace::CacheHit => {
+                        hits += 1;
+                    }
+                    ExecutionTrace::Executed { duration, .. } => {
+                        runs += 1;
+                        command_total_duration += *duration;
+                    }
+                }
+            }
+
+            total_cache_hits += hits;
+            total_execution_duration += command_total_duration;
+            // This makes sense only in our current setup, where:
+            // - If caching is enabled, we record the timing for the initial execution,
+            //   and all subsequent runs will be cache hits.
+            // - If caching is disabled or unused, there will be no cache hits,
+            //   and we'll record timings for all executions.
+            total_saved_duration += command_total_duration * hits as u32;
+
+            let command_vs_bootstrap = if total_bootstrap_duration > Duration::ZERO {
+                100.0 * command_total_duration.as_secs_f64()
+                    / total_bootstrap_duration.as_secs_f64()
+            } else {
+                0.0
+            };
+
+            let duration_str = match max_duration {
+                Some(d) => format!("{d:.2?}"),
+                None => "-".into(),
+            };
+
+            writeln!(
+                writer,
+                "Summary: {runs} run(s), {hits} hit(s), max_duration={duration_str} total_duration: {command_total_duration:.2?} ({command_vs_bootstrap:.2?}% of total)\n"
+            )
+            .unwrap();
+        }
+
+        let overhead_time = total_bootstrap_duration
+            .checked_sub(total_execution_duration)
+            .unwrap_or(Duration::ZERO);
+
+        writeln!(writer, "\n=== Aggregated Summary ===").unwrap();
+        writeln!(writer, "Total unique commands (fingerprints): {total_fingerprints}").unwrap();
+        writeln!(writer, "Total time spent in command executions: {total_execution_duration:.2?}")
+            .unwrap();
+        writeln!(writer, "Total bootstrap time: {total_bootstrap_duration:.2?}").unwrap();
+        writeln!(writer, "Time spent outside command executions: {overhead_time:.2?}").unwrap();
+        writeln!(writer, "Total cache hits: {total_cache_hits}").unwrap();
+        writeln!(writer, "Estimated time saved due to cache hits: {total_saved_duration:.2?}")
+            .unwrap();
+
+        println!("Command profiler report saved to {filename}");
+    }
+}
+
+#[derive(Clone)]
+pub enum ExecutionTrace {
+    CacheHit,
+    Executed { duration: Duration },
+}
+
 /// Wrapper around `std::process::Command`.
 ///
 /// By default, the command will exit bootstrap if it fails.
@@ -244,12 +394,9 @@ impl<'a> BootstrapCommand {
         }
     }
 
-    pub fn cache_key(&self) -> Option<CommandCacheKey> {
-        if !self.should_cache {
-            return None;
-        }
+    pub fn fingerprint(&self) -> CommandFingerprint {
         let command = &self.command;
-        Some(CommandCacheKey {
+        CommandFingerprint {
             program: command.get_program().into(),
             args: command.get_args().map(OsStr::to_os_string).collect(),
             envs: command
@@ -257,7 +404,7 @@ impl<'a> BootstrapCommand {
                 .map(|(k, v)| (k.to_os_string(), v.map(|val| val.to_os_string())))
                 .collect(),
             cwd: command.get_current_dir().map(Path::to_path_buf),
-        })
+        }
     }
 }
 
@@ -400,30 +547,6 @@ impl Default for CommandOutput {
     }
 }
 
-/// Helper trait to format both Command and BootstrapCommand as a short execution line,
-/// without all the other details (e.g. environment variables).
-#[cfg(feature = "tracing")]
-pub trait FormatShortCmd {
-    fn format_short_cmd(&self) -> String;
-}
-
-#[cfg(feature = "tracing")]
-impl FormatShortCmd for BootstrapCommand {
-    fn format_short_cmd(&self) -> String {
-        self.command.format_short_cmd()
-    }
-}
-
-#[cfg(feature = "tracing")]
-impl FormatShortCmd for Command {
-    fn format_short_cmd(&self) -> String {
-        let program = Path::new(self.get_program());
-        let mut line = vec![program.file_name().unwrap().to_str().unwrap()];
-        line.extend(self.get_args().map(|arg| arg.to_str().unwrap()));
-        line.join(" ")
-    }
-}
-
 #[derive(Clone, Default)]
 pub struct ExecutionContext {
     dry_run: DryRun,
@@ -431,11 +554,12 @@ pub struct ExecutionContext {
     pub fail_fast: bool,
     delayed_failures: Arc<Mutex<Vec<String>>>,
     command_cache: Arc<CommandCache>,
+    profiler: Arc<CommandProfiler>,
 }
 
 #[derive(Default)]
 pub struct CommandCache {
-    cache: Mutex<HashMap<CommandCacheKey, CommandOutput>>,
+    cache: Mutex<HashMap<CommandFingerprint, CommandOutput>>,
 }
 
 enum CommandState<'a> {
@@ -446,7 +570,10 @@ enum CommandState<'a> {
         stdout: OutputMode,
         stderr: OutputMode,
         executed_at: &'a Location<'a>,
-        cache_key: Option<CommandCacheKey>,
+        fingerprint: CommandFingerprint,
+        start_time: Instant,
+        #[cfg(feature = "tracing")]
+        _span_guard: tracing::span::EnteredSpan,
     },
 }
 
@@ -454,6 +581,10 @@ pub struct StreamingCommand {
     child: Child,
     pub stdout: Option<ChildStdout>,
     pub stderr: Option<ChildStderr>,
+    fingerprint: CommandFingerprint,
+    start_time: Instant,
+    #[cfg(feature = "tracing")]
+    _span_guard: tracing::span::EnteredSpan,
 }
 
 #[must_use]
@@ -462,11 +593,11 @@ pub struct DeferredCommand<'a> {
 }
 
 impl CommandCache {
-    pub fn get(&self, key: &CommandCacheKey) -> Option<CommandOutput> {
+    pub fn get(&self, key: &CommandFingerprint) -> Option<CommandOutput> {
         self.cache.lock().unwrap().get(key).cloned()
     }
 
-    pub fn insert(&self, key: CommandCacheKey, output: CommandOutput) {
+    pub fn insert(&self, key: CommandFingerprint, output: CommandOutput) {
         self.cache.lock().unwrap().insert(key, output);
     }
 }
@@ -483,6 +614,10 @@ impl ExecutionContext {
         }
     }
 
+    pub fn profiler(&self) -> &CommandProfiler {
+        &self.profiler
+    }
+
     pub fn get_dry_run(&self) -> &DryRun {
         &self.dry_run
     }
@@ -539,12 +674,15 @@ impl ExecutionContext {
         stdout: OutputMode,
         stderr: OutputMode,
     ) -> DeferredCommand<'a> {
-        let cache_key = command.cache_key();
+        let fingerprint = command.fingerprint();
 
-        if let Some(cached_output) = cache_key.as_ref().and_then(|key| self.command_cache.get(key))
-        {
+        #[cfg(feature = "tracing")]
+        let span_guard = trace_cmd!(command);
+
+        if let Some(cached_output) = self.command_cache.get(&fingerprint) {
             command.mark_as_executed();
             self.verbose(|| println!("Cache hit: {command:?}"));
+            self.profiler.record_cache_hit(fingerprint);
             return DeferredCommand { state: CommandState::Cached(cached_output) };
         }
 
@@ -559,14 +697,14 @@ impl ExecutionContext {
                     stdout,
                     stderr,
                     executed_at,
-                    cache_key,
+                    fingerprint,
+                    start_time: Instant::now(),
+                    #[cfg(feature = "tracing")]
+                    _span_guard: span_guard,
                 },
             };
         }
 
-        #[cfg(feature = "tracing")]
-        let _run_span = trace_cmd!(command);
-
         self.verbose(|| {
             println!("running: {command:?} (created at {created_at}, executed at {executed_at})")
         });
@@ -575,6 +713,8 @@ impl ExecutionContext {
         cmd.stdout(stdout.stdio());
         cmd.stderr(stderr.stdio());
 
+        let start_time = Instant::now();
+
         let child = cmd.spawn();
 
         DeferredCommand {
@@ -584,7 +724,10 @@ impl ExecutionContext {
                 stdout,
                 stderr,
                 executed_at,
-                cache_key,
+                fingerprint,
+                start_time,
+                #[cfg(feature = "tracing")]
+                _span_guard: span_guard,
             },
         }
     }
@@ -638,6 +781,12 @@ impl ExecutionContext {
         if !command.run_in_dry_run && self.dry_run() {
             return None;
         }
+
+        #[cfg(feature = "tracing")]
+        let span_guard = trace_cmd!(command);
+
+        let start_time = Instant::now();
+        let fingerprint = command.fingerprint();
         let cmd = &mut command.command;
         cmd.stdout(stdout.stdio());
         cmd.stderr(stderr.stdio());
@@ -649,7 +798,15 @@ impl ExecutionContext {
 
         let stdout = child.stdout.take();
         let stderr = child.stderr.take();
-        Some(StreamingCommand { child, stdout, stderr })
+        Some(StreamingCommand {
+            child,
+            stdout,
+            stderr,
+            fingerprint,
+            start_time,
+            #[cfg(feature = "tracing")]
+            _span_guard: span_guard,
+        })
     }
 }
 
@@ -660,8 +817,14 @@ impl AsRef<ExecutionContext> for ExecutionContext {
 }
 
 impl StreamingCommand {
-    pub fn wait(mut self) -> Result<ExitStatus, std::io::Error> {
-        self.child.wait()
+    pub fn wait(
+        mut self,
+        exec_ctx: impl AsRef<ExecutionContext>,
+    ) -> Result<ExitStatus, std::io::Error> {
+        let exec_ctx = exec_ctx.as_ref();
+        let output = self.child.wait();
+        exec_ctx.profiler().record_execution(self.fingerprint, self.start_time);
+        output
     }
 }
 
@@ -669,16 +832,31 @@ impl<'a> DeferredCommand<'a> {
     pub fn wait_for_output(self, exec_ctx: impl AsRef<ExecutionContext>) -> CommandOutput {
         match self.state {
             CommandState::Cached(output) => output,
-            CommandState::Deferred { process, command, stdout, stderr, executed_at, cache_key } => {
+            CommandState::Deferred {
+                process,
+                command,
+                stdout,
+                stderr,
+                executed_at,
+                fingerprint,
+                start_time,
+                #[cfg(feature = "tracing")]
+                _span_guard,
+            } => {
                 let exec_ctx = exec_ctx.as_ref();
 
                 let output =
                     Self::finish_process(process, command, stdout, stderr, executed_at, exec_ctx);
 
+                #[cfg(feature = "tracing")]
+                drop(_span_guard);
+
                 if (!exec_ctx.dry_run() || command.run_in_dry_run)
-                    && let (Some(cache_key), Some(_)) = (&cache_key, output.status())
+                    && output.status().is_some()
+                    && command.should_cache
                 {
-                    exec_ctx.command_cache.insert(cache_key.clone(), output.clone());
+                    exec_ctx.command_cache.insert(fingerprint.clone(), output.clone());
+                    exec_ctx.profiler.record_execution(fingerprint.clone(), start_time);
                 }
 
                 output
diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs
index 3c5f612daa7..eb00ed566c2 100644
--- a/src/bootstrap/src/utils/helpers.rs
+++ b/src/bootstrap/src/utils/helpers.rs
@@ -404,8 +404,9 @@ pub fn linker_args(
     builder: &Builder<'_>,
     target: TargetSelection,
     lld_threads: LldThreads,
+    stage: u32,
 ) -> Vec<String> {
-    let mut args = linker_flags(builder, target, lld_threads);
+    let mut args = linker_flags(builder, target, lld_threads, stage);
 
     if let Some(linker) = builder.linker(target) {
         args.push(format!("-Clinker={}", linker.display()));
@@ -420,19 +421,30 @@ pub fn linker_flags(
     builder: &Builder<'_>,
     target: TargetSelection,
     lld_threads: LldThreads,
+    stage: u32,
 ) -> Vec<String> {
     let mut args = vec![];
     if !builder.is_lld_direct_linker(target) && builder.config.lld_mode.is_used() {
         match builder.config.lld_mode {
             LldMode::External => {
-                args.push("-Zlinker-features=+lld".to_string());
-                // FIXME(kobzol): remove this flag once MCP510 gets stabilized
+                // cfg(bootstrap) - remove the stage 0 check after updating the bootstrap compiler:
+                // `-Clinker-features` has been stabilized.
+                if stage == 0 {
+                    args.push("-Zlinker-features=+lld".to_string());
+                } else {
+                    args.push("-Clinker-features=+lld".to_string());
+                }
                 args.push("-Zunstable-options".to_string());
             }
             LldMode::SelfContained => {
-                args.push("-Zlinker-features=+lld".to_string());
+                // cfg(bootstrap) - remove the stage 0 check after updating the bootstrap compiler:
+                // `-Clinker-features` has been stabilized.
+                if stage == 0 {
+                    args.push("-Zlinker-features=+lld".to_string());
+                } else {
+                    args.push("-Clinker-features=+lld".to_string());
+                }
                 args.push("-Clink-self-contained=+linker".to_string());
-                // FIXME(kobzol): remove this flag once MCP510 gets stabilized
                 args.push("-Zunstable-options".to_string());
             }
             LldMode::Unused => unreachable!(),
@@ -453,8 +465,9 @@ pub fn add_rustdoc_cargo_linker_args(
     builder: &Builder<'_>,
     target: TargetSelection,
     lld_threads: LldThreads,
+    stage: u32,
 ) {
-    let args = linker_args(builder, target, lld_threads);
+    let args = linker_args(builder, target, lld_threads, stage);
     let mut flags = cmd
         .get_envs()
         .find_map(|(k, v)| if k == OsStr::new("RUSTDOCFLAGS") { v } else { None })
diff --git a/src/bootstrap/src/utils/metrics.rs b/src/bootstrap/src/utils/metrics.rs
index 862c4449624..9b1ccc32cb6 100644
--- a/src/bootstrap/src/utils/metrics.rs
+++ b/src/bootstrap/src/utils/metrics.rs
@@ -43,7 +43,7 @@ pub(crate) struct BuildMetrics {
     state: RefCell<MetricsState>,
 }
 
-/// NOTE: this isn't really cloning anything, but `x suggest` doesn't need metrics so this is probably ok.
+// NOTE: this isn't really cloning anything, but necessary for `Build: Clone`.
 impl Clone for BuildMetrics {
     fn clone(&self) -> Self {
         Self::init()
diff --git a/src/bootstrap/src/utils/render_tests.rs b/src/bootstrap/src/utils/render_tests.rs
index 934699d736b..40006aca5c5 100644
--- a/src/bootstrap/src/utils/render_tests.rs
+++ b/src/bootstrap/src/utils/render_tests.rs
@@ -63,7 +63,7 @@ fn run_tests(builder: &Builder<'_>, cmd: &mut BootstrapCommand, stream: bool) ->
         renderer.render_all();
     }
 
-    let status = streaming_command.wait().unwrap();
+    let status = streaming_command.wait(&builder.config.exec_ctx).unwrap();
     if !status.success() && builder.is_verbose() {
         println!(
             "\n\ncommand did not execute successfully: {cmd:?}\n\
diff --git a/src/bootstrap/src/utils/tests/mod.rs b/src/bootstrap/src/utils/tests/mod.rs
index 59c169b0f2b..ec87e71e0b6 100644
--- a/src/bootstrap/src/utils/tests/mod.rs
+++ b/src/bootstrap/src/utils/tests/mod.rs
@@ -48,17 +48,30 @@ impl ConfigBuilder {
     }
 
     pub fn path(mut self, path: &str) -> Self {
-        self.args.push(path.to_string());
-        self
+        self.arg(path)
     }
 
     pub fn paths(mut self, paths: &[&str]) -> Self {
-        for path in paths {
-            self = self.path(path);
+        self.args(paths)
+    }
+
+    pub fn arg(mut self, arg: &str) -> Self {
+        self.args.push(arg.to_string());
+        self
+    }
+
+    pub fn args(mut self, args: &[&str]) -> Self {
+        for arg in args {
+            self = self.arg(arg);
         }
         self
     }
 
+    /// Set the specified target to be treated as a no_std target.
+    pub fn override_target_no_std(mut self, target: &str) -> Self {
+        self.args(&["--set", &format!("target.{target}.no-std=true")])
+    }
+
     pub fn hosts(mut self, targets: &[&str]) -> Self {
         self.args.push("--host".to_string());
         self.args.push(targets.join(","));
@@ -77,13 +90,6 @@ impl ConfigBuilder {
         self
     }
 
-    pub fn args(mut self, args: &[&str]) -> Self {
-        for arg in args {
-            self.args.push(arg.to_string());
-        }
-        self
-    }
-
     pub fn create_config(mut self) -> Config {
         // Run in dry-check, otherwise the test would be too slow
         self.args.push("--dry-run".to_string());
@@ -96,8 +102,6 @@ impl ConfigBuilder {
         // in-tree LLVM from sources.
         self.args.push("--set".to_string());
         self.args.push("llvm.download-ci-llvm=false".to_string());
-        self.args.push("--set".to_string());
-        self.args.push(format!("target.'{}'.llvm-config=false", get_host_target()));
 
         // Do not mess with the local rustc checkout build directory
         self.args.push("--build-dir".to_string());
diff --git a/src/bootstrap/src/utils/tracing.rs b/src/bootstrap/src/utils/tracing.rs
index 99849341dc3..109407bc5f2 100644
--- a/src/bootstrap/src/utils/tracing.rs
+++ b/src/bootstrap/src/utils/tracing.rs
@@ -52,13 +52,11 @@ macro_rules! error {
 macro_rules! trace_cmd {
     ($cmd:expr) => {
         {
-            use $crate::utils::exec::FormatShortCmd;
-
             ::tracing::span!(
                 target: "COMMAND",
                 ::tracing::Level::TRACE,
                 "executing command",
-                cmd = $cmd.format_short_cmd(),
+                cmd = $cmd.fingerprint().format_short_cmd(),
                 full_cmd = ?$cmd
             ).entered()
         }
diff --git a/src/build_helper/src/ci.rs b/src/build_helper/src/ci.rs
index 9d114c70a67..b5e70eb84cc 100644
--- a/src/build_helper/src/ci.rs
+++ b/src/build_helper/src/ci.rs
@@ -9,7 +9,7 @@ pub enum CiEnv {
 impl CiEnv {
     /// Obtains the current CI environment.
     pub fn current() -> CiEnv {
-        if std::env::var("GITHUB_ACTIONS").map_or(false, |e| e == "true") {
+        if std::env::var("GITHUB_ACTIONS").is_ok_and(|e| e == "true") {
             CiEnv::GitHubActions
         } else {
             CiEnv::None
diff --git a/src/build_helper/src/git.rs b/src/build_helper/src/git.rs
index 9d1195aadf8..cbefb836c00 100644
--- a/src/build_helper/src/git.rs
+++ b/src/build_helper/src/git.rs
@@ -13,7 +13,7 @@ pub struct GitConfig<'a> {
 pub fn output_result(cmd: &mut Command) -> Result<String, String> {
     let output = match cmd.stderr(Stdio::inherit()).output() {
         Ok(status) => status,
-        Err(e) => return Err(format!("failed to run command: {:?}: {}", cmd, e)),
+        Err(e) => return Err(format!("failed to run command: {cmd:?}: {e}")),
     };
     if !output.status.success() {
         return Err(format!(
@@ -62,22 +62,22 @@ pub enum PathFreshness {
 /// The function behaves differently in CI and outside CI.
 ///
 /// - Outside CI, we want to find out if `target_paths` were modified in some local commit on
-/// top of the latest upstream commit that is available in local git history.
-/// If not, we try to find the most recent upstream commit (which we assume are commits
-/// made by bors) that modified `target_paths`.
-/// We don't want to simply take the latest master commit to avoid changing the output of
-/// this function frequently after rebasing on the latest master branch even if `target_paths`
-/// were not modified upstream in the meantime. In that case we would be redownloading CI
-/// artifacts unnecessarily.
+///   top of the latest upstream commit that is available in local git history.
+///   If not, we try to find the most recent upstream commit (which we assume are commits
+///   made by bors) that modified `target_paths`.
+///   We don't want to simply take the latest master commit to avoid changing the output of
+///   this function frequently after rebasing on the latest master branch even if `target_paths`
+///   were not modified upstream in the meantime. In that case we would be redownloading CI
+///   artifacts unnecessarily.
 ///
 /// - In CI, we use a shallow clone of depth 2, i.e., we fetch only a single parent commit
-/// (which will be the most recent bors merge commit) and do not have access
-/// to the full git history. Luckily, we only need to distinguish between two situations:
-/// 1) The current PR made modifications to `target_paths`.
-/// In that case, a build is typically necessary.
-/// 2) The current PR did not make modifications to `target_paths`.
-/// In that case we simply take the latest upstream commit, because on CI there is no need to avoid
-/// redownloading.
+///   (which will be the most recent bors merge commit) and do not have access
+///   to the full git history. Luckily, we only need to distinguish between two situations:
+///   1) The current PR made modifications to `target_paths`.
+///      In that case, a build is typically necessary.
+///   2) The current PR did not make modifications to `target_paths`.
+///      In that case we simply take the latest upstream commit, because on CI there is no need to avoid
+///      redownloading.
 pub fn check_path_modifications(
     git_dir: &Path,
     config: &GitConfig<'_>,
@@ -232,7 +232,7 @@ pub fn get_closest_upstream_commit(
         "--author-date-order",
         &format!("--author={}", config.git_merge_commit_email),
         "-n1",
-        &base,
+        base,
     ]);
 
     let output = output_result(&mut git)?.trim().to_owned();
diff --git a/src/build_helper/src/lib.rs b/src/build_helper/src/lib.rs
index 05de8fd2d42..266eedc6245 100644
--- a/src/build_helper/src/lib.rs
+++ b/src/build_helper/src/lib.rs
@@ -5,6 +5,7 @@ pub mod drop_bomb;
 pub mod fs;
 pub mod git;
 pub mod metrics;
+pub mod npm;
 pub mod stage0_parser;
 pub mod targets;
 pub mod util;
diff --git a/src/build_helper/src/metrics.rs b/src/build_helper/src/metrics.rs
index 8b82e62a327..07157e36415 100644
--- a/src/build_helper/src/metrics.rs
+++ b/src/build_helper/src/metrics.rs
@@ -141,7 +141,7 @@ impl BuildStep {
                 } => {
                     let full_name = format!("{parent_name}-{kind}");
                     let children: Vec<_> =
-                        children.into_iter().filter_map(|s| parse(s, &full_name)).collect();
+                        children.iter().filter_map(|s| parse(s, &full_name)).collect();
                     let children_duration = children.iter().map(|c| c.duration).sum::<Duration>();
                     Some(BuildStep {
                         r#type: kind.to_string(),
diff --git a/src/build_helper/src/npm.rs b/src/build_helper/src/npm.rs
new file mode 100644
index 00000000000..dedef40978d
--- /dev/null
+++ b/src/build_helper/src/npm.rs
@@ -0,0 +1,30 @@
+use std::error::Error;
+use std::path::{Path, PathBuf};
+use std::process::Command;
+use std::{fs, io};
+
+/// Install an exact package version, and return the path of `node_modules`.
+pub fn install_one(
+    out_dir: &Path,
+    npm_bin: &Path,
+    pkg_name: &str,
+    pkg_version: &str,
+) -> Result<PathBuf, io::Error> {
+    let nm_path = out_dir.join("node_modules");
+    let _ = fs::create_dir(&nm_path);
+    let mut child = Command::new(npm_bin)
+        .arg("install")
+        .arg("--audit=false")
+        .arg("--fund=false")
+        .arg(format!("{pkg_name}@{pkg_version}"))
+        .current_dir(out_dir)
+        .spawn()?;
+    let exit_status = child.wait()?;
+    if !exit_status.success() {
+        eprintln!("npm install did not exit successfully");
+        return Err(io::Error::other(Box::<dyn Error + Send + Sync>::from(format!(
+            "npm install returned exit code {exit_status}"
+        ))));
+    }
+    Ok(nm_path)
+}
diff --git a/src/build_helper/src/util.rs b/src/build_helper/src/util.rs
index 80dd6813d13..a8355f774e9 100644
--- a/src/build_helper/src/util.rs
+++ b/src/build_helper/src/util.rs
@@ -18,29 +18,28 @@ macro_rules! exit {
 pub fn detail_exit(code: i32, is_test: bool) -> ! {
     // if in test and code is an error code, panic with status code provided
     if is_test {
-        panic!("status code: {}", code);
+        panic!("status code: {code}");
     } else {
-        // otherwise,exit with provided status code
+        // otherwise, exit with provided status code
         std::process::exit(code);
     }
 }
 
 pub fn fail(s: &str) -> ! {
-    eprintln!("\n\n{}\n\n", s);
+    eprintln!("\n\n{s}\n\n");
     detail_exit(1, cfg!(test));
 }
 
 pub fn try_run(cmd: &mut Command, print_cmd_on_fail: bool) -> Result<(), ()> {
     let status = match cmd.status() {
         Ok(status) => status,
-        Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}", cmd, e)),
+        Err(e) => fail(&format!("failed to execute command: {cmd:?}\nerror: {e}")),
     };
     if !status.success() {
         if print_cmd_on_fail {
             println!(
-                "\n\ncommand did not execute successfully: {:?}\n\
-                 expected success, got: {}\n\n",
-                cmd, status
+                "\n\ncommand did not execute successfully: {cmd:?}\n\
+                 expected success, got: {status}\n\n"
             );
         }
         Err(())
@@ -60,7 +59,7 @@ pub fn parse_gitmodules(target_dir: &Path) -> Vec<String> {
     for line in BufReader::new(file).lines().map_while(Result::ok) {
         let line = line.trim();
         if line.starts_with("path") {
-            let actual_path = line.split(' ').last().expect("Couldn't get value of path");
+            let actual_path = line.split(' ').next_back().expect("Couldn't get value of path");
             submodules_paths.push(actual_path.to_owned());
         }
     }
diff --git a/src/ci/docker/host-aarch64/aarch64-gnu-llvm-19/Dockerfile b/src/ci/docker/host-aarch64/aarch64-gnu-llvm-19/Dockerfile
index 2f9d0010573..e73fbe506f7 100644
--- a/src/ci/docker/host-aarch64/aarch64-gnu-llvm-19/Dockerfile
+++ b/src/ci/docker/host-aarch64/aarch64-gnu-llvm-19/Dockerfile
@@ -50,9 +50,7 @@ ENV RUST_CONFIGURE_ARGS \
 
 COPY scripts/shared.sh /scripts/
 
-ARG SCRIPT_ARG
+COPY scripts/stage_2_test_set1.sh /scripts/
+COPY scripts/stage_2_test_set2.sh /scripts/
 
-COPY scripts/stage_2_test_set1.sh /tmp/
-COPY scripts/stage_2_test_set2.sh /tmp/
-
-ENV SCRIPT "/tmp/${SCRIPT_ARG}"
+ENV SCRIPT "Must specify DOCKER_SCRIPT for this image"
diff --git a/src/ci/docker/host-x86_64/dist-powerpc64le-linux-gnu/Dockerfile b/src/ci/docker/host-x86_64/dist-powerpc64le-linux-gnu/Dockerfile
index d2f1b9400ad..e3ba51e8ffc 100644
--- a/src/ci/docker/host-x86_64/dist-powerpc64le-linux-gnu/Dockerfile
+++ b/src/ci/docker/host-x86_64/dist-powerpc64le-linux-gnu/Dockerfile
@@ -15,19 +15,15 @@ COPY scripts/crosstool-ng-build.sh /scripts/
 COPY host-x86_64/dist-powerpc64le-linux-gnu/powerpc64le-unknown-linux-gnu.defconfig /tmp/crosstool.defconfig
 RUN /scripts/crosstool-ng-build.sh
 
-WORKDIR /build
-
-RUN apt-get install -y --no-install-recommends rpm2cpio cpio
-COPY scripts/shared.sh scripts/build-powerpc64le-toolchain.sh /build/
-RUN ./build-powerpc64le-toolchain.sh
-
 COPY scripts/sccache.sh /scripts/
 RUN sh /scripts/sccache.sh
 
+ENV PATH=$PATH:/x-tools/powerpc64le-unknown-linux-gnu/bin
+
 ENV \
-    AR_powerpc64le_unknown_linux_gnu=powerpc64le-linux-gnu-ar \
-    CC_powerpc64le_unknown_linux_gnu=powerpc64le-linux-gnu-gcc \
-    CXX_powerpc64le_unknown_linux_gnu=powerpc64le-linux-gnu-g++
+    AR_powerpc64le_unknown_linux_gnu=powerpc64le-unknown-linux-gnu-ar \
+    CC_powerpc64le_unknown_linux_gnu=powerpc64le-unknown-linux-gnu-gcc \
+    CXX_powerpc64le_unknown_linux_gnu=powerpc64le-unknown-linux-gnu-g++
 
 ENV HOSTS=powerpc64le-unknown-linux-gnu
 
diff --git a/src/ci/docker/host-x86_64/dist-powerpc64le-linux-gnu/powerpc64le-unknown-linux-gnu.defconfig b/src/ci/docker/host-x86_64/dist-powerpc64le-linux-gnu/powerpc64le-unknown-linux-gnu.defconfig
index 363e5850894..5fbf138cdd7 100644
--- a/src/ci/docker/host-x86_64/dist-powerpc64le-linux-gnu/powerpc64le-unknown-linux-gnu.defconfig
+++ b/src/ci/docker/host-x86_64/dist-powerpc64le-linux-gnu/powerpc64le-unknown-linux-gnu.defconfig
@@ -9,6 +9,7 @@ CT_ARCH_64=y
 # CT_DEMULTILIB is not set
 CT_ARCH_ARCH="powerpc64le"
 CT_KERNEL_LINUX=y
-CT_LINUX_V_4_19=y
+CT_LINUX_V_3_10=y
+CT_GLIBC_V_2_17=y
 CT_CC_LANG_CXX=y
 CT_GETTEXT_NEEDED=y
diff --git a/src/ci/docker/host-x86_64/dist-powerpc64le-linux-musl/Dockerfile b/src/ci/docker/host-x86_64/dist-powerpc64le-linux-musl/Dockerfile
index f045b2a5f65..601c8e90585 100644
--- a/src/ci/docker/host-x86_64/dist-powerpc64le-linux-musl/Dockerfile
+++ b/src/ci/docker/host-x86_64/dist-powerpc64le-linux-musl/Dockerfile
@@ -15,12 +15,6 @@ COPY scripts/crosstool-ng-build.sh /scripts/
 COPY host-x86_64/dist-powerpc64le-linux-musl/powerpc64le-unknown-linux-musl.defconfig /tmp/crosstool.defconfig
 RUN /scripts/crosstool-ng-build.sh
 
-WORKDIR /build
-
-RUN apt-get install -y --no-install-recommends rpm2cpio cpio
-COPY scripts/shared.sh scripts/build-powerpc64le-toolchain.sh /build/
-RUN ./build-powerpc64le-toolchain.sh
-
 COPY scripts/sccache.sh /scripts/
 RUN sh /scripts/sccache.sh
 
diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile
index 44f6a8d2a15..01f19eac1d2 100644
--- a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile
+++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile
@@ -96,12 +96,10 @@ ENV RUST_CONFIGURE_ARGS \
       --set rust.lto=thin \
       --set rust.codegen-units=1
 
-ARG SCRIPT_ARG
-
 COPY host-x86_64/dist-x86_64-linux/dist.sh /scripts/
 COPY host-x86_64/dist-x86_64-linux/dist-alt.sh /scripts/
 
-ENV SCRIPT /scripts/${SCRIPT_ARG}
+ENV SCRIPT "Must specify DOCKER_SCRIPT for this image"
 
 ENV CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=clang
 
diff --git a/src/ci/docker/host-x86_64/i686-gnu-nopt/Dockerfile b/src/ci/docker/host-x86_64/i686-gnu-nopt/Dockerfile
index 58e66fd637a..be3df9d4036 100644
--- a/src/ci/docker/host-x86_64/i686-gnu-nopt/Dockerfile
+++ b/src/ci/docker/host-x86_64/i686-gnu-nopt/Dockerfile
@@ -23,7 +23,7 @@ COPY scripts/sccache.sh /scripts/
 RUN sh /scripts/sccache.sh
 
 ENV RUST_CONFIGURE_ARGS --build=i686-unknown-linux-gnu --disable-optimize-tests
-ARG SCRIPT_ARG
 COPY scripts/stage_2_test_set1.sh /scripts/
 COPY scripts/stage_2_test_set2.sh /scripts/
-ENV SCRIPT ${SCRIPT_ARG}
+COPY scripts/i686-gnu-nopt-2.sh /scripts/
+ENV SCRIPT "Must specify DOCKER_SCRIPT for this image"
diff --git a/src/ci/docker/host-x86_64/i686-gnu/Dockerfile b/src/ci/docker/host-x86_64/i686-gnu/Dockerfile
index a715f7182d2..00cd24b89db 100644
--- a/src/ci/docker/host-x86_64/i686-gnu/Dockerfile
+++ b/src/ci/docker/host-x86_64/i686-gnu/Dockerfile
@@ -24,7 +24,6 @@ COPY scripts/sccache.sh /scripts/
 RUN sh /scripts/sccache.sh
 
 ENV RUST_CONFIGURE_ARGS --build=i686-unknown-linux-gnu
-ARG SCRIPT_ARG
 COPY scripts/stage_2_test_set1.sh /scripts/
 COPY scripts/stage_2_test_set2.sh /scripts/
-ENV SCRIPT /scripts/${SCRIPT_ARG}
+ENV SCRIPT "Must specify DOCKER_SCRIPT for this image"
diff --git a/src/ci/docker/host-x86_64/pr-check-1/Dockerfile b/src/ci/docker/host-x86_64/pr-check-1/Dockerfile
index 7a8056b70dc..8bbcc18e2be 100644
--- a/src/ci/docker/host-x86_64/pr-check-1/Dockerfile
+++ b/src/ci/docker/host-x86_64/pr-check-1/Dockerfile
@@ -45,11 +45,11 @@ COPY host-x86_64/pr-check-1/validate-toolstate.sh /scripts/
 # We also skip the x86_64-unknown-linux-gnu target as it is well-tested by other jobs.
 ENV SCRIPT \
   /scripts/check-default-config-profiles.sh && \
-  python3 ../x.py build --stage 1 src/tools/build-manifest && \
+  python3 ../x.py build src/tools/build-manifest && \
   python3 ../x.py test --stage 0 src/tools/compiletest && \
   python3 ../x.py check compiletest --set build.compiletest-use-stage0-libtest=true && \
-  python3 ../x.py check --stage 1 --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu && \
-  python3 ../x.py check --stage 1 --set build.optimized-compiler-builtins=false core alloc std --target=aarch64-unknown-linux-gnu,i686-pc-windows-msvc,i686-unknown-linux-gnu,x86_64-apple-darwin,x86_64-pc-windows-gnu,x86_64-pc-windows-msvc && \
+  python3 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu && \
+  python3 ../x.py check --set build.optimized-compiler-builtins=false core alloc std --target=aarch64-unknown-linux-gnu,i686-pc-windows-msvc,i686-unknown-linux-gnu,x86_64-apple-darwin,x86_64-pc-windows-gnu,x86_64-pc-windows-msvc && \
   /scripts/validate-toolstate.sh && \
   reuse --include-submodules lint && \
   python3 ../x.py test collect-license-metadata && \
diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile
index c09be047c6a..5cba7c564f1 100644
--- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile
+++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-19/Dockerfile
@@ -57,12 +57,10 @@ ENV RUST_CONFIGURE_ARGS \
 
 COPY scripts/shared.sh /scripts/
 
-ARG SCRIPT_ARG
+COPY scripts/x86_64-gnu-llvm.sh /scripts/
+COPY scripts/x86_64-gnu-llvm2.sh /scripts/
+COPY scripts/x86_64-gnu-llvm3.sh /scripts/
+COPY scripts/stage_2_test_set1.sh /scripts/
+COPY scripts/stage_2_test_set2.sh /scripts/
 
-COPY scripts/x86_64-gnu-llvm.sh /tmp/
-COPY scripts/x86_64-gnu-llvm2.sh /tmp/
-COPY scripts/x86_64-gnu-llvm3.sh /tmp/
-COPY scripts/stage_2_test_set1.sh /tmp/
-COPY scripts/stage_2_test_set2.sh /tmp/
-
-ENV SCRIPT "/tmp/${SCRIPT_ARG}"
+ENV SCRIPT "Must specify DOCKER_SCRIPT for this image"
diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-20/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-20/Dockerfile
index 83a3bfb37a5..92c2631000f 100644
--- a/src/ci/docker/host-x86_64/x86_64-gnu-llvm-20/Dockerfile
+++ b/src/ci/docker/host-x86_64/x86_64-gnu-llvm-20/Dockerfile
@@ -57,12 +57,10 @@ ENV RUST_CONFIGURE_ARGS \
 
 COPY scripts/shared.sh /scripts/
 
-ARG SCRIPT_ARG
+COPY scripts/x86_64-gnu-llvm.sh /scripts/
+COPY scripts/x86_64-gnu-llvm2.sh /scripts/
+COPY scripts/x86_64-gnu-llvm3.sh /scripts/
+COPY scripts/stage_2_test_set1.sh /scripts/
+COPY scripts/stage_2_test_set2.sh /scripts/
 
-COPY scripts/x86_64-gnu-llvm.sh /tmp/
-COPY scripts/x86_64-gnu-llvm2.sh /tmp/
-COPY scripts/x86_64-gnu-llvm3.sh /tmp/
-COPY scripts/stage_2_test_set1.sh /tmp/
-COPY scripts/stage_2_test_set2.sh /tmp/
-
-ENV SCRIPT "/tmp/${SCRIPT_ARG}"
+ENV SCRIPT "Must specify DOCKER_SCRIPT for this image"
diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-miri/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-miri/Dockerfile
index b937bc3e678..ad2ee85c7bb 100644
--- a/src/ci/docker/host-x86_64/x86_64-gnu-miri/Dockerfile
+++ b/src/ci/docker/host-x86_64/x86_64-gnu-miri/Dockerfile
@@ -46,12 +46,4 @@ ENV HOST_TARGET x86_64-unknown-linux-gnu
 
 COPY scripts/shared.sh /scripts/
 
-# For now, we need to use `--unsafe-perm=true` to go around an issue when npm tries
-# to create a new folder. For reference:
-# https://github.com/puppeteer/puppeteer/issues/375
-#
-# We also specify the version in case we need to update it to go around cache limitations.
-#
-# The `browser-ui-test.version` file is also used by bootstrap to emit warnings in case
-# the local version of the package is different than the one used by the CI.
 ENV SCRIPT /tmp/check-miri.sh ../x.py
diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile
index e770c58bd9c..95357d22937 100644
--- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile
+++ b/src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile
@@ -76,8 +76,6 @@ COPY scripts/nodejs.sh /scripts/
 RUN sh /scripts/nodejs.sh /node
 ENV PATH="/node/bin:${PATH}"
 
-COPY host-x86_64/x86_64-gnu-tools/browser-ui-test.version /tmp/
-
 ENV RUST_CONFIGURE_ARGS \
   --build=x86_64-unknown-linux-gnu \
   --save-toolstates=/tmp/toolstate/toolstates.json \
@@ -91,15 +89,6 @@ ENV HOST_TARGET x86_64-unknown-linux-gnu
 
 COPY scripts/shared.sh /scripts/
 
-# For now, we need to use `--unsafe-perm=true` to go around an issue when npm tries
-# to create a new folder. For reference:
-# https://github.com/puppeteer/puppeteer/issues/375
-#
-# We also specify the version in case we need to update it to go around cache limitations.
-#
-# The `browser-ui-test.version` file is also used by bootstrap to emit warnings in case
-# the local version of the package is different than the one used by the CI.
 ENV SCRIPT /tmp/checktools.sh ../x.py && \
-  npm install browser-ui-test@$(head -n 1 /tmp/browser-ui-test.version) --unsafe-perm=true && \
   python3 ../x.py check compiletest --set build.compiletest-use-stage0-libtest=true && \
   python3 ../x.py test tests/rustdoc-gui --stage 2 --test-args "'--jobs 1'"
diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version b/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version
deleted file mode 100644
index b9f8e558df4..00000000000
--- a/src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version
+++ /dev/null
@@ -1 +0,0 @@
-0.21.1
\ No newline at end of file
diff --git a/src/ci/docker/run.sh b/src/ci/docker/run.sh
index 4e69fb2f370..044f5a8fff3 100755
--- a/src/ci/docker/run.sh
+++ b/src/ci/docker/run.sh
@@ -114,14 +114,6 @@ if [ -f "$docker_dir/$image/Dockerfile" ]; then
         "$context"
     )
 
-    # If the environment variable DOCKER_SCRIPT is defined,
-    # set the build argument SCRIPT_ARG to DOCKER_SCRIPT.
-    # In this way, we run the script defined in CI,
-    # instead of the one defined in the Dockerfile.
-    if [ -n "${DOCKER_SCRIPT+x}" ]; then
-      build_args+=("--build-arg" "SCRIPT_ARG=${DOCKER_SCRIPT}")
-    fi
-
     GHCR_BUILDKIT_IMAGE="ghcr.io/rust-lang/buildkit:buildx-stable-1"
     # On non-CI jobs, we try to download a pre-built image from the rust-lang-ci
     # ghcr.io registry. If it is not possible, we fall back to building the image
@@ -341,6 +333,10 @@ if [ "$ENABLE_GCC_CODEGEN" = "1" ]; then
   echo "Setting extra environment values for docker: $extra_env"
 fi
 
+if [ -n "${DOCKER_SCRIPT}" ]; then
+  extra_env="$extra_env --env SCRIPT=\"/scripts/${DOCKER_SCRIPT}\""
+fi
+
 docker \
   run \
   --workdir /checkout/obj \
@@ -361,7 +357,7 @@ docker \
   --env TOOLSTATE_REPO \
   --env TOOLSTATE_PUBLISH \
   --env RUST_CI_OVERRIDE_RELEASE_CHANNEL \
-  --env CI_JOB_NAME="${CI_JOB_NAME-$IMAGE}" \
+  --env CI_JOB_NAME="${CI_JOB_NAME-$image}" \
   --env CI_JOB_DOC_URL="${CI_JOB_DOC_URL}" \
   --env BASE_COMMIT="$BASE_COMMIT" \
   --env DIST_TRY_BUILD \
diff --git a/src/ci/docker/scripts/build-powerpc64le-toolchain.sh b/src/ci/docker/scripts/build-powerpc64le-toolchain.sh
deleted file mode 100755
index 56ea28b6ca5..00000000000
--- a/src/ci/docker/scripts/build-powerpc64le-toolchain.sh
+++ /dev/null
@@ -1,74 +0,0 @@
-#!/usr/bin/env bash
-
-set -ex
-
-source shared.sh
-
-BINUTILS=2.32
-GCC=8.3.0
-TARGET=powerpc64le-linux-gnu
-SYSROOT=/usr/local/$TARGET/sysroot
-
-# First, download the CentOS7 glibc.ppc64le and relevant header files.
-# (upstream ppc64le support wasn't added until 2.19, which el7 backported.)
-mkdir -p $SYSROOT
-pushd $SYSROOT
-
-# centos_base=http://vault.centos.org/altarch/7.3.1611/os/ppc64le/Packages/
-# Mirrored from centos_base above
-centos_base=https://ci-mirrors.rust-lang.org/rustc
-glibc_v=2.17-157-2020-11-25.el7
-kernel_v=3.10.0-514-2020-11-25.el7
-for package in glibc{,-devel,-headers}-$glibc_v kernel-headers-$kernel_v; do
-  curl $centos_base/$package.ppc64le.rpm | \
-    rpm2cpio - | cpio -idm
-done
-
-ln -sT lib64 lib
-ln -sT lib64 usr/lib
-
-popd
-
-# Next, download and build binutils.
-mkdir binutils-$TARGET
-pushd binutils-$TARGET
-curl https://ftp.gnu.org/gnu/binutils/binutils-$BINUTILS.tar.xz | tar xJf -
-mkdir binutils-build
-cd binutils-build
-hide_output ../binutils-$BINUTILS/configure --target=$TARGET --with-sysroot=$SYSROOT
-hide_output make -j10
-hide_output make install
-popd
-rm -rf binutils-$TARGET
-
-# Finally, download and build gcc.
-mkdir gcc-$TARGET
-pushd gcc-$TARGET
-curl https://ftp.gnu.org/gnu/gcc/gcc-$GCC/gcc-$GCC.tar.xz | tar xJf -
-cd gcc-$GCC
-hide_output ./contrib/download_prerequisites
-
-mkdir ../gcc-build
-cd ../gcc-build
-hide_output ../gcc-$GCC/configure                            \
-  --enable-languages=c,c++                       \
-  --target=$TARGET                               \
-  --with-cpu=power8                              \
-  --with-sysroot=$SYSROOT                        \
-  --disable-libcilkrts                           \
-  --disable-multilib                             \
-  --disable-nls                                  \
-  --disable-libgomp                              \
-  --disable-libquadmath                          \
-  --disable-libssp                               \
-  --disable-libvtv                               \
-  --disable-libcilkrt                            \
-  --disable-libada                               \
-  --disable-libsanitizer                         \
-  --disable-libquadmath-support                  \
-  --disable-lto
-hide_output hide_output make -j10
-hide_output make install
-
-popd
-rm -rf gcc-$TARGET
diff --git a/src/ci/docker/scripts/i686-gnu-nopt-2.sh b/src/ci/docker/scripts/i686-gnu-nopt-2.sh
new file mode 100755
index 00000000000..4c171739daf
--- /dev/null
+++ b/src/ci/docker/scripts/i686-gnu-nopt-2.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+set -ex
+
+python3 ../x.py test --stage 1 --set rust.optimize=false library/std &&
+/scripts/stage_2_test_set2.sh
diff --git a/src/ci/docker/scripts/x86_64-gnu-llvm2.sh b/src/ci/docker/scripts/x86_64-gnu-llvm2.sh
index fe5382aaa48..0060b9bfd23 100755
--- a/src/ci/docker/scripts/x86_64-gnu-llvm2.sh
+++ b/src/ci/docker/scripts/x86_64-gnu-llvm2.sh
@@ -4,7 +4,7 @@ set -ex
 
 ##### Test stage 2 #####
 
-/tmp/stage_2_test_set1.sh
+/scripts/stage_2_test_set1.sh
 
 # Run the `mir-opt` tests again but this time for a 32-bit target.
 # This enforces that tests using `// EMIT_MIR_FOR_EACH_BIT_WIDTH` have
diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml
index 01993ad28aa..6c5e37b51ef 100644
--- a/src/ci/github-actions/jobs.yml
+++ b/src/ci/github-actions/jobs.yml
@@ -46,6 +46,10 @@ runners:
     os: windows-2025-8core-32gb
     <<: *base-job
 
+  - &job-windows-aarch64
+    os: windows-11-arm
+    <<: *base-job
+
   - &job-aarch64-linux
     # Free some disk space to avoid running out of space during the build.
     free_disk: true
@@ -311,16 +315,14 @@ auto:
   - name: i686-gnu-nopt-1
     env:
       IMAGE: i686-gnu-nopt
-      DOCKER_SCRIPT: /scripts/stage_2_test_set1.sh
+      DOCKER_SCRIPT: stage_2_test_set1.sh
     <<: *job-linux-4c
 
   # Skip tests that run in i686-gnu-nopt-1
   - name: i686-gnu-nopt-2
     env:
       IMAGE: i686-gnu-nopt
-      DOCKER_SCRIPT: >-
-        python3 ../x.py test --stage 1 --set rust.optimize=false library/std &&
-        /scripts/stage_2_test_set2.sh
+      DOCKER_SCRIPT: i686-gnu-nopt-2.sh
     <<: *job-linux-4c
 
   - name: pr-check-1
@@ -561,6 +563,19 @@ auto:
       SCRIPT: make ci-msvc-ps1
     <<: *job-windows
 
+  # aarch64-msvc is split into two jobs to run tests in parallel.
+  - name: aarch64-msvc-1
+    env:
+      RUST_CONFIGURE_ARGS: --build=aarch64-pc-windows-msvc
+      SCRIPT: make ci-msvc-py
+    <<: *job-windows-aarch64
+
+  - name: aarch64-msvc-2
+    env:
+      RUST_CONFIGURE_ARGS: --build=aarch64-pc-windows-msvc
+      SCRIPT: make ci-msvc-ps1
+    <<: *job-windows-aarch64
+
   # x86_64-msvc-ext is split into multiple jobs to run tests in parallel.
   - name: x86_64-msvc-ext1
     env:
@@ -656,14 +671,14 @@ auto:
   - name: dist-aarch64-msvc
     env:
       RUST_CONFIGURE_ARGS: >-
-        --build=x86_64-pc-windows-msvc
+        --build=aarch64-pc-windows-msvc
         --host=aarch64-pc-windows-msvc
         --target=aarch64-pc-windows-msvc,arm64ec-pc-windows-msvc
         --enable-full-tools
         --enable-profiler
       SCRIPT: python x.py dist bootstrap --include-default-paths
       DIST_REQUIRE_ALL_TOOLS: 1
-    <<: *job-windows
+    <<: *job-windows-aarch64
 
   - name: dist-i686-mingw
     env:
diff --git a/src/ci/scripts/install-clang.sh b/src/ci/scripts/install-clang.sh
index a9528e92915..657e1492a6d 100755
--- a/src/ci/scripts/install-clang.sh
+++ b/src/ci/scripts/install-clang.sh
@@ -56,9 +56,19 @@ elif isWindows && ! isKnownToBeMingwBuild; then
 
     mkdir -p citools/clang-rust
     cd citools
-    retry curl -f "${MIRRORS_BASE}/LLVM-${LLVM_VERSION}-win64.exe" \
-        -o "LLVM-${LLVM_VERSION}-win64.exe"
-    7z x -oclang-rust/ "LLVM-${LLVM_VERSION}-win64.exe"
+
+    if [[ "${CI_JOB_NAME}" = *aarch64* ]]; then
+        suffix=woa64
+
+        # On Arm64, the Ring crate requires that Clang be on the PATH.
+        # https://github.com/briansmith/ring/blob/main/BUILDING.md
+        ciCommandAddPath "$(cygpath -m "$(pwd)/clang-rust/bin")"
+    else
+        suffix=win64
+    fi
+    retry curl -f "${MIRRORS_BASE}/LLVM-${LLVM_VERSION}-${suffix}.exe" \
+        -o "LLVM-${LLVM_VERSION}-${suffix}.exe"
+    7z x -oclang-rust/ "LLVM-${LLVM_VERSION}-${suffix}.exe"
     ciCommandSetEnv RUST_CONFIGURE_ARGS \
         "${RUST_CONFIGURE_ARGS} --set llvm.clang-cl=$(pwd)/clang-rust/bin/clang-cl.exe"
 
diff --git a/src/ci/scripts/install-rust.sh b/src/ci/scripts/install-rust.sh
new file mode 100755
index 00000000000..e4aee98c9fb
--- /dev/null
+++ b/src/ci/scripts/install-rust.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+
+# The Arm64 Windows Runner does not have Rust already installed
+# https://github.com/actions/partner-runner-images/issues/77
+
+set -euo pipefail
+IFS=$'\n\t'
+
+source "$(cd "$(dirname "$0")" && pwd)/../shared.sh"
+
+if [[ "${CI_JOB_NAME}" = *aarch64* ]] && isWindows; then
+    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \
+    sh -s -- -y -q --default-host aarch64-pc-windows-msvc
+    ciCommandAddPath "${USERPROFILE}/.cargo/bin"
+fi
diff --git a/src/doc/book b/src/doc/book
-Subproject ef1ce8f87a8b18feb1b6a9cf9a4939a79bde679
+Subproject b2d1a0821e12a676b496d61891b8e3d374a8e83
diff --git a/src/doc/embedded-book b/src/doc/embedded-book
-Subproject 41f688a598a5022b749e23d37f3c524f6a0b28e
+Subproject fe88fbb68391a465680dd91109f0a151a1676f3
diff --git a/src/doc/nomicon b/src/doc/nomicon
-Subproject 8b61acfaea822e9ac926190bc8f15791c33336e
+Subproject 3ff384320598bbe8d8cfe5cb8f18f78a3a3e6b1
diff --git a/src/doc/reference b/src/doc/reference
-Subproject e9fc99f107840813916f62e16b3f6d9556e1f2d
+Subproject 1f45bd41fa6c17b7c048ed6bfe5f168c4311206
diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example
-Subproject 288b4e4948add43f387cad35adc7b1c54ca6fe1
+Subproject e386be5f44af711854207c11fdd61bb576270b0
diff --git a/src/doc/rustc-dev-guide/src/SUMMARY.md b/src/doc/rustc-dev-guide/src/SUMMARY.md
index 7f2f32c62ff..651e2925ad5 100644
--- a/src/doc/rustc-dev-guide/src/SUMMARY.md
+++ b/src/doc/rustc-dev-guide/src/SUMMARY.md
@@ -35,7 +35,6 @@
         - [Cranelift codegen backend](./tests/codegen-backend-tests/cg_clif.md)
         - [GCC codegen backend](./tests/codegen-backend-tests/cg_gcc.md)
     - [Performance testing](./tests/perf.md)
-    - [Suggest tests tool](./tests/suggest-tests.md)
     - [Misc info](./tests/misc.md)
 - [Debugging the compiler](./compiler-debugging.md)
     - [Using the tracing/logging instrumentation](./tracing.md)
diff --git a/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md b/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md
index ed267850401..c9c0d64a604 100644
--- a/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md
+++ b/src/doc/rustc-dev-guide/src/building/bootstrapping/debugging-bootstrap.md
@@ -168,10 +168,17 @@ For `#[instrument]`, it's recommended to:
 
 ### Profiling bootstrap
 
-You can use the `COMMAND` tracing target to trace execution of most commands spawned by bootstrap. If you also use the `BOOTSTRAP_PROFILE=1` environment variable, bootstrap will generate a Chrome JSON trace file, which can be visualized in Chrome's `chrome://tracing` page or on https://ui.perfetto.dev.
+You can set the `BOOTSTRAP_PROFILE=1` environment variable to enable command execution profiling during bootstrap. This generates:
+
+* A Chrome trace file (for visualization in `chrome://tracing` or [Perfetto](https://ui.perfetto.dev)) if tracing is enabled via `BOOTSTRAP_TRACING=COMMAND=trace`
+* A plain-text summary file, `bootstrap-profile-{pid}.txt`, listing all commands sorted by execution time (slowest first), along with cache hits and working directories
+
+Note: the `.txt` report is always generated when `BOOTSTRAP_PROFILE=1` is set — tracing is not required.
+
+Example usage:
 
 ```bash
-$ BOOTSTRAP_TRACING=COMMAND=trace BOOTSTRAP_PROFILE=1 ./x build library
+$ BOOTSTRAP_PROFILE=1 BOOTSTRAP_TRACING=COMMAND=trace ./x build library
 ```
 
 ### rust-analyzer integration?
diff --git a/src/doc/rustc-dev-guide/src/building/quickstart.md b/src/doc/rustc-dev-guide/src/building/quickstart.md
index 9a8ab353e02..97314d80369 100644
--- a/src/doc/rustc-dev-guide/src/building/quickstart.md
+++ b/src/doc/rustc-dev-guide/src/building/quickstart.md
@@ -61,9 +61,6 @@ and check the output.
 Use `--bless` if you've made a change and want to update the `.stderr` files
 with the new output.
 
-> `./x suggest` can also be helpful for suggesting which tests to run after a
-> change.
-
 Congrats, you are now ready to make a change to the compiler! If you have more
 questions, [the full chapter](./how-to-build-and-run.md) might contain the
 answers, and if it doesn't, feel free to ask for help on
diff --git a/src/doc/rustc-dev-guide/src/building/suggested.md b/src/doc/rustc-dev-guide/src/building/suggested.md
index bfb2f4d1084..7f626314f71 100644
--- a/src/doc/rustc-dev-guide/src/building/suggested.md
+++ b/src/doc/rustc-dev-guide/src/building/suggested.md
@@ -270,23 +270,6 @@ run the tests at some later time. You can then use `git bisect` to track down
 is that you are left with a fairly fine-grained set of commits at the end, all
 of which build and pass tests. This often helps reviewing.
 
-## `x suggest`
-
-The `x suggest` subcommand suggests (and runs) a subset of the extensive
-`rust-lang/rust` tests based on files you have changed. This is especially
-useful for new contributors who have not mastered the arcane `x` flags yet and
-more experienced contributors as a shorthand for reducing mental effort. In all
-cases it is useful not to run the full tests (which can take on the order of
-tens of minutes) and just run a subset which are relevant to your changes. For
-example, running `tidy` and `linkchecker` is useful when editing Markdown files,
-whereas UI tests are much less likely to be helpful. While `x suggest` is a
-useful tool, it does not guarantee perfect coverage (just as PR CI isn't a
-substitute for bors). See the [dedicated chapter](../tests/suggest-tests.md) for
-more information and contribution instructions.
-
-Please note that `x suggest` is in a beta state currently and the tests that it
-will suggest are limited.
-
 ## Configuring `rustup` to use nightly
 
 Some parts of the bootstrap process uses pinned, nightly versions of tools like
diff --git a/src/doc/rustc-dev-guide/src/effects.md b/src/doc/rustc-dev-guide/src/effects.md
index c7aa2714668..87b0103a7bc 100644
--- a/src/doc/rustc-dev-guide/src/effects.md
+++ b/src/doc/rustc-dev-guide/src/effects.md
@@ -67,10 +67,8 @@ in [`wfcheck::check_impl`].
 Here's an example:
 
 ```rust
-#[const_trait]
-trait Bar {}
-#[const_trait]
-trait Foo: ~const Bar {}
+const trait Bar {}
+const trait Foo: ~const Bar {}
 // `const_conditions` contains `HostEffect(Self: Bar, maybe)`
 
 impl const Bar for () {}
@@ -85,8 +83,7 @@ predicates of the trait method, and we attempt to prove the predicates of the
 impl method. We do the same for `const_conditions`:
 
 ```rust
-#[const_trait]
-trait Foo {
+const trait Foo {
     fn hi<T: ~const Default>();
 }
 
diff --git a/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-test-suite.md b/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-test-suite.md
index b05318ce9e6..4f44cf1701c 100644
--- a/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-test-suite.md
+++ b/src/doc/rustc-dev-guide/src/rustdoc-internals/rustdoc-test-suite.md
@@ -55,6 +55,9 @@ Similar to shell commands,
 directives can extend across multiple lines if their last char is `\`.
 In this case, the start of the next line should be `//`, with no `@`.
 
+Similar to compiletest directives, besides a space you can also use a colon `:` to separate
+the directive name and the arguments, however a space is preferred for HtmlDocCk directives.
+
 Use the special string `{{channel}}` in XPaths, `PATTERN` arguments and [snapshot files](#snapshot)
 if you'd like to refer to the URL `https://doc.rust-lang.org/CHANNEL` where `CHANNEL` refers to the
 current release channel (e.g, `stable` or `nightly`).
diff --git a/src/doc/rustc-dev-guide/src/tests/suggest-tests.md b/src/doc/rustc-dev-guide/src/tests/suggest-tests.md
deleted file mode 100644
index 663e8a5af3b..00000000000
--- a/src/doc/rustc-dev-guide/src/tests/suggest-tests.md
+++ /dev/null
@@ -1,59 +0,0 @@
-# Suggest tests tool
-
-This chapter is about the internals of and contribution instructions for the
-`suggest-tests` tool. For a high-level overview of the tool, see [this
-section](../building/suggested.md#x-suggest). This tool is currently in a beta
-state and is tracked by [this](https://github.com/rust-lang/rust/issues/109933)
-issue on Github. Currently the number of tests it will suggest are very limited
-in scope, we are looking to expand this (contributions welcome!).
-
-## Internals
-
-The tool is defined in a separate crate
-([`src/tools/suggest-tests`](https://github.com/rust-lang/rust/blob/master/src/tools/suggest-tests))
-which outputs suggestions which are parsed by a shim in bootstrap
-([`src/bootstrap/src/core/build_steps/suggest.rs`](https://github.com/rust-lang/rust/blob/master/src/bootstrap/src/core/build_steps/suggest.rs)).
-The only notable thing the bootstrap shim does is (when invoked with the `--run`
-flag) use bootstrap's internal mechanisms to create a new `Builder` and uses it
-to invoke the suggested commands. The `suggest-tests` crate is where the fun
-happens, two kinds of suggestions are defined: "static" and "dynamic"
-suggestions.
-
-### Static suggestions
-
-Defined
-[here](https://github.com/rust-lang/rust/blob/master/src/tools/suggest-tests/src/static_suggestions.rs).
-Static suggestions are simple: they are just
-[globs](https://crates.io/crates/glob) which map to a `x` command. In
-`suggest-tests`, this is implemented with a simple `macro_rules` macro.
-
-### Dynamic suggestions
-
-Defined
-[here](https://github.com/rust-lang/rust/blob/master/src/tools/suggest-tests/src/dynamic_suggestions.rs).
-These are more complicated than static suggestions and are implemented as
-functions with the following signature: `fn(&Path) -> Vec<Suggestion>`. In other
-words, each suggestion takes a path to a modified file and (after running
-arbitrary Rust code) can return any number of suggestions, or none. Dynamic
-suggestions are useful for situations where fine-grained control over
-suggestions is needed. For example, modifications to the `compiler/xyz/` path
-should trigger the `x test compiler/xyz` suggestion. In the future, dynamic
-suggestions might even read file contents to determine if (what) tests should
-run.
-
-## Adding a suggestion
-
-The following steps should serve as a rough guide to add suggestions to
-`suggest-tests` (very welcome!):
-
-1. Determine the rules for your suggestion. Is it simple and operates only on a
-   single path or does it match globs? Does it need fine-grained control over
-   the resulting command or does "one size fit all"?
-2. Based on the previous step, decide if your suggestion should be implemented
-   as either static or dynamic.
-3. Implement the suggestion. If it is dynamic then a test is highly recommended,
-   to verify that your logic is correct and to give an example of the
-   suggestion. See the
-   [tests.rs](https://github.com/rust-lang/rust/blob/master/src/tools/suggest-tests/src/tests.rs)
-   file.
-4. Open a PR implementing your suggestion. **(TODO: add example PR)**
diff --git a/src/doc/rustc/src/SUMMARY.md b/src/doc/rustc/src/SUMMARY.md
index 9fe4c218121..7c688e32bc0 100644
--- a/src/doc/rustc/src/SUMMARY.md
+++ b/src/doc/rustc/src/SUMMARY.md
@@ -46,6 +46,7 @@
     - [\*-apple-watchos](platform-support/apple-watchos.md)
     - [\*-apple-visionos](platform-support/apple-visionos.md)
     - [aarch64-nintendo-switch-freestanding](platform-support/aarch64-nintendo-switch-freestanding.md)
+    - [aarch64-unknown-linux-musl](platform-support/aarch64-unknown-linux-musl.md)
     - [amdgcn-amd-amdhsa](platform-support/amdgcn-amd-amdhsa.md)
     - [armeb-unknown-linux-gnueabi](platform-support/armeb-unknown-linux-gnueabi.md)
     - [arm-none-eabi](platform-support/arm-none-eabi.md)
@@ -83,6 +84,7 @@
     - [m68k-unknown-linux-gnu](platform-support/m68k-unknown-linux-gnu.md)
     - [m68k-unknown-none-elf](platform-support/m68k-unknown-none-elf.md)
     - [mips64-openwrt-linux-musl](platform-support/mips64-openwrt-linux-musl.md)
+    - [mips64-unknown-linux-muslabi64](platform-support/mips64-unknown-linux-muslabi64.md)
     - [mipsel-sony-psx](platform-support/mipsel-sony-psx.md)
     - [mipsel-unknown-linux-gnu](platform-support/mipsel-unknown-linux-gnu.md)
     - [mips\*-mti-none-elf](platform-support/mips-mti-none-elf.md)
diff --git a/src/doc/rustc/src/codegen-options/index.md b/src/doc/rustc/src/codegen-options/index.md
index bb109adf76f..07eafdf4c4c 100644
--- a/src/doc/rustc/src/codegen-options/index.md
+++ b/src/doc/rustc/src/codegen-options/index.md
@@ -235,15 +235,33 @@ coverage measurement. Its use is not recommended.
 
 ## link-self-contained
 
-On `windows-gnu`, `linux-musl`, and `wasi` targets, this flag controls whether the
-linker will use libraries and objects shipped with Rust instead of those in the system.
-It takes one of the following values:
+This flag controls whether the linker will use libraries and objects shipped with Rust instead of
+those in the system.  It also controls which binary is used for the linker itself. This allows
+overriding cases when detection fails or the user wants to use shipped libraries.
+
+You can enable or disable the usage of any self-contained components using one of the following values:
 
 * no value: rustc will use heuristic to disable self-contained mode if system has necessary tools.
 * `y`, `yes`, `on`, `true`: use only libraries/objects shipped with Rust.
 * `n`, `no`, `off` or `false`: rely on the user or the linker to provide non-Rust libraries/objects.
 
-This allows overriding cases when detection fails or user wants to use shipped libraries.
+It is also possible to enable or disable specific self-contained components in a more granular way.
+You can pass a comma-separated list of self-contained components, individually enabled
+(`+component`) or disabled (`-component`).
+
+Currently, only the `linker` granular option is stabilized, and only on the `x86_64-unknown-linux-gnu` target:
+- `linker`: toggle the usage of self-contained linker binaries (linker, dlltool, and their necessary libraries)
+
+Note that only the `-linker` opt-out is stable on the `x86_64-unknown-linux-gnu` target: `+linker` is
+already the default on this target.
+
+#### Implementation notes
+
+On the `x86_64-unknown-linux-gnu` target, when using the default linker flavor (using `cc` as the
+linker driver) and linker features (to try using `lld`), `rustc` will try to use the self-contained
+linker by passing a `-B /path/to/sysroot/` link argument to the driver to find `rust-lld` in the
+sysroot. For backwards-compatibility, and to limit name and `PATH` collisions, this is done using a
+shim executable (the `lld-wrapper` tool) that forwards execution to the `rust-lld` executable itself.
 
 ## linker
 
@@ -256,6 +274,39 @@ Note that on Unix-like targets (for example, `*-unknown-linux-gnu` or `*-unknown
 the C compiler (for example `cc` or `clang`) is used as the "linker" here, serving as a linker driver.
 It will invoke the actual linker with all the necessary flags to be able to link against the system libraries like libc.
 
+## linker-features
+
+The `-Clinker-features` flag allows enabling or disabling specific features used during linking.
+
+These feature flags are a flexible extension mechanism that is complementary to linker flavors,
+designed to avoid the combinatorial explosion of having to create a new set of flavors for each
+linker feature we'd want to use.
+
+The flag accepts a comma-separated list of features, individually enabled (`+feature`) or disabled
+(`-feature`).
+
+Currently only one is stable, and only on the `x86_64-unknown-linux-gnu` target:
+- `lld`: to toggle trying to use the lld linker, either the system-installed binary, or the self-contained
+  `rust-lld` linker (via the [`-Clink-self-contained=+linker`](#link-self-contained) flag).
+
+For example, use:
+- `-Clinker-features=+lld` to opt into using the `lld` linker, when possible (see the Implementation notes below)
+- `-Clinker-features=-lld` to opt out instead, for targets where it is configured as the default linker
+
+Note that only the `-lld` opt-out is stable on the `x86_64-unknown-linux-gnu` target: `+lld` is
+already the default on this target.
+
+#### Implementation notes
+
+On the `x86_64-unknown-linux-gnu` target, when using the default linker flavor (using `cc` as the
+linker driver), `rustc` will try to use lld by passing a `-fuse-ld=lld` link argument to the driver.
+`rustc` will also try to detect if that _causes_ an error during linking (for example, if GCC is too
+old to understand the flag, and returns an error) and will then retry linking without this argument,
+as a fallback.
+
+If the user _also_ passes a `-Clink-arg=-fuse-ld=$value`, both will be given to the linker
+driver but the user's will be passed last, and would generally have priority over `rustc`'s.
+
 ## linker-flavor
 
 This flag controls the linker flavor used by `rustc`. If a linker is given with
diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md
index 285b1e519b7..65b70630153 100644
--- a/src/doc/rustc/src/platform-support.md
+++ b/src/doc/rustc/src/platform-support.md
@@ -90,7 +90,7 @@ target | notes
 -------|-------
 [`aarch64-pc-windows-gnullvm`](platform-support/windows-gnullvm.md) | ARM64 MinGW (Windows 10+), LLVM ABI
 [`aarch64-pc-windows-msvc`](platform-support/windows-msvc.md) | ARM64 Windows MSVC
-`aarch64-unknown-linux-musl` | ARM64 Linux with musl 1.2.3
+[`aarch64-unknown-linux-musl`](platform-support/aarch64-unknown-linux-musl.md) | ARM64 Linux with musl 1.2.3
 [`aarch64-unknown-linux-ohos`](platform-support/openharmony.md) | ARM64 OpenHarmony
 `arm-unknown-linux-gnueabi` | Armv6 Linux (kernel 3.2+, glibc 2.17)
 `arm-unknown-linux-gnueabihf` | Armv6 Linux, hardfloat (kernel 3.2+, glibc 2.17)
@@ -333,7 +333,7 @@ target | std | host | notes
 `mips-unknown-linux-uclibc` | ✓ |  | MIPS Linux with uClibc
 [`mips64-openwrt-linux-musl`](platform-support/mips64-openwrt-linux-musl.md) | ? |  | MIPS64 for OpenWrt Linux musl 1.2.3
 `mips64-unknown-linux-gnuabi64` | ✓ | ✓ | MIPS64 Linux, N64 ABI (kernel 4.4, glibc 2.23)
-`mips64-unknown-linux-muslabi64` | ✓ |  | MIPS64 Linux, N64 ABI, musl 1.2.3
+[`mips64-unknown-linux-muslabi64`](platform-support/mips64-unknown-linux-muslabi64.md) | ✓ | ✓ | MIPS64 Linux, N64 ABI, musl 1.2.3
 `mips64el-unknown-linux-gnuabi64` | ✓ | ✓ | MIPS64 (little endian) Linux, N64 ABI (kernel 4.4, glibc 2.23)
 `mips64el-unknown-linux-muslabi64` | ✓ |  | MIPS64 (little endian) Linux, N64 ABI, musl 1.2.3
 `mipsel-sony-psp` | * |  | MIPS (LE) Sony PlayStation Portable (PSP)
diff --git a/src/doc/rustc/src/platform-support/aarch64-unknown-linux-musl.md b/src/doc/rustc/src/platform-support/aarch64-unknown-linux-musl.md
new file mode 100644
index 00000000000..5d9a552e460
--- /dev/null
+++ b/src/doc/rustc/src/platform-support/aarch64-unknown-linux-musl.md
@@ -0,0 +1,49 @@
+# aarch64-unknown-linux-musl
+
+**Tier: 2**
+
+Target for 64-bit little endian ARMv8-A Linux programs using musl libc.
+
+## Target maintainers
+
+[@Gelbpunkt](https://github.com/Gelbpunkt)
+[@famfo](https://github.com/famfo)
+
+## Requirements
+
+Building the target itself requires a 64-bit little endian ARMv8-A compiler
+that is supported by `cc-rs`.
+
+## Building the target
+
+The target can be built by enabling it for a `rustc` build.
+
+```toml
+[build]
+target = ["aarch64-unknown-linux-musl"]
+```
+
+Make sure your C compiler is included in `$PATH`, then add it to the
+`bootstrap.toml`:
+
+```toml
+[target.aarch64-unknown-linux-musl]
+cc = "aarch64-linux-musl-gcc"
+cxx = "aarch64-linux-musl-g++"
+ar = "aarch64-linux-musl-ar"
+linker = "aarch64-linux-musl-gcc"
+```
+
+## Building Rust programs
+
+This target is distributed through `rustup`, and otherwise requires no
+special configuration.
+
+## Cross-compilation
+
+This target can be cross-compiled from any host.
+
+## Testing
+
+This target can be tested as normal with `x.py` on a 64-bit little endian
+ARMv8-A host or via QEMU emulation.
diff --git a/src/doc/rustc/src/platform-support/mips64-unknown-linux-muslabi64.md b/src/doc/rustc/src/platform-support/mips64-unknown-linux-muslabi64.md
new file mode 100644
index 00000000000..60c0972bb3e
--- /dev/null
+++ b/src/doc/rustc/src/platform-support/mips64-unknown-linux-muslabi64.md
@@ -0,0 +1,49 @@
+# mips64-unknown-linux-muslabi64
+
+**Tier: 3**
+
+Target for 64-bit big endian MIPS Linux programs using musl libc and the N64 ABI.
+
+## Target maintainers
+
+[@Gelbpunkt](https://github.com/Gelbpunkt)
+
+## Requirements
+
+Building the target itself requires a 64-bit big endian MIPS compiler that is
+supported by `cc-rs`.
+
+## Building the target
+
+The target can be built by enabling it for a `rustc` build.
+
+```toml
+[build]
+target = ["mips64-unknown-linux-muslabi64"]
+```
+
+Make sure your C compiler is included in `$PATH`, then add it to the
+`bootstrap.toml`:
+
+```toml
+[target.mips64-unknown-linux-muslabi64]
+cc = "mips64-linux-musl-gcc"
+cxx = "mips64-linux-musl-g++"
+ar = "mips64-linux-musl-ar"
+linker = "mips64-linux-musl-gcc"
+```
+
+## Building Rust programs
+
+Rust does not yet ship pre-compiled artifacts for this target. To compile for
+this target, you will first need to build Rust with the target enabled (see
+"Building the target" above).
+
+## Cross-compilation
+
+This target can be cross-compiled from any host.
+
+## Testing
+
+This target can be tested as normal with `x.py` on a 64-bit big endian MIPS
+host or via QEMU emulation.
diff --git a/src/doc/rustc/src/platform-support/netbsd.md b/src/doc/rustc/src/platform-support/netbsd.md
index 9040ef637be..b173bf82b44 100644
--- a/src/doc/rustc/src/platform-support/netbsd.md
+++ b/src/doc/rustc/src/platform-support/netbsd.md
@@ -1,7 +1,5 @@
 # \*-unknown-netbsd
 
-**Tier: 3**
-
 [NetBSD] multi-platform 4.4BSD-based UNIX-like operating system.
 
 [NetBSD]: https://www.NetBSD.org/
@@ -11,19 +9,19 @@ where `$ARCH` specifies the target processor architecture and
 `-$SUFFIX` (optional) might indicate the ABI. The following targets
 are currently defined running NetBSD:
 
-|          Target name           | NetBSD Platform |
-|--------------------------------|-----------------|
-| `x86_64-unknown-netbsd`        | [amd64 / x86_64 systems](https://wiki.netbsd.org/ports/amd64/) |
-| `armv7-unknown-netbsd-eabihf`  | [32-bit ARMv7 systems with hard-float](https://wiki.netbsd.org/ports/evbarm/) |
-| `armv6-unknown-netbsd-eabihf`  | [32-bit ARMv6 systems with hard-float](https://wiki.netbsd.org/ports/evbarm/) |
-| `aarch64-unknown-netbsd`       | [64-bit ARM systems, little-endian](https://wiki.netbsd.org/ports/evbarm/) |
-| `aarch64_be-unknown-netbsd`    | [64-bit ARM systems, big-endian](https://wiki.netbsd.org/ports/evbarm/) |
-| `i586-unknown-netbsd`          | [32-bit i386, restricted to Pentium](https://wiki.netbsd.org/ports/i386/) |
-| `i686-unknown-netbsd`          | [32-bit i386 with SSE](https://wiki.netbsd.org/ports/i386/) |
-| `mipsel-unknown-netbsd`        | [32-bit mips, requires mips32 cpu support](https://wiki.netbsd.org/ports/evbmips/) |
-| `powerpc-unknown-netbsd`       | [Various 32-bit PowerPC systems, e.g. MacPPC](https://wiki.netbsd.org/ports/macppc/) |
-| `riscv64gc-unknown-netbsd`     | [64-bit RISC-V](https://wiki.netbsd.org/ports/riscv/) |
-| `sparc64-unknown-netbsd`       | [Sun UltraSPARC systems](https://wiki.netbsd.org/ports/sparc64/) |
+| Target tier         | Target name                   | NetBSD Platform                                                                      |
+|---------------------|-------------------------------|--------------------------------------------------------------------------------------|
+| 2 (with host tools) | `x86_64-unknown-netbsd`       | [amd64 / x86_64 systems](https://wiki.netbsd.org/ports/amd64/)                       |
+| 3                   | `armv7-unknown-netbsd-eabihf` | [32-bit ARMv7 systems with hard-float](https://wiki.netbsd.org/ports/evbarm/)        |
+| 3                   | `armv6-unknown-netbsd-eabihf` | [32-bit ARMv6 systems with hard-float](https://wiki.netbsd.org/ports/evbarm/)        |
+| 3                   | `aarch64-unknown-netbsd`      | [64-bit ARM systems, little-endian](https://wiki.netbsd.org/ports/evbarm/)           |
+| 3                   | `aarch64_be-unknown-netbsd`   | [64-bit ARM systems, big-endian](https://wiki.netbsd.org/ports/evbarm/)              |
+| 3                   | `i586-unknown-netbsd`         | [32-bit i386, restricted to Pentium](https://wiki.netbsd.org/ports/i386/)            |
+| 3                   | `i686-unknown-netbsd`         | [32-bit i386 with SSE](https://wiki.netbsd.org/ports/i386/)                          |
+| 3                   | `mipsel-unknown-netbsd`       | [32-bit mips, requires mips32 cpu support](https://wiki.netbsd.org/ports/evbmips/)   |
+| 3                   | `powerpc-unknown-netbsd`      | [Various 32-bit PowerPC systems, e.g. MacPPC](https://wiki.netbsd.org/ports/macppc/) |
+| 3                   | `riscv64gc-unknown-netbsd`    | [64-bit RISC-V](https://wiki.netbsd.org/ports/riscv/)                                |
+| 3                   | `sparc64-unknown-netbsd`      | [Sun UltraSPARC systems](https://wiki.netbsd.org/ports/sparc64/)                     |
 
 All use the "native" `stdc++` library which goes along with the natively
 supplied GNU C++ compiler for the given OS version.  Many of the bootstraps
@@ -34,10 +32,11 @@ are built for NetBSD 8.x but also work on newer OS versions).
 ## Target Maintainers
 
 [@he32](https://github.com/he32)
+[@0323pin](https://github.com/0323pin)
 
 Further contacts:
 
-- [NetBSD/pkgsrc-wip's rust](https://github.com/NetBSD/pkgsrc-wip/blob/master/rust185/Makefile) maintainer (see MAINTAINER variable). This package is part of "pkgsrc work-in-progress" and is used for deployment and testing of new versions of rust
+- [NetBSD/pkgsrc-wip's rust](https://github.com/NetBSD/pkgsrc-wip/blob/master/rust188/Makefile) maintainer (see MAINTAINER variable). This package is part of "pkgsrc work-in-progress" and is used for deployment and testing of new versions of rust.  Note that we have the convention of having multiple rust versions active in pkgsrc-wip at any one time, so the version number is part of the directory name, and from time to time old versions are culled so this is not a fully "stable" link.
 - [NetBSD's pkgsrc lang/rust](https://github.com/NetBSD/pkgsrc/tree/trunk/lang/rust) for the "proper" package in pkgsrc.
 - [NetBSD's pkgsrc lang/rust-bin](https://github.com/NetBSD/pkgsrc/tree/trunk/lang/rust-bin) which re-uses the bootstrap kit as a binary distribution and therefore avoids the rather protracted native build time of rust itself
 
diff --git a/src/doc/rustc/src/platform-support/wasm32-wasip1.md b/src/doc/rustc/src/platform-support/wasm32-wasip1.md
index 4f065a554cf..a8a9e550581 100644
--- a/src/doc/rustc/src/platform-support/wasm32-wasip1.md
+++ b/src/doc/rustc/src/platform-support/wasm32-wasip1.md
@@ -4,37 +4,35 @@
 
 The `wasm32-wasip1` target is a WebAssembly compilation target which
 assumes that the [WASIp1] (aka "WASI preview1") set of "syscalls" are available
-for use in the standard library. Historically this target in the Rust compiler
-was one of the first for WebAssembly where Rust and C code are explicitly
-intended to interoperate as well.
-
-There's a bit of history to the target and current development which is also
-worth explaining before going much further. Historically this target was
-originally called `wasm32-wasi` in both rustc and Clang. This was first added
-to Rust in 2019. In the intervening years leading up to 2024 the WASI standard
-continued to be developed and was eventually "rebased" on top of the [Component
-Model]. This was a large change to the WASI specification and was released as
-0.2.0 ("WASIp2" colloquially) in January 2024. The previous target's name in
-rustc, `wasm32-wasi`, was then renamed to `wasm32-wasip1`, to avoid
-confusion with this new target to be added to rustc as `wasm32-wasip2`.
-Some more context can be found in these MCPs:
-
-* [Rename wasm32-wasi target to wasm32-wasip1](https://github.com/rust-lang/compiler-team/issues/607)
-* [Smooth the renaming transition of wasm32-wasi](https://github.com/rust-lang/compiler-team/issues/695)
-
-At this point the `wasm32-wasip1` target is intended for historical
-compatibility with the first version of the WASI standard. As of now (January
-2024) the 0.2.0 target of WASI ("WASIp2") is relatively new. The state of
-WASI will likely change in few years after which point this documentation will
-probably receive another update.
-
-[WASI Preview1]: https://github.com/WebAssembly/WASI/tree/main/legacy/preview1
+for use in the standard library. This target explicitly supports interop with
+non-Rust code such as C and C++.
+
+The [WASIp1] set of syscalls is standard insofar as it was written down once by
+a set of folks and has not changed since then. Additionally the [WASIp1]
+syscalls have been adapted and adopted into a number of runtimes and embeddings.
+It is not standard in the sense that there are no formal semantics for each
+syscall and APIs are no longer receiving any maintenance (e.g. no new APIs, no
+new documentation, etc). After [WASIp1] was originally developed in 2019 the
+WASI standard effort has since been "rebased" on top of the [Component Model].
+This was a large change to the WASI specification and was released as 0.2.0
+("WASIp2" colloquially) in January 2024. Current standardization efforts are
+focused on the Component Model-based definition of WASI. At this point the
+`wasm32-wasip1` Rust target is intended for historical compatibility with
+[WASIp1] set of syscalls.
+
+[WASIp1]: https://github.com/WebAssembly/WASI/tree/main/legacy/preview1
 [Component Model]: https://github.com/webassembly/component-model
 
 Today the `wasm32-wasip1` target will generate core WebAssembly modules
 which will import functions from the `wasi_snapshot_preview1` module for
 OS-related functionality (e.g. printing).
 
+> **Note**: Prior to March 2024 this target was known as `wasm32-wasi` with some
+> historical context found in old MCPs:
+>
+> * [Rename wasm32-wasi target to wasm32-wasip1](https://github.com/rust-lang/compiler-team/issues/607)
+> * [Smooth the renaming transition of wasm32-wasi](https://github.com/rust-lang/compiler-team/issues/695)
+
 ## Target maintainers
 
 When this target was added to the compiler platform-specific documentation here
@@ -44,6 +42,7 @@ said since when this document was last updated those interested in maintaining
 this target are:
 
 [@alexcrichton](https://github.com/alexcrichton)
+[@loganek](https://github.com/loganek)
 
 ## Requirements
 
diff --git a/src/doc/rustdoc.md b/src/doc/rustdoc.md
index d4a25efec17..90ce7f8b225 100644
--- a/src/doc/rustdoc.md
+++ b/src/doc/rustdoc.md
@@ -1,3 +1,3 @@
 % Rust Documentation
 
-This has been moved [into the book](book/documentation.html).
+This has been moved [into the rustdoc book](rustdoc/index.html).
diff --git a/src/doc/unstable-book/src/compiler-flags/codegen-options.md b/src/doc/unstable-book/src/compiler-flags/codegen-options.md
index cc51554706d..f927e5c439c 100644
--- a/src/doc/unstable-book/src/compiler-flags/codegen-options.md
+++ b/src/doc/unstable-book/src/compiler-flags/codegen-options.md
@@ -51,10 +51,10 @@ instead of those in the system. The stable boolean values for this flag are coar
 - `mingw`: other MinGW libs and Windows import libs
 
 Out of the above self-contained linking components, `linker` is the only one currently implemented
-(beyond parsing the CLI options).
+(beyond parsing the CLI options) and stabilized.
 
 It refers to the LLD linker, built from the same LLVM revision used by rustc (named `rust-lld` to
 avoid naming conflicts), that is distributed via `rustup` with the compiler (and is used by default
-for the wasm targets). One can also opt-in to use it by combining this flag with an appropriate
-linker flavor: for example, `-Clinker-flavor=gnu-lld-cc -Clink-self-contained=+linker` will use the
-toolchain's `rust-lld` as the linker.
+for the wasm targets). One can also opt into using it by combining this flag with the appropriate
+linker feature: for example, `-Clinker-features=+lld -Clink-self-contained=+linker` will use the
+toolchain's `rust-lld` as the linker instead of the system's lld with `-Clinker-features=+lld` only.
diff --git a/src/doc/unstable-book/src/compiler-flags/linker-features.md b/src/doc/unstable-book/src/compiler-flags/linker-features.md
deleted file mode 100644
index 643fcf7c6d7..00000000000
--- a/src/doc/unstable-book/src/compiler-flags/linker-features.md
+++ /dev/null
@@ -1,35 +0,0 @@
-# `linker-features`
-
---------------------
-
-The `-Zlinker-features` compiler flag allows enabling or disabling specific features used during
-linking, and is intended to be stabilized under the codegen options as `-Clinker-features`.
-
-These feature flags are a flexible extension mechanism that is complementary to linker flavors,
-designed to avoid the combinatorial explosion of having to create a new set of flavors for each
-linker feature we'd want to use.
-
-For example, this design allows:
-- default feature sets for principal flavors, or for specific targets.
-- flavor-specific features: for example, clang offers automatic cross-linking with `--target`, which
-  gcc-style compilers don't support. The *flavor* is still a C/C++ compiler, and we don't want to
-  multiply the number of flavors for this use-case. Instead, we can have a single `+target` feature.
-- umbrella features: for example, if clang accumulates more features in the future than just the
-  `+target` above. That could be modeled as `+clang`.
-- niche features for resolving specific issues: for example, on Apple targets the linker flag
-  implementing the `as-needed` native link modifier (#99424) is only possible on sufficiently recent
-  linker versions.
-- still allows for discovery and automation, for example via feature detection. This can be useful
-  in exotic environments/build systems.
-
-The flag accepts a comma-separated list of features, individually enabled (`+features`) or disabled
-(`-features`), though currently only one is exposed on the CLI:
-- `lld`: to toggle using the lld linker, either the system-installed binary, or the self-contained
-  `rust-lld` linker.
-
-As described above, this list is intended to grow in the future.
-
-One of the most common uses of this flag will be to toggle self-contained linking with `rust-lld` on
-and off: `-Clinker-features=+lld -Clink-self-contained=+linker` will use the toolchain's `rust-lld`
-as the linker. Inversely, `-Clinker-features=-lld` would opt out of that, if the current target had
-self-contained linking enabled by default.
diff --git a/src/etc/completions/x.fish b/src/etc/completions/x.fish
index 69bd525a312..28a228d5464 100644
--- a/src/etc/completions/x.fish
+++ b/src/etc/completions/x.fish
@@ -1,6 +1,6 @@
 # Print an optspec for argparse to handle cmd's options that are independent of any subcommand.
 function __fish_x_global_optspecs
-	string join \n v/verbose i/incremental config= build-dir= build= host= target= exclude= skip= include-default-paths rustc-error-format= on-fail= dry-run dump-bootstrap-shims stage= keep-stage= keep-stage-std= src= j/jobs= warnings= json-output color= bypass-bootstrap-lock rust-profile-generate= rust-profile-use= llvm-profile-use= llvm-profile-generate enable-bolt-settings skip-stage0-validation reproducible-artifact= set= ci= skip-std-check-if-no-download-rustc h/help
+	string join \n v/verbose i/incremental config= build-dir= build= host= target= exclude= skip= include-default-paths rustc-error-format= on-fail= dry-run dump-bootstrap-shims stage= keep-stage= keep-stage-std= src= j/jobs= warnings= json-output compile-time-deps color= bypass-bootstrap-lock rust-profile-generate= rust-profile-use= llvm-profile-use= llvm-profile-generate enable-bolt-settings skip-stage0-validation reproducible-artifact= set= ci= skip-std-check-if-no-download-rustc h/help
 end
 
 function __fish_x_needs_command
@@ -52,6 +52,7 @@ complete -c x -n "__fish_x_needs_command" -l include-default-paths -d 'include d
 complete -c x -n "__fish_x_needs_command" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x -n "__fish_x_needs_command" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x -n "__fish_x_needs_command" -l json-output -d 'use message-format=json'
+complete -c x -n "__fish_x_needs_command" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x -n "__fish_x_needs_command" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x -n "__fish_x_needs_command" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x -n "__fish_x_needs_command" -l enable-bolt-settings -d 'Enable BOLT link flags'
@@ -72,7 +73,6 @@ complete -c x -n "__fish_x_needs_command" -a "dist" -d 'Build distribution artif
 complete -c x -n "__fish_x_needs_command" -a "install" -d 'Install distribution artifacts'
 complete -c x -n "__fish_x_needs_command" -a "run" -d 'Run tools contained in this repository'
 complete -c x -n "__fish_x_needs_command" -a "setup" -d 'Set up the environment for development'
-complete -c x -n "__fish_x_needs_command" -a "suggest" -d 'Suggest a subset of tests to run, based on modified files'
 complete -c x -n "__fish_x_needs_command" -a "vendor" -d 'Vendor dependencies'
 complete -c x -n "__fish_x_needs_command" -a "perf" -d 'Perform profiling and benchmarking of the compiler using `rustc-perf`'
 complete -c x -n "__fish_x_using_subcommand build" -l config -d 'TOML configuration file for build' -r -F
@@ -103,6 +103,7 @@ complete -c x -n "__fish_x_using_subcommand build" -l include-default-paths -d '
 complete -c x -n "__fish_x_using_subcommand build" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x -n "__fish_x_using_subcommand build" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x -n "__fish_x_using_subcommand build" -l json-output -d 'use message-format=json'
+complete -c x -n "__fish_x_using_subcommand build" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x -n "__fish_x_using_subcommand build" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x -n "__fish_x_using_subcommand build" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x -n "__fish_x_using_subcommand build" -l enable-bolt-settings -d 'Enable BOLT link flags'
@@ -138,6 +139,7 @@ complete -c x -n "__fish_x_using_subcommand check" -l include-default-paths -d '
 complete -c x -n "__fish_x_using_subcommand check" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x -n "__fish_x_using_subcommand check" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x -n "__fish_x_using_subcommand check" -l json-output -d 'use message-format=json'
+complete -c x -n "__fish_x_using_subcommand check" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x -n "__fish_x_using_subcommand check" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x -n "__fish_x_using_subcommand check" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x -n "__fish_x_using_subcommand check" -l enable-bolt-settings -d 'Enable BOLT link flags'
@@ -179,6 +181,7 @@ complete -c x -n "__fish_x_using_subcommand clippy" -l include-default-paths -d
 complete -c x -n "__fish_x_using_subcommand clippy" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x -n "__fish_x_using_subcommand clippy" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x -n "__fish_x_using_subcommand clippy" -l json-output -d 'use message-format=json'
+complete -c x -n "__fish_x_using_subcommand clippy" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x -n "__fish_x_using_subcommand clippy" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x -n "__fish_x_using_subcommand clippy" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x -n "__fish_x_using_subcommand clippy" -l enable-bolt-settings -d 'Enable BOLT link flags'
@@ -213,6 +216,7 @@ complete -c x -n "__fish_x_using_subcommand fix" -l include-default-paths -d 'in
 complete -c x -n "__fish_x_using_subcommand fix" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x -n "__fish_x_using_subcommand fix" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x -n "__fish_x_using_subcommand fix" -l json-output -d 'use message-format=json'
+complete -c x -n "__fish_x_using_subcommand fix" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x -n "__fish_x_using_subcommand fix" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x -n "__fish_x_using_subcommand fix" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x -n "__fish_x_using_subcommand fix" -l enable-bolt-settings -d 'Enable BOLT link flags'
@@ -249,6 +253,7 @@ complete -c x -n "__fish_x_using_subcommand fmt" -l include-default-paths -d 'in
 complete -c x -n "__fish_x_using_subcommand fmt" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x -n "__fish_x_using_subcommand fmt" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x -n "__fish_x_using_subcommand fmt" -l json-output -d 'use message-format=json'
+complete -c x -n "__fish_x_using_subcommand fmt" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x -n "__fish_x_using_subcommand fmt" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x -n "__fish_x_using_subcommand fmt" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x -n "__fish_x_using_subcommand fmt" -l enable-bolt-settings -d 'Enable BOLT link flags'
@@ -285,6 +290,7 @@ complete -c x -n "__fish_x_using_subcommand doc" -l include-default-paths -d 'in
 complete -c x -n "__fish_x_using_subcommand doc" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x -n "__fish_x_using_subcommand doc" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x -n "__fish_x_using_subcommand doc" -l json-output -d 'use message-format=json'
+complete -c x -n "__fish_x_using_subcommand doc" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x -n "__fish_x_using_subcommand doc" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x -n "__fish_x_using_subcommand doc" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x -n "__fish_x_using_subcommand doc" -l enable-bolt-settings -d 'Enable BOLT link flags'
@@ -293,7 +299,7 @@ complete -c x -n "__fish_x_using_subcommand doc" -l skip-std-check-if-no-downloa
 complete -c x -n "__fish_x_using_subcommand doc" -s h -l help -d 'Print help (see more with \'--help\')'
 complete -c x -n "__fish_x_using_subcommand test" -l test-args -d 'extra arguments to be passed for the test tool being used (e.g. libtest, compiletest or rustdoc)' -r
 complete -c x -n "__fish_x_using_subcommand test" -l compiletest-rustc-args -d 'extra options to pass the compiler when running compiletest tests' -r
-complete -c x -n "__fish_x_using_subcommand test" -l extra-checks -d 'comma-separated list of other files types to check (accepts py, py:lint, py:fmt, shell, shell:lint, cpp, cpp:fmt, spellcheck, spellcheck:fix)' -r
+complete -c x -n "__fish_x_using_subcommand test" -l extra-checks -d 'comma-separated list of other files types to check (accepts py, py:lint, py:fmt, shell, shell:lint, cpp, cpp:fmt, spellcheck)' -r
 complete -c x -n "__fish_x_using_subcommand test" -l compare-mode -d 'mode describing what file the actual ui output will be compared to' -r
 complete -c x -n "__fish_x_using_subcommand test" -l pass -d 'force {check,build,run}-pass tests to this mode' -r
 complete -c x -n "__fish_x_using_subcommand test" -l run -d 'whether to execute run-* tests' -r
@@ -333,6 +339,7 @@ complete -c x -n "__fish_x_using_subcommand test" -l include-default-paths -d 'i
 complete -c x -n "__fish_x_using_subcommand test" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x -n "__fish_x_using_subcommand test" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x -n "__fish_x_using_subcommand test" -l json-output -d 'use message-format=json'
+complete -c x -n "__fish_x_using_subcommand test" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x -n "__fish_x_using_subcommand test" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x -n "__fish_x_using_subcommand test" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x -n "__fish_x_using_subcommand test" -l enable-bolt-settings -d 'Enable BOLT link flags'
@@ -371,6 +378,7 @@ complete -c x -n "__fish_x_using_subcommand miri" -l include-default-paths -d 'i
 complete -c x -n "__fish_x_using_subcommand miri" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x -n "__fish_x_using_subcommand miri" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x -n "__fish_x_using_subcommand miri" -l json-output -d 'use message-format=json'
+complete -c x -n "__fish_x_using_subcommand miri" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x -n "__fish_x_using_subcommand miri" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x -n "__fish_x_using_subcommand miri" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x -n "__fish_x_using_subcommand miri" -l enable-bolt-settings -d 'Enable BOLT link flags'
@@ -406,6 +414,7 @@ complete -c x -n "__fish_x_using_subcommand bench" -l include-default-paths -d '
 complete -c x -n "__fish_x_using_subcommand bench" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x -n "__fish_x_using_subcommand bench" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x -n "__fish_x_using_subcommand bench" -l json-output -d 'use message-format=json'
+complete -c x -n "__fish_x_using_subcommand bench" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x -n "__fish_x_using_subcommand bench" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x -n "__fish_x_using_subcommand bench" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x -n "__fish_x_using_subcommand bench" -l enable-bolt-settings -d 'Enable BOLT link flags'
@@ -441,6 +450,7 @@ complete -c x -n "__fish_x_using_subcommand clean" -l include-default-paths -d '
 complete -c x -n "__fish_x_using_subcommand clean" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x -n "__fish_x_using_subcommand clean" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x -n "__fish_x_using_subcommand clean" -l json-output -d 'use message-format=json'
+complete -c x -n "__fish_x_using_subcommand clean" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x -n "__fish_x_using_subcommand clean" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x -n "__fish_x_using_subcommand clean" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x -n "__fish_x_using_subcommand clean" -l enable-bolt-settings -d 'Enable BOLT link flags'
@@ -475,6 +485,7 @@ complete -c x -n "__fish_x_using_subcommand dist" -l include-default-paths -d 'i
 complete -c x -n "__fish_x_using_subcommand dist" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x -n "__fish_x_using_subcommand dist" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x -n "__fish_x_using_subcommand dist" -l json-output -d 'use message-format=json'
+complete -c x -n "__fish_x_using_subcommand dist" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x -n "__fish_x_using_subcommand dist" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x -n "__fish_x_using_subcommand dist" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x -n "__fish_x_using_subcommand dist" -l enable-bolt-settings -d 'Enable BOLT link flags'
@@ -509,6 +520,7 @@ complete -c x -n "__fish_x_using_subcommand install" -l include-default-paths -d
 complete -c x -n "__fish_x_using_subcommand install" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x -n "__fish_x_using_subcommand install" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x -n "__fish_x_using_subcommand install" -l json-output -d 'use message-format=json'
+complete -c x -n "__fish_x_using_subcommand install" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x -n "__fish_x_using_subcommand install" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x -n "__fish_x_using_subcommand install" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x -n "__fish_x_using_subcommand install" -l enable-bolt-settings -d 'Enable BOLT link flags'
@@ -544,6 +556,7 @@ complete -c x -n "__fish_x_using_subcommand run" -l include-default-paths -d 'in
 complete -c x -n "__fish_x_using_subcommand run" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x -n "__fish_x_using_subcommand run" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x -n "__fish_x_using_subcommand run" -l json-output -d 'use message-format=json'
+complete -c x -n "__fish_x_using_subcommand run" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x -n "__fish_x_using_subcommand run" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x -n "__fish_x_using_subcommand run" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x -n "__fish_x_using_subcommand run" -l enable-bolt-settings -d 'Enable BOLT link flags'
@@ -578,47 +591,13 @@ complete -c x -n "__fish_x_using_subcommand setup" -l include-default-paths -d '
 complete -c x -n "__fish_x_using_subcommand setup" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x -n "__fish_x_using_subcommand setup" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x -n "__fish_x_using_subcommand setup" -l json-output -d 'use message-format=json'
+complete -c x -n "__fish_x_using_subcommand setup" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x -n "__fish_x_using_subcommand setup" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x -n "__fish_x_using_subcommand setup" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x -n "__fish_x_using_subcommand setup" -l enable-bolt-settings -d 'Enable BOLT link flags'
 complete -c x -n "__fish_x_using_subcommand setup" -l skip-stage0-validation -d 'Skip stage0 compiler validation'
 complete -c x -n "__fish_x_using_subcommand setup" -l skip-std-check-if-no-download-rustc -d 'Skip checking the standard library if `rust.download-rustc` isn\'t available. This is mostly for RA as building the stage1 compiler to check the library tree on each code change might be too much for some computers'
 complete -c x -n "__fish_x_using_subcommand setup" -s h -l help -d 'Print help (see more with \'--help\')'
-complete -c x -n "__fish_x_using_subcommand suggest" -l config -d 'TOML configuration file for build' -r -F
-complete -c x -n "__fish_x_using_subcommand suggest" -l build-dir -d 'Build directory, overrides `build.build-dir` in `bootstrap.toml`' -r -f -a "(__fish_complete_directories)"
-complete -c x -n "__fish_x_using_subcommand suggest" -l build -d 'host target of the stage0 compiler' -r -f
-complete -c x -n "__fish_x_using_subcommand suggest" -l host -d 'host targets to build' -r -f
-complete -c x -n "__fish_x_using_subcommand suggest" -l target -d 'target targets to build' -r -f
-complete -c x -n "__fish_x_using_subcommand suggest" -l exclude -d 'build paths to exclude' -r -F
-complete -c x -n "__fish_x_using_subcommand suggest" -l skip -d 'build paths to skip' -r -F
-complete -c x -n "__fish_x_using_subcommand suggest" -l rustc-error-format -d 'rustc error format' -r -f
-complete -c x -n "__fish_x_using_subcommand suggest" -l on-fail -d 'command to run on failure' -r -f -a "(__fish_complete_command)"
-complete -c x -n "__fish_x_using_subcommand suggest" -l stage -d 'stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)' -r -f
-complete -c x -n "__fish_x_using_subcommand suggest" -l keep-stage -d 'stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f
-complete -c x -n "__fish_x_using_subcommand suggest" -l keep-stage-std -d 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f
-complete -c x -n "__fish_x_using_subcommand suggest" -l src -d 'path to the root of the rust checkout' -r -f -a "(__fish_complete_directories)"
-complete -c x -n "__fish_x_using_subcommand suggest" -s j -l jobs -d 'number of jobs to run in parallel' -r -f
-complete -c x -n "__fish_x_using_subcommand suggest" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny\t'',warn\t'',default\t''}"
-complete -c x -n "__fish_x_using_subcommand suggest" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always\t'',never\t'',auto\t''}"
-complete -c x -n "__fish_x_using_subcommand suggest" -l rust-profile-generate -d 'generate PGO profile with rustc build' -r -F
-complete -c x -n "__fish_x_using_subcommand suggest" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F
-complete -c x -n "__fish_x_using_subcommand suggest" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F
-complete -c x -n "__fish_x_using_subcommand suggest" -l reproducible-artifact -d 'Additional reproducible artifacts that should be added to the reproducible artifacts archive' -r
-complete -c x -n "__fish_x_using_subcommand suggest" -l set -d 'override options in bootstrap.toml' -r -f
-complete -c x -n "__fish_x_using_subcommand suggest" -l ci -d 'Make bootstrap to behave as it\'s running on the CI environment or not' -r -f -a "{true\t'',false\t''}"
-complete -c x -n "__fish_x_using_subcommand suggest" -l run -d 'run suggested tests'
-complete -c x -n "__fish_x_using_subcommand suggest" -s v -l verbose -d 'use verbose output (-vv for very verbose)'
-complete -c x -n "__fish_x_using_subcommand suggest" -s i -l incremental -d 'use incremental compilation'
-complete -c x -n "__fish_x_using_subcommand suggest" -l include-default-paths -d 'include default paths in addition to the provided ones'
-complete -c x -n "__fish_x_using_subcommand suggest" -l dry-run -d 'dry run; don\'t build anything'
-complete -c x -n "__fish_x_using_subcommand suggest" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
-complete -c x -n "__fish_x_using_subcommand suggest" -l json-output -d 'use message-format=json'
-complete -c x -n "__fish_x_using_subcommand suggest" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
-complete -c x -n "__fish_x_using_subcommand suggest" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
-complete -c x -n "__fish_x_using_subcommand suggest" -l enable-bolt-settings -d 'Enable BOLT link flags'
-complete -c x -n "__fish_x_using_subcommand suggest" -l skip-stage0-validation -d 'Skip stage0 compiler validation'
-complete -c x -n "__fish_x_using_subcommand suggest" -l skip-std-check-if-no-download-rustc -d 'Skip checking the standard library if `rust.download-rustc` isn\'t available. This is mostly for RA as building the stage1 compiler to check the library tree on each code change might be too much for some computers'
-complete -c x -n "__fish_x_using_subcommand suggest" -s h -l help -d 'Print help (see more with \'--help\')'
 complete -c x -n "__fish_x_using_subcommand vendor" -l sync -d 'Additional `Cargo.toml` to sync and vendor' -r -F
 complete -c x -n "__fish_x_using_subcommand vendor" -l config -d 'TOML configuration file for build' -r -F
 complete -c x -n "__fish_x_using_subcommand vendor" -l build-dir -d 'Build directory, overrides `build.build-dir` in `bootstrap.toml`' -r -f -a "(__fish_complete_directories)"
@@ -649,6 +628,7 @@ complete -c x -n "__fish_x_using_subcommand vendor" -l include-default-paths -d
 complete -c x -n "__fish_x_using_subcommand vendor" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x -n "__fish_x_using_subcommand vendor" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x -n "__fish_x_using_subcommand vendor" -l json-output -d 'use message-format=json'
+complete -c x -n "__fish_x_using_subcommand vendor" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x -n "__fish_x_using_subcommand vendor" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x -n "__fish_x_using_subcommand vendor" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x -n "__fish_x_using_subcommand vendor" -l enable-bolt-settings -d 'Enable BOLT link flags'
@@ -683,6 +663,7 @@ complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand
 complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l json-output -d 'use message-format=json'
+complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x -n "__fish_x_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l enable-bolt-settings -d 'Enable BOLT link flags'
@@ -725,6 +706,7 @@ complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_fro
 complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l json-output -d 'use message-format=json'
+complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l enable-bolt-settings -d 'Enable BOLT link flags'
@@ -762,6 +744,7 @@ complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_fro
 complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l json-output -d 'use message-format=json'
+complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from samply" -l enable-bolt-settings -d 'Enable BOLT link flags'
@@ -799,6 +782,7 @@ complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_fro
 complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l json-output -d 'use message-format=json'
+complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l enable-bolt-settings -d 'Enable BOLT link flags'
@@ -836,6 +820,7 @@ complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_fro
 complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l json-output -d 'use message-format=json'
+complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l enable-bolt-settings -d 'Enable BOLT link flags'
@@ -870,6 +855,7 @@ complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_fro
 complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -l json-output -d 'use message-format=json'
+complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x -n "__fish_x_using_subcommand perf; and __fish_seen_subcommand_from compare" -l enable-bolt-settings -d 'Enable BOLT link flags'
diff --git a/src/etc/completions/x.ps1 b/src/etc/completions/x.ps1
index 7b142ba97ce..0c9b3828273 100644
--- a/src/etc/completions/x.ps1
+++ b/src/etc/completions/x.ps1
@@ -52,6 +52,7 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -73,7 +74,6 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock {
             [CompletionResult]::new('install', 'install', [CompletionResultType]::ParameterValue, 'Install distribution artifacts')
             [CompletionResult]::new('run', 'run', [CompletionResultType]::ParameterValue, 'Run tools contained in this repository')
             [CompletionResult]::new('setup', 'setup', [CompletionResultType]::ParameterValue, 'Set up the environment for development')
-            [CompletionResult]::new('suggest', 'suggest', [CompletionResultType]::ParameterValue, 'Suggest a subset of tests to run, based on modified files')
             [CompletionResult]::new('vendor', 'vendor', [CompletionResultType]::ParameterValue, 'Vendor dependencies')
             [CompletionResult]::new('perf', 'perf', [CompletionResultType]::ParameterValue, 'Perform profiling and benchmarking of the compiler using `rustc-perf`')
             break
@@ -110,6 +110,7 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -152,6 +153,7 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -200,6 +202,7 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -241,6 +244,7 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -284,6 +288,7 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -327,6 +332,7 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -339,7 +345,7 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock {
         'x;test' {
             [CompletionResult]::new('--test-args', '--test-args', [CompletionResultType]::ParameterName, 'extra arguments to be passed for the test tool being used (e.g. libtest, compiletest or rustdoc)')
             [CompletionResult]::new('--compiletest-rustc-args', '--compiletest-rustc-args', [CompletionResultType]::ParameterName, 'extra options to pass the compiler when running compiletest tests')
-            [CompletionResult]::new('--extra-checks', '--extra-checks', [CompletionResultType]::ParameterName, 'comma-separated list of other files types to check (accepts py, py:lint, py:fmt, shell, shell:lint, cpp, cpp:fmt, spellcheck, spellcheck:fix)')
+            [CompletionResult]::new('--extra-checks', '--extra-checks', [CompletionResultType]::ParameterName, 'comma-separated list of other files types to check (accepts py, py:lint, py:fmt, shell, shell:lint, cpp, cpp:fmt, spellcheck)')
             [CompletionResult]::new('--compare-mode', '--compare-mode', [CompletionResultType]::ParameterName, 'mode describing what file the actual ui output will be compared to')
             [CompletionResult]::new('--pass', '--pass', [CompletionResultType]::ParameterName, 'force {check,build,run}-pass tests to this mode')
             [CompletionResult]::new('--run', '--run', [CompletionResultType]::ParameterName, 'whether to execute run-* tests')
@@ -382,6 +388,7 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -427,6 +434,7 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -469,6 +477,7 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -511,6 +520,7 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -552,6 +562,7 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -593,6 +604,7 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -635,6 +647,7 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -676,48 +689,7 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
-            [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
-            [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
-            [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
-            [CompletionResult]::new('--skip-stage0-validation', '--skip-stage0-validation', [CompletionResultType]::ParameterName, 'Skip stage0 compiler validation')
-            [CompletionResult]::new('--skip-std-check-if-no-download-rustc', '--skip-std-check-if-no-download-rustc', [CompletionResultType]::ParameterName, 'Skip checking the standard library if `rust.download-rustc` isn''t available. This is mostly for RA as building the stage1 compiler to check the library tree on each code change might be too much for some computers')
-            [CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')')
-            [CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')')
-            break
-        }
-        'x;suggest' {
-            [CompletionResult]::new('--config', '--config', [CompletionResultType]::ParameterName, 'TOML configuration file for build')
-            [CompletionResult]::new('--build-dir', '--build-dir', [CompletionResultType]::ParameterName, 'Build directory, overrides `build.build-dir` in `bootstrap.toml`')
-            [CompletionResult]::new('--build', '--build', [CompletionResultType]::ParameterName, 'host target of the stage0 compiler')
-            [CompletionResult]::new('--host', '--host', [CompletionResultType]::ParameterName, 'host targets to build')
-            [CompletionResult]::new('--target', '--target', [CompletionResultType]::ParameterName, 'target targets to build')
-            [CompletionResult]::new('--exclude', '--exclude', [CompletionResultType]::ParameterName, 'build paths to exclude')
-            [CompletionResult]::new('--skip', '--skip', [CompletionResultType]::ParameterName, 'build paths to skip')
-            [CompletionResult]::new('--rustc-error-format', '--rustc-error-format', [CompletionResultType]::ParameterName, 'rustc error format')
-            [CompletionResult]::new('--on-fail', '--on-fail', [CompletionResultType]::ParameterName, 'command to run on failure')
-            [CompletionResult]::new('--stage', '--stage', [CompletionResultType]::ParameterName, 'stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)')
-            [CompletionResult]::new('--keep-stage', '--keep-stage', [CompletionResultType]::ParameterName, 'stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)')
-            [CompletionResult]::new('--keep-stage-std', '--keep-stage-std', [CompletionResultType]::ParameterName, 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)')
-            [CompletionResult]::new('--src', '--src', [CompletionResultType]::ParameterName, 'path to the root of the rust checkout')
-            [CompletionResult]::new('-j', '-j', [CompletionResultType]::ParameterName, 'number of jobs to run in parallel')
-            [CompletionResult]::new('--jobs', '--jobs', [CompletionResultType]::ParameterName, 'number of jobs to run in parallel')
-            [CompletionResult]::new('--warnings', '--warnings', [CompletionResultType]::ParameterName, 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour')
-            [CompletionResult]::new('--color', '--color', [CompletionResultType]::ParameterName, 'whether to use color in cargo and rustc output')
-            [CompletionResult]::new('--rust-profile-generate', '--rust-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with rustc build')
-            [CompletionResult]::new('--rust-profile-use', '--rust-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for rustc build')
-            [CompletionResult]::new('--llvm-profile-use', '--llvm-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for LLVM build')
-            [CompletionResult]::new('--reproducible-artifact', '--reproducible-artifact', [CompletionResultType]::ParameterName, 'Additional reproducible artifacts that should be added to the reproducible artifacts archive')
-            [CompletionResult]::new('--set', '--set', [CompletionResultType]::ParameterName, 'override options in bootstrap.toml')
-            [CompletionResult]::new('--ci', '--ci', [CompletionResultType]::ParameterName, 'Make bootstrap to behave as it''s running on the CI environment or not')
-            [CompletionResult]::new('--run', '--run', [CompletionResultType]::ParameterName, 'run suggested tests')
-            [CompletionResult]::new('-v', '-v', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)')
-            [CompletionResult]::new('--verbose', '--verbose', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)')
-            [CompletionResult]::new('-i', '-i', [CompletionResultType]::ParameterName, 'use incremental compilation')
-            [CompletionResult]::new('--incremental', '--incremental', [CompletionResultType]::ParameterName, 'use incremental compilation')
-            [CompletionResult]::new('--include-default-paths', '--include-default-paths', [CompletionResultType]::ParameterName, 'include default paths in addition to the provided ones')
-            [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
-            [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
-            [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -761,6 +733,7 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -802,6 +775,7 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -851,6 +825,7 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -895,6 +870,7 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -939,6 +915,7 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -983,6 +960,7 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -1024,6 +1002,7 @@ Register-ArgumentCompleter -Native -CommandName 'x' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
diff --git a/src/etc/completions/x.py.fish b/src/etc/completions/x.py.fish
index e982cde634f..43ae7424e27 100644
--- a/src/etc/completions/x.py.fish
+++ b/src/etc/completions/x.py.fish
@@ -1,6 +1,6 @@
 # Print an optspec for argparse to handle cmd's options that are independent of any subcommand.
 function __fish_x.py_global_optspecs
-	string join \n v/verbose i/incremental config= build-dir= build= host= target= exclude= skip= include-default-paths rustc-error-format= on-fail= dry-run dump-bootstrap-shims stage= keep-stage= keep-stage-std= src= j/jobs= warnings= json-output color= bypass-bootstrap-lock rust-profile-generate= rust-profile-use= llvm-profile-use= llvm-profile-generate enable-bolt-settings skip-stage0-validation reproducible-artifact= set= ci= skip-std-check-if-no-download-rustc h/help
+	string join \n v/verbose i/incremental config= build-dir= build= host= target= exclude= skip= include-default-paths rustc-error-format= on-fail= dry-run dump-bootstrap-shims stage= keep-stage= keep-stage-std= src= j/jobs= warnings= json-output compile-time-deps color= bypass-bootstrap-lock rust-profile-generate= rust-profile-use= llvm-profile-use= llvm-profile-generate enable-bolt-settings skip-stage0-validation reproducible-artifact= set= ci= skip-std-check-if-no-download-rustc h/help
 end
 
 function __fish_x.py_needs_command
@@ -52,6 +52,7 @@ complete -c x.py -n "__fish_x.py_needs_command" -l include-default-paths -d 'inc
 complete -c x.py -n "__fish_x.py_needs_command" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x.py -n "__fish_x.py_needs_command" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x.py -n "__fish_x.py_needs_command" -l json-output -d 'use message-format=json'
+complete -c x.py -n "__fish_x.py_needs_command" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x.py -n "__fish_x.py_needs_command" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x.py -n "__fish_x.py_needs_command" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x.py -n "__fish_x.py_needs_command" -l enable-bolt-settings -d 'Enable BOLT link flags'
@@ -72,7 +73,6 @@ complete -c x.py -n "__fish_x.py_needs_command" -a "dist" -d 'Build distribution
 complete -c x.py -n "__fish_x.py_needs_command" -a "install" -d 'Install distribution artifacts'
 complete -c x.py -n "__fish_x.py_needs_command" -a "run" -d 'Run tools contained in this repository'
 complete -c x.py -n "__fish_x.py_needs_command" -a "setup" -d 'Set up the environment for development'
-complete -c x.py -n "__fish_x.py_needs_command" -a "suggest" -d 'Suggest a subset of tests to run, based on modified files'
 complete -c x.py -n "__fish_x.py_needs_command" -a "vendor" -d 'Vendor dependencies'
 complete -c x.py -n "__fish_x.py_needs_command" -a "perf" -d 'Perform profiling and benchmarking of the compiler using `rustc-perf`'
 complete -c x.py -n "__fish_x.py_using_subcommand build" -l config -d 'TOML configuration file for build' -r -F
@@ -103,6 +103,7 @@ complete -c x.py -n "__fish_x.py_using_subcommand build" -l include-default-path
 complete -c x.py -n "__fish_x.py_using_subcommand build" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x.py -n "__fish_x.py_using_subcommand build" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x.py -n "__fish_x.py_using_subcommand build" -l json-output -d 'use message-format=json'
+complete -c x.py -n "__fish_x.py_using_subcommand build" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x.py -n "__fish_x.py_using_subcommand build" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x.py -n "__fish_x.py_using_subcommand build" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x.py -n "__fish_x.py_using_subcommand build" -l enable-bolt-settings -d 'Enable BOLT link flags'
@@ -138,6 +139,7 @@ complete -c x.py -n "__fish_x.py_using_subcommand check" -l include-default-path
 complete -c x.py -n "__fish_x.py_using_subcommand check" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x.py -n "__fish_x.py_using_subcommand check" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x.py -n "__fish_x.py_using_subcommand check" -l json-output -d 'use message-format=json'
+complete -c x.py -n "__fish_x.py_using_subcommand check" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x.py -n "__fish_x.py_using_subcommand check" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x.py -n "__fish_x.py_using_subcommand check" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x.py -n "__fish_x.py_using_subcommand check" -l enable-bolt-settings -d 'Enable BOLT link flags'
@@ -179,6 +181,7 @@ complete -c x.py -n "__fish_x.py_using_subcommand clippy" -l include-default-pat
 complete -c x.py -n "__fish_x.py_using_subcommand clippy" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x.py -n "__fish_x.py_using_subcommand clippy" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x.py -n "__fish_x.py_using_subcommand clippy" -l json-output -d 'use message-format=json'
+complete -c x.py -n "__fish_x.py_using_subcommand clippy" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x.py -n "__fish_x.py_using_subcommand clippy" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x.py -n "__fish_x.py_using_subcommand clippy" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x.py -n "__fish_x.py_using_subcommand clippy" -l enable-bolt-settings -d 'Enable BOLT link flags'
@@ -213,6 +216,7 @@ complete -c x.py -n "__fish_x.py_using_subcommand fix" -l include-default-paths
 complete -c x.py -n "__fish_x.py_using_subcommand fix" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x.py -n "__fish_x.py_using_subcommand fix" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x.py -n "__fish_x.py_using_subcommand fix" -l json-output -d 'use message-format=json'
+complete -c x.py -n "__fish_x.py_using_subcommand fix" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x.py -n "__fish_x.py_using_subcommand fix" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x.py -n "__fish_x.py_using_subcommand fix" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x.py -n "__fish_x.py_using_subcommand fix" -l enable-bolt-settings -d 'Enable BOLT link flags'
@@ -249,6 +253,7 @@ complete -c x.py -n "__fish_x.py_using_subcommand fmt" -l include-default-paths
 complete -c x.py -n "__fish_x.py_using_subcommand fmt" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x.py -n "__fish_x.py_using_subcommand fmt" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x.py -n "__fish_x.py_using_subcommand fmt" -l json-output -d 'use message-format=json'
+complete -c x.py -n "__fish_x.py_using_subcommand fmt" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x.py -n "__fish_x.py_using_subcommand fmt" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x.py -n "__fish_x.py_using_subcommand fmt" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x.py -n "__fish_x.py_using_subcommand fmt" -l enable-bolt-settings -d 'Enable BOLT link flags'
@@ -285,6 +290,7 @@ complete -c x.py -n "__fish_x.py_using_subcommand doc" -l include-default-paths
 complete -c x.py -n "__fish_x.py_using_subcommand doc" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x.py -n "__fish_x.py_using_subcommand doc" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x.py -n "__fish_x.py_using_subcommand doc" -l json-output -d 'use message-format=json'
+complete -c x.py -n "__fish_x.py_using_subcommand doc" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x.py -n "__fish_x.py_using_subcommand doc" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x.py -n "__fish_x.py_using_subcommand doc" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x.py -n "__fish_x.py_using_subcommand doc" -l enable-bolt-settings -d 'Enable BOLT link flags'
@@ -293,7 +299,7 @@ complete -c x.py -n "__fish_x.py_using_subcommand doc" -l skip-std-check-if-no-d
 complete -c x.py -n "__fish_x.py_using_subcommand doc" -s h -l help -d 'Print help (see more with \'--help\')'
 complete -c x.py -n "__fish_x.py_using_subcommand test" -l test-args -d 'extra arguments to be passed for the test tool being used (e.g. libtest, compiletest or rustdoc)' -r
 complete -c x.py -n "__fish_x.py_using_subcommand test" -l compiletest-rustc-args -d 'extra options to pass the compiler when running compiletest tests' -r
-complete -c x.py -n "__fish_x.py_using_subcommand test" -l extra-checks -d 'comma-separated list of other files types to check (accepts py, py:lint, py:fmt, shell, shell:lint, cpp, cpp:fmt, spellcheck, spellcheck:fix)' -r
+complete -c x.py -n "__fish_x.py_using_subcommand test" -l extra-checks -d 'comma-separated list of other files types to check (accepts py, py:lint, py:fmt, shell, shell:lint, cpp, cpp:fmt, spellcheck)' -r
 complete -c x.py -n "__fish_x.py_using_subcommand test" -l compare-mode -d 'mode describing what file the actual ui output will be compared to' -r
 complete -c x.py -n "__fish_x.py_using_subcommand test" -l pass -d 'force {check,build,run}-pass tests to this mode' -r
 complete -c x.py -n "__fish_x.py_using_subcommand test" -l run -d 'whether to execute run-* tests' -r
@@ -333,6 +339,7 @@ complete -c x.py -n "__fish_x.py_using_subcommand test" -l include-default-paths
 complete -c x.py -n "__fish_x.py_using_subcommand test" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x.py -n "__fish_x.py_using_subcommand test" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x.py -n "__fish_x.py_using_subcommand test" -l json-output -d 'use message-format=json'
+complete -c x.py -n "__fish_x.py_using_subcommand test" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x.py -n "__fish_x.py_using_subcommand test" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x.py -n "__fish_x.py_using_subcommand test" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x.py -n "__fish_x.py_using_subcommand test" -l enable-bolt-settings -d 'Enable BOLT link flags'
@@ -371,6 +378,7 @@ complete -c x.py -n "__fish_x.py_using_subcommand miri" -l include-default-paths
 complete -c x.py -n "__fish_x.py_using_subcommand miri" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x.py -n "__fish_x.py_using_subcommand miri" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x.py -n "__fish_x.py_using_subcommand miri" -l json-output -d 'use message-format=json'
+complete -c x.py -n "__fish_x.py_using_subcommand miri" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x.py -n "__fish_x.py_using_subcommand miri" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x.py -n "__fish_x.py_using_subcommand miri" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x.py -n "__fish_x.py_using_subcommand miri" -l enable-bolt-settings -d 'Enable BOLT link flags'
@@ -406,6 +414,7 @@ complete -c x.py -n "__fish_x.py_using_subcommand bench" -l include-default-path
 complete -c x.py -n "__fish_x.py_using_subcommand bench" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x.py -n "__fish_x.py_using_subcommand bench" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x.py -n "__fish_x.py_using_subcommand bench" -l json-output -d 'use message-format=json'
+complete -c x.py -n "__fish_x.py_using_subcommand bench" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x.py -n "__fish_x.py_using_subcommand bench" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x.py -n "__fish_x.py_using_subcommand bench" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x.py -n "__fish_x.py_using_subcommand bench" -l enable-bolt-settings -d 'Enable BOLT link flags'
@@ -441,6 +450,7 @@ complete -c x.py -n "__fish_x.py_using_subcommand clean" -l include-default-path
 complete -c x.py -n "__fish_x.py_using_subcommand clean" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x.py -n "__fish_x.py_using_subcommand clean" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x.py -n "__fish_x.py_using_subcommand clean" -l json-output -d 'use message-format=json'
+complete -c x.py -n "__fish_x.py_using_subcommand clean" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x.py -n "__fish_x.py_using_subcommand clean" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x.py -n "__fish_x.py_using_subcommand clean" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x.py -n "__fish_x.py_using_subcommand clean" -l enable-bolt-settings -d 'Enable BOLT link flags'
@@ -475,6 +485,7 @@ complete -c x.py -n "__fish_x.py_using_subcommand dist" -l include-default-paths
 complete -c x.py -n "__fish_x.py_using_subcommand dist" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x.py -n "__fish_x.py_using_subcommand dist" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x.py -n "__fish_x.py_using_subcommand dist" -l json-output -d 'use message-format=json'
+complete -c x.py -n "__fish_x.py_using_subcommand dist" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x.py -n "__fish_x.py_using_subcommand dist" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x.py -n "__fish_x.py_using_subcommand dist" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x.py -n "__fish_x.py_using_subcommand dist" -l enable-bolt-settings -d 'Enable BOLT link flags'
@@ -509,6 +520,7 @@ complete -c x.py -n "__fish_x.py_using_subcommand install" -l include-default-pa
 complete -c x.py -n "__fish_x.py_using_subcommand install" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x.py -n "__fish_x.py_using_subcommand install" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x.py -n "__fish_x.py_using_subcommand install" -l json-output -d 'use message-format=json'
+complete -c x.py -n "__fish_x.py_using_subcommand install" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x.py -n "__fish_x.py_using_subcommand install" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x.py -n "__fish_x.py_using_subcommand install" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x.py -n "__fish_x.py_using_subcommand install" -l enable-bolt-settings -d 'Enable BOLT link flags'
@@ -544,6 +556,7 @@ complete -c x.py -n "__fish_x.py_using_subcommand run" -l include-default-paths
 complete -c x.py -n "__fish_x.py_using_subcommand run" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x.py -n "__fish_x.py_using_subcommand run" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x.py -n "__fish_x.py_using_subcommand run" -l json-output -d 'use message-format=json'
+complete -c x.py -n "__fish_x.py_using_subcommand run" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x.py -n "__fish_x.py_using_subcommand run" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x.py -n "__fish_x.py_using_subcommand run" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x.py -n "__fish_x.py_using_subcommand run" -l enable-bolt-settings -d 'Enable BOLT link flags'
@@ -578,47 +591,13 @@ complete -c x.py -n "__fish_x.py_using_subcommand setup" -l include-default-path
 complete -c x.py -n "__fish_x.py_using_subcommand setup" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x.py -n "__fish_x.py_using_subcommand setup" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x.py -n "__fish_x.py_using_subcommand setup" -l json-output -d 'use message-format=json'
+complete -c x.py -n "__fish_x.py_using_subcommand setup" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x.py -n "__fish_x.py_using_subcommand setup" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x.py -n "__fish_x.py_using_subcommand setup" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x.py -n "__fish_x.py_using_subcommand setup" -l enable-bolt-settings -d 'Enable BOLT link flags'
 complete -c x.py -n "__fish_x.py_using_subcommand setup" -l skip-stage0-validation -d 'Skip stage0 compiler validation'
 complete -c x.py -n "__fish_x.py_using_subcommand setup" -l skip-std-check-if-no-download-rustc -d 'Skip checking the standard library if `rust.download-rustc` isn\'t available. This is mostly for RA as building the stage1 compiler to check the library tree on each code change might be too much for some computers'
 complete -c x.py -n "__fish_x.py_using_subcommand setup" -s h -l help -d 'Print help (see more with \'--help\')'
-complete -c x.py -n "__fish_x.py_using_subcommand suggest" -l config -d 'TOML configuration file for build' -r -F
-complete -c x.py -n "__fish_x.py_using_subcommand suggest" -l build-dir -d 'Build directory, overrides `build.build-dir` in `bootstrap.toml`' -r -f -a "(__fish_complete_directories)"
-complete -c x.py -n "__fish_x.py_using_subcommand suggest" -l build -d 'host target of the stage0 compiler' -r -f
-complete -c x.py -n "__fish_x.py_using_subcommand suggest" -l host -d 'host targets to build' -r -f
-complete -c x.py -n "__fish_x.py_using_subcommand suggest" -l target -d 'target targets to build' -r -f
-complete -c x.py -n "__fish_x.py_using_subcommand suggest" -l exclude -d 'build paths to exclude' -r -F
-complete -c x.py -n "__fish_x.py_using_subcommand suggest" -l skip -d 'build paths to skip' -r -F
-complete -c x.py -n "__fish_x.py_using_subcommand suggest" -l rustc-error-format -d 'rustc error format' -r -f
-complete -c x.py -n "__fish_x.py_using_subcommand suggest" -l on-fail -d 'command to run on failure' -r -f -a "(__fish_complete_command)"
-complete -c x.py -n "__fish_x.py_using_subcommand suggest" -l stage -d 'stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)' -r -f
-complete -c x.py -n "__fish_x.py_using_subcommand suggest" -l keep-stage -d 'stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f
-complete -c x.py -n "__fish_x.py_using_subcommand suggest" -l keep-stage-std -d 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)' -r -f
-complete -c x.py -n "__fish_x.py_using_subcommand suggest" -l src -d 'path to the root of the rust checkout' -r -f -a "(__fish_complete_directories)"
-complete -c x.py -n "__fish_x.py_using_subcommand suggest" -s j -l jobs -d 'number of jobs to run in parallel' -r -f
-complete -c x.py -n "__fish_x.py_using_subcommand suggest" -l warnings -d 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour' -r -f -a "{deny\t'',warn\t'',default\t''}"
-complete -c x.py -n "__fish_x.py_using_subcommand suggest" -l color -d 'whether to use color in cargo and rustc output' -r -f -a "{always\t'',never\t'',auto\t''}"
-complete -c x.py -n "__fish_x.py_using_subcommand suggest" -l rust-profile-generate -d 'generate PGO profile with rustc build' -r -F
-complete -c x.py -n "__fish_x.py_using_subcommand suggest" -l rust-profile-use -d 'use PGO profile for rustc build' -r -F
-complete -c x.py -n "__fish_x.py_using_subcommand suggest" -l llvm-profile-use -d 'use PGO profile for LLVM build' -r -F
-complete -c x.py -n "__fish_x.py_using_subcommand suggest" -l reproducible-artifact -d 'Additional reproducible artifacts that should be added to the reproducible artifacts archive' -r
-complete -c x.py -n "__fish_x.py_using_subcommand suggest" -l set -d 'override options in bootstrap.toml' -r -f
-complete -c x.py -n "__fish_x.py_using_subcommand suggest" -l ci -d 'Make bootstrap to behave as it\'s running on the CI environment or not' -r -f -a "{true\t'',false\t''}"
-complete -c x.py -n "__fish_x.py_using_subcommand suggest" -l run -d 'run suggested tests'
-complete -c x.py -n "__fish_x.py_using_subcommand suggest" -s v -l verbose -d 'use verbose output (-vv for very verbose)'
-complete -c x.py -n "__fish_x.py_using_subcommand suggest" -s i -l incremental -d 'use incremental compilation'
-complete -c x.py -n "__fish_x.py_using_subcommand suggest" -l include-default-paths -d 'include default paths in addition to the provided ones'
-complete -c x.py -n "__fish_x.py_using_subcommand suggest" -l dry-run -d 'dry run; don\'t build anything'
-complete -c x.py -n "__fish_x.py_using_subcommand suggest" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
-complete -c x.py -n "__fish_x.py_using_subcommand suggest" -l json-output -d 'use message-format=json'
-complete -c x.py -n "__fish_x.py_using_subcommand suggest" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
-complete -c x.py -n "__fish_x.py_using_subcommand suggest" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
-complete -c x.py -n "__fish_x.py_using_subcommand suggest" -l enable-bolt-settings -d 'Enable BOLT link flags'
-complete -c x.py -n "__fish_x.py_using_subcommand suggest" -l skip-stage0-validation -d 'Skip stage0 compiler validation'
-complete -c x.py -n "__fish_x.py_using_subcommand suggest" -l skip-std-check-if-no-download-rustc -d 'Skip checking the standard library if `rust.download-rustc` isn\'t available. This is mostly for RA as building the stage1 compiler to check the library tree on each code change might be too much for some computers'
-complete -c x.py -n "__fish_x.py_using_subcommand suggest" -s h -l help -d 'Print help (see more with \'--help\')'
 complete -c x.py -n "__fish_x.py_using_subcommand vendor" -l sync -d 'Additional `Cargo.toml` to sync and vendor' -r -F
 complete -c x.py -n "__fish_x.py_using_subcommand vendor" -l config -d 'TOML configuration file for build' -r -F
 complete -c x.py -n "__fish_x.py_using_subcommand vendor" -l build-dir -d 'Build directory, overrides `build.build-dir` in `bootstrap.toml`' -r -f -a "(__fish_complete_directories)"
@@ -649,6 +628,7 @@ complete -c x.py -n "__fish_x.py_using_subcommand vendor" -l include-default-pat
 complete -c x.py -n "__fish_x.py_using_subcommand vendor" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x.py -n "__fish_x.py_using_subcommand vendor" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x.py -n "__fish_x.py_using_subcommand vendor" -l json-output -d 'use message-format=json'
+complete -c x.py -n "__fish_x.py_using_subcommand vendor" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x.py -n "__fish_x.py_using_subcommand vendor" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x.py -n "__fish_x.py_using_subcommand vendor" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x.py -n "__fish_x.py_using_subcommand vendor" -l enable-bolt-settings -d 'Enable BOLT link flags'
@@ -683,6 +663,7 @@ complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subc
 complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l json-output -d 'use message-format=json'
+complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x.py -n "__fish_x.py_using_subcommand perf; and not __fish_seen_subcommand_from eprintln samply cachegrind benchmark compare" -l enable-bolt-settings -d 'Enable BOLT link flags'
@@ -725,6 +706,7 @@ complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcomma
 complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l json-output -d 'use message-format=json'
+complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from eprintln" -l enable-bolt-settings -d 'Enable BOLT link flags'
@@ -762,6 +744,7 @@ complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcomma
 complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l json-output -d 'use message-format=json'
+complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from samply" -l enable-bolt-settings -d 'Enable BOLT link flags'
@@ -799,6 +782,7 @@ complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcomma
 complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l json-output -d 'use message-format=json'
+complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from cachegrind" -l enable-bolt-settings -d 'Enable BOLT link flags'
@@ -836,6 +820,7 @@ complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcomma
 complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l json-output -d 'use message-format=json'
+complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from benchmark" -l enable-bolt-settings -d 'Enable BOLT link flags'
@@ -870,6 +855,7 @@ complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcomma
 complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -l dry-run -d 'dry run; don\'t build anything'
 complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -l dump-bootstrap-shims -d 'Indicates whether to dump the work done from bootstrap shims'
 complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -l json-output -d 'use message-format=json'
+complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -l compile-time-deps -d 'only build proc-macros and build scripts (for rust-analyzer)'
 complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -l bypass-bootstrap-lock -d 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)'
 complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -l llvm-profile-generate -d 'generate PGO profile with llvm built for rustc'
 complete -c x.py -n "__fish_x.py_using_subcommand perf; and __fish_seen_subcommand_from compare" -l enable-bolt-settings -d 'Enable BOLT link flags'
diff --git a/src/etc/completions/x.py.ps1 b/src/etc/completions/x.py.ps1
index f4b26fac0bc..4311e383d64 100644
--- a/src/etc/completions/x.py.ps1
+++ b/src/etc/completions/x.py.ps1
@@ -52,6 +52,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -73,7 +74,6 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
             [CompletionResult]::new('install', 'install', [CompletionResultType]::ParameterValue, 'Install distribution artifacts')
             [CompletionResult]::new('run', 'run', [CompletionResultType]::ParameterValue, 'Run tools contained in this repository')
             [CompletionResult]::new('setup', 'setup', [CompletionResultType]::ParameterValue, 'Set up the environment for development')
-            [CompletionResult]::new('suggest', 'suggest', [CompletionResultType]::ParameterValue, 'Suggest a subset of tests to run, based on modified files')
             [CompletionResult]::new('vendor', 'vendor', [CompletionResultType]::ParameterValue, 'Vendor dependencies')
             [CompletionResult]::new('perf', 'perf', [CompletionResultType]::ParameterValue, 'Perform profiling and benchmarking of the compiler using `rustc-perf`')
             break
@@ -110,6 +110,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -152,6 +153,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -200,6 +202,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -241,6 +244,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -284,6 +288,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -327,6 +332,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -339,7 +345,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
         'x.py;test' {
             [CompletionResult]::new('--test-args', '--test-args', [CompletionResultType]::ParameterName, 'extra arguments to be passed for the test tool being used (e.g. libtest, compiletest or rustdoc)')
             [CompletionResult]::new('--compiletest-rustc-args', '--compiletest-rustc-args', [CompletionResultType]::ParameterName, 'extra options to pass the compiler when running compiletest tests')
-            [CompletionResult]::new('--extra-checks', '--extra-checks', [CompletionResultType]::ParameterName, 'comma-separated list of other files types to check (accepts py, py:lint, py:fmt, shell, shell:lint, cpp, cpp:fmt, spellcheck, spellcheck:fix)')
+            [CompletionResult]::new('--extra-checks', '--extra-checks', [CompletionResultType]::ParameterName, 'comma-separated list of other files types to check (accepts py, py:lint, py:fmt, shell, shell:lint, cpp, cpp:fmt, spellcheck)')
             [CompletionResult]::new('--compare-mode', '--compare-mode', [CompletionResultType]::ParameterName, 'mode describing what file the actual ui output will be compared to')
             [CompletionResult]::new('--pass', '--pass', [CompletionResultType]::ParameterName, 'force {check,build,run}-pass tests to this mode')
             [CompletionResult]::new('--run', '--run', [CompletionResultType]::ParameterName, 'whether to execute run-* tests')
@@ -382,6 +388,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -427,6 +434,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -469,6 +477,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -511,6 +520,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -552,6 +562,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -593,6 +604,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -635,6 +647,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -676,48 +689,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
-            [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
-            [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
-            [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
-            [CompletionResult]::new('--skip-stage0-validation', '--skip-stage0-validation', [CompletionResultType]::ParameterName, 'Skip stage0 compiler validation')
-            [CompletionResult]::new('--skip-std-check-if-no-download-rustc', '--skip-std-check-if-no-download-rustc', [CompletionResultType]::ParameterName, 'Skip checking the standard library if `rust.download-rustc` isn''t available. This is mostly for RA as building the stage1 compiler to check the library tree on each code change might be too much for some computers')
-            [CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')')
-            [CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'Print help (see more with ''--help'')')
-            break
-        }
-        'x.py;suggest' {
-            [CompletionResult]::new('--config', '--config', [CompletionResultType]::ParameterName, 'TOML configuration file for build')
-            [CompletionResult]::new('--build-dir', '--build-dir', [CompletionResultType]::ParameterName, 'Build directory, overrides `build.build-dir` in `bootstrap.toml`')
-            [CompletionResult]::new('--build', '--build', [CompletionResultType]::ParameterName, 'host target of the stage0 compiler')
-            [CompletionResult]::new('--host', '--host', [CompletionResultType]::ParameterName, 'host targets to build')
-            [CompletionResult]::new('--target', '--target', [CompletionResultType]::ParameterName, 'target targets to build')
-            [CompletionResult]::new('--exclude', '--exclude', [CompletionResultType]::ParameterName, 'build paths to exclude')
-            [CompletionResult]::new('--skip', '--skip', [CompletionResultType]::ParameterName, 'build paths to skip')
-            [CompletionResult]::new('--rustc-error-format', '--rustc-error-format', [CompletionResultType]::ParameterName, 'rustc error format')
-            [CompletionResult]::new('--on-fail', '--on-fail', [CompletionResultType]::ParameterName, 'command to run on failure')
-            [CompletionResult]::new('--stage', '--stage', [CompletionResultType]::ParameterName, 'stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)')
-            [CompletionResult]::new('--keep-stage', '--keep-stage', [CompletionResultType]::ParameterName, 'stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)')
-            [CompletionResult]::new('--keep-stage-std', '--keep-stage-std', [CompletionResultType]::ParameterName, 'stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)')
-            [CompletionResult]::new('--src', '--src', [CompletionResultType]::ParameterName, 'path to the root of the rust checkout')
-            [CompletionResult]::new('-j', '-j', [CompletionResultType]::ParameterName, 'number of jobs to run in parallel')
-            [CompletionResult]::new('--jobs', '--jobs', [CompletionResultType]::ParameterName, 'number of jobs to run in parallel')
-            [CompletionResult]::new('--warnings', '--warnings', [CompletionResultType]::ParameterName, 'if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour')
-            [CompletionResult]::new('--color', '--color', [CompletionResultType]::ParameterName, 'whether to use color in cargo and rustc output')
-            [CompletionResult]::new('--rust-profile-generate', '--rust-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with rustc build')
-            [CompletionResult]::new('--rust-profile-use', '--rust-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for rustc build')
-            [CompletionResult]::new('--llvm-profile-use', '--llvm-profile-use', [CompletionResultType]::ParameterName, 'use PGO profile for LLVM build')
-            [CompletionResult]::new('--reproducible-artifact', '--reproducible-artifact', [CompletionResultType]::ParameterName, 'Additional reproducible artifacts that should be added to the reproducible artifacts archive')
-            [CompletionResult]::new('--set', '--set', [CompletionResultType]::ParameterName, 'override options in bootstrap.toml')
-            [CompletionResult]::new('--ci', '--ci', [CompletionResultType]::ParameterName, 'Make bootstrap to behave as it''s running on the CI environment or not')
-            [CompletionResult]::new('--run', '--run', [CompletionResultType]::ParameterName, 'run suggested tests')
-            [CompletionResult]::new('-v', '-v', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)')
-            [CompletionResult]::new('--verbose', '--verbose', [CompletionResultType]::ParameterName, 'use verbose output (-vv for very verbose)')
-            [CompletionResult]::new('-i', '-i', [CompletionResultType]::ParameterName, 'use incremental compilation')
-            [CompletionResult]::new('--incremental', '--incremental', [CompletionResultType]::ParameterName, 'use incremental compilation')
-            [CompletionResult]::new('--include-default-paths', '--include-default-paths', [CompletionResultType]::ParameterName, 'include default paths in addition to the provided ones')
-            [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
-            [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
-            [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -761,6 +733,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -802,6 +775,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -851,6 +825,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -895,6 +870,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -939,6 +915,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -983,6 +960,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
@@ -1024,6 +1002,7 @@ Register-ArgumentCompleter -Native -CommandName 'x.py' -ScriptBlock {
             [CompletionResult]::new('--dry-run', '--dry-run', [CompletionResultType]::ParameterName, 'dry run; don''t build anything')
             [CompletionResult]::new('--dump-bootstrap-shims', '--dump-bootstrap-shims', [CompletionResultType]::ParameterName, 'Indicates whether to dump the work done from bootstrap shims')
             [CompletionResult]::new('--json-output', '--json-output', [CompletionResultType]::ParameterName, 'use message-format=json')
+            [CompletionResult]::new('--compile-time-deps', '--compile-time-deps', [CompletionResultType]::ParameterName, 'only build proc-macros and build scripts (for rust-analyzer)')
             [CompletionResult]::new('--bypass-bootstrap-lock', '--bypass-bootstrap-lock', [CompletionResultType]::ParameterName, 'Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)')
             [CompletionResult]::new('--llvm-profile-generate', '--llvm-profile-generate', [CompletionResultType]::ParameterName, 'generate PGO profile with llvm built for rustc')
             [CompletionResult]::new('--enable-bolt-settings', '--enable-bolt-settings', [CompletionResultType]::ParameterName, 'Enable BOLT link flags')
diff --git a/src/etc/completions/x.py.sh b/src/etc/completions/x.py.sh
index fe4ed5c915f..f31bdb58dc4 100644
--- a/src/etc/completions/x.py.sh
+++ b/src/etc/completions/x.py.sh
@@ -54,9 +54,6 @@ _x.py() {
             x.py,setup)
                 cmd="x.py__setup"
                 ;;
-            x.py,suggest)
-                cmd="x.py__suggest"
-                ;;
             x.py,test)
                 cmd="x.py__test"
                 ;;
@@ -85,7 +82,7 @@ _x.py() {
 
     case "${cmd}" in
         x.py)
-            opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]... build check clippy fix fmt doc test miri bench clean dist install run setup suggest vendor perf"
+            opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]... build check clippy fix fmt doc test miri bench clean dist install run setup vendor perf"
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -271,7 +268,7 @@ _x.py() {
             return 0
             ;;
         x.py__bench)
-            opts="-v -i -j -h --test-args --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
+            opts="-v -i -j -h --test-args --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -461,7 +458,7 @@ _x.py() {
             return 0
             ;;
         x.py__build)
-            opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
+            opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -647,7 +644,7 @@ _x.py() {
             return 0
             ;;
         x.py__check)
-            opts="-v -i -j -h --all-targets --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
+            opts="-v -i -j -h --all-targets --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -833,7 +830,7 @@ _x.py() {
             return 0
             ;;
         x.py__clean)
-            opts="-v -i -j -h --all --stage --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
+            opts="-v -i -j -h --all --stage --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -1016,7 +1013,7 @@ _x.py() {
             return 0
             ;;
         x.py__clippy)
-            opts="-A -D -W -F -v -i -j -h --fix --allow-dirty --allow-staged --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
+            opts="-A -D -W -F -v -i -j -h --fix --allow-dirty --allow-staged --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -1218,7 +1215,7 @@ _x.py() {
             return 0
             ;;
         x.py__dist)
-            opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
+            opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -1404,7 +1401,7 @@ _x.py() {
             return 0
             ;;
         x.py__doc)
-            opts="-v -i -j -h --open --json --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
+            opts="-v -i -j -h --open --json --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -1590,7 +1587,7 @@ _x.py() {
             return 0
             ;;
         x.py__fix)
-            opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
+            opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -1776,7 +1773,7 @@ _x.py() {
             return 0
             ;;
         x.py__fmt)
-            opts="-v -i -j -h --check --all --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
+            opts="-v -i -j -h --check --all --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -1962,7 +1959,7 @@ _x.py() {
             return 0
             ;;
         x.py__install)
-            opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
+            opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -2148,7 +2145,7 @@ _x.py() {
             return 0
             ;;
         x.py__miri)
-            opts="-v -i -j -h --no-fail-fast --test-args --no-doc --doc --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
+            opts="-v -i -j -h --no-fail-fast --test-args --no-doc --doc --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -2338,7 +2335,7 @@ _x.py() {
             return 0
             ;;
         x.py__perf)
-            opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]... eprintln samply cachegrind benchmark compare"
+            opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]... eprintln samply cachegrind benchmark compare"
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -2524,7 +2521,7 @@ _x.py() {
             return 0
             ;;
         x.py__perf__benchmark)
-            opts="-v -i -j -h --include --exclude --scenarios --profiles --verbose --incremental --config --build-dir --build --host --target --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help <benchmark-id> [PATHS]... [ARGS]..."
+            opts="-v -i -j -h --include --exclude --scenarios --profiles --verbose --incremental --config --build-dir --build --host --target --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help <benchmark-id> [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -2722,7 +2719,7 @@ _x.py() {
             return 0
             ;;
         x.py__perf__cachegrind)
-            opts="-v -i -j -h --include --exclude --scenarios --profiles --verbose --incremental --config --build-dir --build --host --target --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
+            opts="-v -i -j -h --include --exclude --scenarios --profiles --verbose --incremental --config --build-dir --build --host --target --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -2920,7 +2917,7 @@ _x.py() {
             return 0
             ;;
         x.py__perf__compare)
-            opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help <BASE> <MODIFIED> [PATHS]... [ARGS]..."
+            opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help <BASE> <MODIFIED> [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -3106,7 +3103,7 @@ _x.py() {
             return 0
             ;;
         x.py__perf__eprintln)
-            opts="-v -i -j -h --include --exclude --scenarios --profiles --verbose --incremental --config --build-dir --build --host --target --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
+            opts="-v -i -j -h --include --exclude --scenarios --profiles --verbose --incremental --config --build-dir --build --host --target --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -3304,7 +3301,7 @@ _x.py() {
             return 0
             ;;
         x.py__perf__samply)
-            opts="-v -i -j -h --include --exclude --scenarios --profiles --verbose --incremental --config --build-dir --build --host --target --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
+            opts="-v -i -j -h --include --exclude --scenarios --profiles --verbose --incremental --config --build-dir --build --host --target --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -3502,7 +3499,7 @@ _x.py() {
             return 0
             ;;
         x.py__run)
-            opts="-v -i -j -h --args --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
+            opts="-v -i -j -h --args --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -3692,193 +3689,7 @@ _x.py() {
             return 0
             ;;
         x.py__setup)
-            opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [<PROFILE>|hook|editor|link] [PATHS]... [ARGS]..."
-            if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
-                COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
-                return 0
-            fi
-            case "${prev}" in
-                --config)
-                    local oldifs
-                    if [ -n "${IFS+x}" ]; then
-                        oldifs="$IFS"
-                    fi
-                    IFS=$'\n'
-                    COMPREPLY=($(compgen -f "${cur}"))
-                    if [ -n "${oldifs+x}" ]; then
-                        IFS="$oldifs"
-                    fi
-                    if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
-                        compopt -o filenames
-                    fi
-                    return 0
-                    ;;
-                --build-dir)
-                    COMPREPLY=()
-                    if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
-                        compopt -o plusdirs
-                    fi
-                    return 0
-                    ;;
-                --build)
-                    COMPREPLY=("${cur}")
-                    if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
-                        compopt -o nospace
-                    fi
-                    return 0
-                    ;;
-                --host)
-                    COMPREPLY=("${cur}")
-                    if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
-                        compopt -o nospace
-                    fi
-                    return 0
-                    ;;
-                --target)
-                    COMPREPLY=("${cur}")
-                    if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
-                        compopt -o nospace
-                    fi
-                    return 0
-                    ;;
-                --exclude)
-                    COMPREPLY=($(compgen -f "${cur}"))
-                    return 0
-                    ;;
-                --skip)
-                    COMPREPLY=($(compgen -f "${cur}"))
-                    return 0
-                    ;;
-                --rustc-error-format)
-                    COMPREPLY=("${cur}")
-                    if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
-                        compopt -o nospace
-                    fi
-                    return 0
-                    ;;
-                --on-fail)
-                    COMPREPLY=($(compgen -f "${cur}"))
-                    return 0
-                    ;;
-                --stage)
-                    COMPREPLY=("${cur}")
-                    if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
-                        compopt -o nospace
-                    fi
-                    return 0
-                    ;;
-                --keep-stage)
-                    COMPREPLY=("${cur}")
-                    if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
-                        compopt -o nospace
-                    fi
-                    return 0
-                    ;;
-                --keep-stage-std)
-                    COMPREPLY=("${cur}")
-                    if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
-                        compopt -o nospace
-                    fi
-                    return 0
-                    ;;
-                --src)
-                    COMPREPLY=()
-                    if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
-                        compopt -o plusdirs
-                    fi
-                    return 0
-                    ;;
-                --jobs)
-                    COMPREPLY=("${cur}")
-                    if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
-                        compopt -o nospace
-                    fi
-                    return 0
-                    ;;
-                -j)
-                    COMPREPLY=("${cur}")
-                    if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
-                        compopt -o nospace
-                    fi
-                    return 0
-                    ;;
-                --warnings)
-                    COMPREPLY=($(compgen -W "deny warn default" -- "${cur}"))
-                    return 0
-                    ;;
-                --color)
-                    COMPREPLY=($(compgen -W "always never auto" -- "${cur}"))
-                    return 0
-                    ;;
-                --rust-profile-generate)
-                    local oldifs
-                    if [ -n "${IFS+x}" ]; then
-                        oldifs="$IFS"
-                    fi
-                    IFS=$'\n'
-                    COMPREPLY=($(compgen -f "${cur}"))
-                    if [ -n "${oldifs+x}" ]; then
-                        IFS="$oldifs"
-                    fi
-                    if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
-                        compopt -o filenames
-                    fi
-                    return 0
-                    ;;
-                --rust-profile-use)
-                    local oldifs
-                    if [ -n "${IFS+x}" ]; then
-                        oldifs="$IFS"
-                    fi
-                    IFS=$'\n'
-                    COMPREPLY=($(compgen -f "${cur}"))
-                    if [ -n "${oldifs+x}" ]; then
-                        IFS="$oldifs"
-                    fi
-                    if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
-                        compopt -o filenames
-                    fi
-                    return 0
-                    ;;
-                --llvm-profile-use)
-                    local oldifs
-                    if [ -n "${IFS+x}" ]; then
-                        oldifs="$IFS"
-                    fi
-                    IFS=$'\n'
-                    COMPREPLY=($(compgen -f "${cur}"))
-                    if [ -n "${oldifs+x}" ]; then
-                        IFS="$oldifs"
-                    fi
-                    if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
-                        compopt -o filenames
-                    fi
-                    return 0
-                    ;;
-                --reproducible-artifact)
-                    COMPREPLY=($(compgen -f "${cur}"))
-                    return 0
-                    ;;
-                --set)
-                    COMPREPLY=("${cur}")
-                    if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
-                        compopt -o nospace
-                    fi
-                    return 0
-                    ;;
-                --ci)
-                    COMPREPLY=($(compgen -W "true false" -- "${cur}"))
-                    return 0
-                    ;;
-                *)
-                    COMPREPLY=()
-                    ;;
-            esac
-            COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
-            return 0
-            ;;
-        x.py__suggest)
-            opts="-v -i -j -h --run --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
+            opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [<PROFILE>|hook|editor|link] [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -4064,7 +3875,7 @@ _x.py() {
             return 0
             ;;
         x.py__test)
-            opts="-v -i -j -h --no-fail-fast --test-args --compiletest-rustc-args --no-doc --doc --bless --extra-checks --force-rerun --only-modified --compare-mode --pass --run --rustfix-coverage --no-capture --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
+            opts="-v -i -j -h --no-fail-fast --test-args --compiletest-rustc-args --no-doc --doc --bless --extra-checks --force-rerun --only-modified --compare-mode --pass --run --rustfix-coverage --no-capture --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -4274,7 +4085,7 @@ _x.py() {
             return 0
             ;;
         x.py__vendor)
-            opts="-v -i -j -h --sync --versioned-dirs --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
+            opts="-v -i -j -h --sync --versioned-dirs --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
diff --git a/src/etc/completions/x.py.zsh b/src/etc/completions/x.py.zsh
index 8fc4f052252..aff35b31e21 100644
--- a/src/etc/completions/x.py.zsh
+++ b/src/etc/completions/x.py.zsh
@@ -46,6 +46,7 @@ _x.py() {
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -97,6 +98,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -141,6 +143,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -191,6 +194,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -234,6 +238,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -279,6 +284,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -324,6 +330,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -338,7 +345,7 @@ _arguments "${_arguments_options[@]}" : \
 _arguments "${_arguments_options[@]}" : \
 '*--test-args=[extra arguments to be passed for the test tool being used (e.g. libtest, compiletest or rustdoc)]:ARGS:_default' \
 '*--compiletest-rustc-args=[extra options to pass the compiler when running compiletest tests]:ARGS:_default' \
-'--extra-checks=[comma-separated list of other files types to check (accepts py, py\:lint, py\:fmt, shell, shell\:lint, cpp, cpp\:fmt, spellcheck, spellcheck\:fix)]:EXTRA_CHECKS:_default' \
+'--extra-checks=[comma-separated list of other files types to check (accepts py, py\:lint, py\:fmt, shell, shell\:lint, cpp, cpp\:fmt, spellcheck)]:EXTRA_CHECKS:_default' \
 '--compare-mode=[mode describing what file the actual ui output will be compared to]:COMPARE MODE:_default' \
 '--pass=[force {check,build,run}-pass tests to this mode]:check | build | run:_default' \
 '--run=[whether to execute run-* tests]:auto | always | never:_default' \
@@ -381,6 +388,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -428,6 +436,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -472,6 +481,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -516,6 +526,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -559,6 +570,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -602,6 +614,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -646,6 +659,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -689,6 +703,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -700,50 +715,6 @@ _arguments "${_arguments_options[@]}" : \
 '*::paths -- paths for the subcommand:_files' \
 && ret=0
 ;;
-(suggest)
-_arguments "${_arguments_options[@]}" : \
-'--config=[TOML configuration file for build]:FILE:_files' \
-'--build-dir=[Build directory, overrides \`build.build-dir\` in \`bootstrap.toml\`]:DIR:_files -/' \
-'--build=[host target of the stage0 compiler]:BUILD:' \
-'--host=[host targets to build]:HOST:' \
-'--target=[target targets to build]:TARGET:' \
-'*--exclude=[build paths to exclude]:PATH:_files' \
-'*--skip=[build paths to skip]:PATH:_files' \
-'--rustc-error-format=[rustc error format]:RUSTC_ERROR_FORMAT:' \
-'--on-fail=[command to run on failure]:CMD:_cmdstring' \
-'--stage=[stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)]:N:' \
-'*--keep-stage=[stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:' \
-'*--keep-stage-std=[stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:' \
-'--src=[path to the root of the rust checkout]:DIR:_files -/' \
-'-j+[number of jobs to run in parallel]:JOBS:' \
-'--jobs=[number of jobs to run in parallel]:JOBS:' \
-'--warnings=[if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour]:deny|warn:(deny warn default)' \
-'--color=[whether to use color in cargo and rustc output]:STYLE:(always never auto)' \
-'--rust-profile-generate=[generate PGO profile with rustc build]:PROFILE:_files' \
-'--rust-profile-use=[use PGO profile for rustc build]:PROFILE:_files' \
-'--llvm-profile-use=[use PGO profile for LLVM build]:PROFILE:_files' \
-'*--reproducible-artifact=[Additional reproducible artifacts that should be added to the reproducible artifacts archive]:REPRODUCIBLE_ARTIFACT:_default' \
-'*--set=[override options in bootstrap.toml]:section.option=value:' \
-'--ci=[Make bootstrap to behave as it'\''s running on the CI environment or not]:bool:(true false)' \
-'--run[run suggested tests]' \
-'*-v[use verbose output (-vv for very verbose)]' \
-'*--verbose[use verbose output (-vv for very verbose)]' \
-'-i[use incremental compilation]' \
-'--incremental[use incremental compilation]' \
-'--include-default-paths[include default paths in addition to the provided ones]' \
-'--dry-run[dry run; don'\''t build anything]' \
-'--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
-'--json-output[use message-format=json]' \
-'--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
-'--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
-'--enable-bolt-settings[Enable BOLT link flags]' \
-'--skip-stage0-validation[Skip stage0 compiler validation]' \
-'--skip-std-check-if-no-download-rustc[Skip checking the standard library if \`rust.download-rustc\` isn'\''t available. This is mostly for RA as building the stage1 compiler to check the library tree on each code change might be too much for some computers]' \
-'-h[Print help (see more with '\''--help'\'')]' \
-'--help[Print help (see more with '\''--help'\'')]' \
-'*::paths -- paths for the subcommand:_files' \
-&& ret=0
-;;
 (vendor)
 _arguments "${_arguments_options[@]}" : \
 '*--sync=[Additional \`Cargo.toml\` to sync and vendor]:SYNC:_files' \
@@ -779,6 +750,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -822,6 +794,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -877,6 +850,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -923,6 +897,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -969,6 +944,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -1015,6 +991,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -1059,6 +1036,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -1097,7 +1075,6 @@ _x.py_commands() {
 'install:Install distribution artifacts' \
 'run:Run tools contained in this repository' \
 'setup:Set up the environment for development' \
-'suggest:Suggest a subset of tests to run, based on modified files' \
 'vendor:Vendor dependencies' \
 'perf:Perform profiling and benchmarking of the compiler using \`rustc-perf\`' \
     )
@@ -1204,11 +1181,6 @@ _x.py__setup_commands() {
     local commands; commands=()
     _describe -t commands 'x.py setup commands' commands "$@"
 }
-(( $+functions[_x.py__suggest_commands] )) ||
-_x.py__suggest_commands() {
-    local commands; commands=()
-    _describe -t commands 'x.py suggest commands' commands "$@"
-}
 (( $+functions[_x.py__test_commands] )) ||
 _x.py__test_commands() {
     local commands; commands=()
diff --git a/src/etc/completions/x.sh b/src/etc/completions/x.sh
index 105fc668e8e..927d8f7661c 100644
--- a/src/etc/completions/x.sh
+++ b/src/etc/completions/x.sh
@@ -54,9 +54,6 @@ _x() {
             x,setup)
                 cmd="x__setup"
                 ;;
-            x,suggest)
-                cmd="x__suggest"
-                ;;
             x,test)
                 cmd="x__test"
                 ;;
@@ -85,7 +82,7 @@ _x() {
 
     case "${cmd}" in
         x)
-            opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]... build check clippy fix fmt doc test miri bench clean dist install run setup suggest vendor perf"
+            opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]... build check clippy fix fmt doc test miri bench clean dist install run setup vendor perf"
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 1 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -271,7 +268,7 @@ _x() {
             return 0
             ;;
         x__bench)
-            opts="-v -i -j -h --test-args --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
+            opts="-v -i -j -h --test-args --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -461,7 +458,7 @@ _x() {
             return 0
             ;;
         x__build)
-            opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
+            opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -647,7 +644,7 @@ _x() {
             return 0
             ;;
         x__check)
-            opts="-v -i -j -h --all-targets --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
+            opts="-v -i -j -h --all-targets --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -833,7 +830,7 @@ _x() {
             return 0
             ;;
         x__clean)
-            opts="-v -i -j -h --all --stage --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
+            opts="-v -i -j -h --all --stage --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -1016,7 +1013,7 @@ _x() {
             return 0
             ;;
         x__clippy)
-            opts="-A -D -W -F -v -i -j -h --fix --allow-dirty --allow-staged --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
+            opts="-A -D -W -F -v -i -j -h --fix --allow-dirty --allow-staged --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -1218,7 +1215,7 @@ _x() {
             return 0
             ;;
         x__dist)
-            opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
+            opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -1404,7 +1401,7 @@ _x() {
             return 0
             ;;
         x__doc)
-            opts="-v -i -j -h --open --json --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
+            opts="-v -i -j -h --open --json --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -1590,7 +1587,7 @@ _x() {
             return 0
             ;;
         x__fix)
-            opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
+            opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -1776,7 +1773,7 @@ _x() {
             return 0
             ;;
         x__fmt)
-            opts="-v -i -j -h --check --all --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
+            opts="-v -i -j -h --check --all --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -1962,7 +1959,7 @@ _x() {
             return 0
             ;;
         x__install)
-            opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
+            opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -2148,7 +2145,7 @@ _x() {
             return 0
             ;;
         x__miri)
-            opts="-v -i -j -h --no-fail-fast --test-args --no-doc --doc --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
+            opts="-v -i -j -h --no-fail-fast --test-args --no-doc --doc --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -2338,7 +2335,7 @@ _x() {
             return 0
             ;;
         x__perf)
-            opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]... eprintln samply cachegrind benchmark compare"
+            opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]... eprintln samply cachegrind benchmark compare"
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -2524,7 +2521,7 @@ _x() {
             return 0
             ;;
         x__perf__benchmark)
-            opts="-v -i -j -h --include --exclude --scenarios --profiles --verbose --incremental --config --build-dir --build --host --target --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help <benchmark-id> [PATHS]... [ARGS]..."
+            opts="-v -i -j -h --include --exclude --scenarios --profiles --verbose --incremental --config --build-dir --build --host --target --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help <benchmark-id> [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -2722,7 +2719,7 @@ _x() {
             return 0
             ;;
         x__perf__cachegrind)
-            opts="-v -i -j -h --include --exclude --scenarios --profiles --verbose --incremental --config --build-dir --build --host --target --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
+            opts="-v -i -j -h --include --exclude --scenarios --profiles --verbose --incremental --config --build-dir --build --host --target --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -2920,7 +2917,7 @@ _x() {
             return 0
             ;;
         x__perf__compare)
-            opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help <BASE> <MODIFIED> [PATHS]... [ARGS]..."
+            opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help <BASE> <MODIFIED> [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -3106,7 +3103,7 @@ _x() {
             return 0
             ;;
         x__perf__eprintln)
-            opts="-v -i -j -h --include --exclude --scenarios --profiles --verbose --incremental --config --build-dir --build --host --target --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
+            opts="-v -i -j -h --include --exclude --scenarios --profiles --verbose --incremental --config --build-dir --build --host --target --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -3304,7 +3301,7 @@ _x() {
             return 0
             ;;
         x__perf__samply)
-            opts="-v -i -j -h --include --exclude --scenarios --profiles --verbose --incremental --config --build-dir --build --host --target --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
+            opts="-v -i -j -h --include --exclude --scenarios --profiles --verbose --incremental --config --build-dir --build --host --target --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 3 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -3502,7 +3499,7 @@ _x() {
             return 0
             ;;
         x__run)
-            opts="-v -i -j -h --args --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
+            opts="-v -i -j -h --args --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -3692,193 +3689,7 @@ _x() {
             return 0
             ;;
         x__setup)
-            opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [<PROFILE>|hook|editor|link] [PATHS]... [ARGS]..."
-            if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
-                COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
-                return 0
-            fi
-            case "${prev}" in
-                --config)
-                    local oldifs
-                    if [ -n "${IFS+x}" ]; then
-                        oldifs="$IFS"
-                    fi
-                    IFS=$'\n'
-                    COMPREPLY=($(compgen -f "${cur}"))
-                    if [ -n "${oldifs+x}" ]; then
-                        IFS="$oldifs"
-                    fi
-                    if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
-                        compopt -o filenames
-                    fi
-                    return 0
-                    ;;
-                --build-dir)
-                    COMPREPLY=()
-                    if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
-                        compopt -o plusdirs
-                    fi
-                    return 0
-                    ;;
-                --build)
-                    COMPREPLY=("${cur}")
-                    if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
-                        compopt -o nospace
-                    fi
-                    return 0
-                    ;;
-                --host)
-                    COMPREPLY=("${cur}")
-                    if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
-                        compopt -o nospace
-                    fi
-                    return 0
-                    ;;
-                --target)
-                    COMPREPLY=("${cur}")
-                    if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
-                        compopt -o nospace
-                    fi
-                    return 0
-                    ;;
-                --exclude)
-                    COMPREPLY=($(compgen -f "${cur}"))
-                    return 0
-                    ;;
-                --skip)
-                    COMPREPLY=($(compgen -f "${cur}"))
-                    return 0
-                    ;;
-                --rustc-error-format)
-                    COMPREPLY=("${cur}")
-                    if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
-                        compopt -o nospace
-                    fi
-                    return 0
-                    ;;
-                --on-fail)
-                    COMPREPLY=($(compgen -f "${cur}"))
-                    return 0
-                    ;;
-                --stage)
-                    COMPREPLY=("${cur}")
-                    if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
-                        compopt -o nospace
-                    fi
-                    return 0
-                    ;;
-                --keep-stage)
-                    COMPREPLY=("${cur}")
-                    if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
-                        compopt -o nospace
-                    fi
-                    return 0
-                    ;;
-                --keep-stage-std)
-                    COMPREPLY=("${cur}")
-                    if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
-                        compopt -o nospace
-                    fi
-                    return 0
-                    ;;
-                --src)
-                    COMPREPLY=()
-                    if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
-                        compopt -o plusdirs
-                    fi
-                    return 0
-                    ;;
-                --jobs)
-                    COMPREPLY=("${cur}")
-                    if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
-                        compopt -o nospace
-                    fi
-                    return 0
-                    ;;
-                -j)
-                    COMPREPLY=("${cur}")
-                    if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
-                        compopt -o nospace
-                    fi
-                    return 0
-                    ;;
-                --warnings)
-                    COMPREPLY=($(compgen -W "deny warn default" -- "${cur}"))
-                    return 0
-                    ;;
-                --color)
-                    COMPREPLY=($(compgen -W "always never auto" -- "${cur}"))
-                    return 0
-                    ;;
-                --rust-profile-generate)
-                    local oldifs
-                    if [ -n "${IFS+x}" ]; then
-                        oldifs="$IFS"
-                    fi
-                    IFS=$'\n'
-                    COMPREPLY=($(compgen -f "${cur}"))
-                    if [ -n "${oldifs+x}" ]; then
-                        IFS="$oldifs"
-                    fi
-                    if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
-                        compopt -o filenames
-                    fi
-                    return 0
-                    ;;
-                --rust-profile-use)
-                    local oldifs
-                    if [ -n "${IFS+x}" ]; then
-                        oldifs="$IFS"
-                    fi
-                    IFS=$'\n'
-                    COMPREPLY=($(compgen -f "${cur}"))
-                    if [ -n "${oldifs+x}" ]; then
-                        IFS="$oldifs"
-                    fi
-                    if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
-                        compopt -o filenames
-                    fi
-                    return 0
-                    ;;
-                --llvm-profile-use)
-                    local oldifs
-                    if [ -n "${IFS+x}" ]; then
-                        oldifs="$IFS"
-                    fi
-                    IFS=$'\n'
-                    COMPREPLY=($(compgen -f "${cur}"))
-                    if [ -n "${oldifs+x}" ]; then
-                        IFS="$oldifs"
-                    fi
-                    if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
-                        compopt -o filenames
-                    fi
-                    return 0
-                    ;;
-                --reproducible-artifact)
-                    COMPREPLY=($(compgen -f "${cur}"))
-                    return 0
-                    ;;
-                --set)
-                    COMPREPLY=("${cur}")
-                    if [[ "${BASH_VERSINFO[0]}" -ge 4 ]]; then
-                        compopt -o nospace
-                    fi
-                    return 0
-                    ;;
-                --ci)
-                    COMPREPLY=($(compgen -W "true false" -- "${cur}"))
-                    return 0
-                    ;;
-                *)
-                    COMPREPLY=()
-                    ;;
-            esac
-            COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
-            return 0
-            ;;
-        x__suggest)
-            opts="-v -i -j -h --run --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
+            opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [<PROFILE>|hook|editor|link] [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -4064,7 +3875,7 @@ _x() {
             return 0
             ;;
         x__test)
-            opts="-v -i -j -h --no-fail-fast --test-args --compiletest-rustc-args --no-doc --doc --bless --extra-checks --force-rerun --only-modified --compare-mode --pass --run --rustfix-coverage --no-capture --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
+            opts="-v -i -j -h --no-fail-fast --test-args --compiletest-rustc-args --no-doc --doc --bless --extra-checks --force-rerun --only-modified --compare-mode --pass --run --rustfix-coverage --no-capture --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
@@ -4274,7 +4085,7 @@ _x() {
             return 0
             ;;
         x__vendor)
-            opts="-v -i -j -h --sync --versioned-dirs --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
+            opts="-v -i -j -h --sync --versioned-dirs --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --json-output --compile-time-deps --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --ci --skip-std-check-if-no-download-rustc --help [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
diff --git a/src/etc/completions/x.zsh b/src/etc/completions/x.zsh
index c495e8318ba..28ad00f3a0d 100644
--- a/src/etc/completions/x.zsh
+++ b/src/etc/completions/x.zsh
@@ -46,6 +46,7 @@ _x() {
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -97,6 +98,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -141,6 +143,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -191,6 +194,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -234,6 +238,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -279,6 +284,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -324,6 +330,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -338,7 +345,7 @@ _arguments "${_arguments_options[@]}" : \
 _arguments "${_arguments_options[@]}" : \
 '*--test-args=[extra arguments to be passed for the test tool being used (e.g. libtest, compiletest or rustdoc)]:ARGS:_default' \
 '*--compiletest-rustc-args=[extra options to pass the compiler when running compiletest tests]:ARGS:_default' \
-'--extra-checks=[comma-separated list of other files types to check (accepts py, py\:lint, py\:fmt, shell, shell\:lint, cpp, cpp\:fmt, spellcheck, spellcheck\:fix)]:EXTRA_CHECKS:_default' \
+'--extra-checks=[comma-separated list of other files types to check (accepts py, py\:lint, py\:fmt, shell, shell\:lint, cpp, cpp\:fmt, spellcheck)]:EXTRA_CHECKS:_default' \
 '--compare-mode=[mode describing what file the actual ui output will be compared to]:COMPARE MODE:_default' \
 '--pass=[force {check,build,run}-pass tests to this mode]:check | build | run:_default' \
 '--run=[whether to execute run-* tests]:auto | always | never:_default' \
@@ -381,6 +388,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -428,6 +436,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -472,6 +481,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -516,6 +526,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -559,6 +570,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -602,6 +614,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -646,6 +659,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -689,6 +703,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -700,50 +715,6 @@ _arguments "${_arguments_options[@]}" : \
 '*::paths -- paths for the subcommand:_files' \
 && ret=0
 ;;
-(suggest)
-_arguments "${_arguments_options[@]}" : \
-'--config=[TOML configuration file for build]:FILE:_files' \
-'--build-dir=[Build directory, overrides \`build.build-dir\` in \`bootstrap.toml\`]:DIR:_files -/' \
-'--build=[host target of the stage0 compiler]:BUILD:' \
-'--host=[host targets to build]:HOST:' \
-'--target=[target targets to build]:TARGET:' \
-'*--exclude=[build paths to exclude]:PATH:_files' \
-'*--skip=[build paths to skip]:PATH:_files' \
-'--rustc-error-format=[rustc error format]:RUSTC_ERROR_FORMAT:' \
-'--on-fail=[command to run on failure]:CMD:_cmdstring' \
-'--stage=[stage to build (indicates compiler to use/test, e.g., stage 0 uses the bootstrap compiler, stage 1 the stage 0 rustc artifacts, etc.)]:N:' \
-'*--keep-stage=[stage(s) to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:' \
-'*--keep-stage-std=[stage(s) of the standard library to keep without recompiling (pass multiple times to keep e.g., both stages 0 and 1)]:N:' \
-'--src=[path to the root of the rust checkout]:DIR:_files -/' \
-'-j+[number of jobs to run in parallel]:JOBS:' \
-'--jobs=[number of jobs to run in parallel]:JOBS:' \
-'--warnings=[if value is deny, will deny warnings if value is warn, will emit warnings otherwise, use the default configured behaviour]:deny|warn:(deny warn default)' \
-'--color=[whether to use color in cargo and rustc output]:STYLE:(always never auto)' \
-'--rust-profile-generate=[generate PGO profile with rustc build]:PROFILE:_files' \
-'--rust-profile-use=[use PGO profile for rustc build]:PROFILE:_files' \
-'--llvm-profile-use=[use PGO profile for LLVM build]:PROFILE:_files' \
-'*--reproducible-artifact=[Additional reproducible artifacts that should be added to the reproducible artifacts archive]:REPRODUCIBLE_ARTIFACT:_default' \
-'*--set=[override options in bootstrap.toml]:section.option=value:' \
-'--ci=[Make bootstrap to behave as it'\''s running on the CI environment or not]:bool:(true false)' \
-'--run[run suggested tests]' \
-'*-v[use verbose output (-vv for very verbose)]' \
-'*--verbose[use verbose output (-vv for very verbose)]' \
-'-i[use incremental compilation]' \
-'--incremental[use incremental compilation]' \
-'--include-default-paths[include default paths in addition to the provided ones]' \
-'--dry-run[dry run; don'\''t build anything]' \
-'--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
-'--json-output[use message-format=json]' \
-'--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
-'--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
-'--enable-bolt-settings[Enable BOLT link flags]' \
-'--skip-stage0-validation[Skip stage0 compiler validation]' \
-'--skip-std-check-if-no-download-rustc[Skip checking the standard library if \`rust.download-rustc\` isn'\''t available. This is mostly for RA as building the stage1 compiler to check the library tree on each code change might be too much for some computers]' \
-'-h[Print help (see more with '\''--help'\'')]' \
-'--help[Print help (see more with '\''--help'\'')]' \
-'*::paths -- paths for the subcommand:_files' \
-&& ret=0
-;;
 (vendor)
 _arguments "${_arguments_options[@]}" : \
 '*--sync=[Additional \`Cargo.toml\` to sync and vendor]:SYNC:_files' \
@@ -779,6 +750,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -822,6 +794,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -877,6 +850,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -923,6 +897,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -969,6 +944,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -1015,6 +991,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -1059,6 +1036,7 @@ _arguments "${_arguments_options[@]}" : \
 '--dry-run[dry run; don'\''t build anything]' \
 '--dump-bootstrap-shims[Indicates whether to dump the work done from bootstrap shims]' \
 '--json-output[use message-format=json]' \
+'--compile-time-deps[only build proc-macros and build scripts (for rust-analyzer)]' \
 '--bypass-bootstrap-lock[Bootstrap uses this value to decide whether it should bypass locking the build process. This is rarely needed (e.g., compiling the std library for different targets in parallel)]' \
 '--llvm-profile-generate[generate PGO profile with llvm built for rustc]' \
 '--enable-bolt-settings[Enable BOLT link flags]' \
@@ -1097,7 +1075,6 @@ _x_commands() {
 'install:Install distribution artifacts' \
 'run:Run tools contained in this repository' \
 'setup:Set up the environment for development' \
-'suggest:Suggest a subset of tests to run, based on modified files' \
 'vendor:Vendor dependencies' \
 'perf:Perform profiling and benchmarking of the compiler using \`rustc-perf\`' \
     )
@@ -1204,11 +1181,6 @@ _x__setup_commands() {
     local commands; commands=()
     _describe -t commands 'x setup commands' commands "$@"
 }
-(( $+functions[_x__suggest_commands] )) ||
-_x__suggest_commands() {
-    local commands; commands=()
-    _describe -t commands 'x suggest commands' commands "$@"
-}
 (( $+functions[_x__test_commands] )) ||
 _x__test_commands() {
     local commands; commands=()
diff --git a/src/etc/htmldocck.py b/src/etc/htmldocck.py
index 1806e2be9bb..72975cc6206 100755
--- a/src/etc/htmldocck.py
+++ b/src/etc/htmldocck.py
@@ -147,48 +147,17 @@ def concat_multi_lines(f):
         print_err(lineno, line, "Trailing backslash at the end of the file")
 
 
-def get_known_directive_names():
-    def filter_line(line):
-        line = line.strip()
-        return line.startswith('"') and (line.endswith('",') or line.endswith('"'))
-
-    # Equivalent to `src/tools/compiletest/src/header.rs` constant of the same name.
-    with open(
-        os.path.join(
-            # We go back to `src`.
-            os.path.dirname(os.path.dirname(__file__)),
-            "tools/compiletest/src/directive-list.rs",
-        ),
-        "r",
-        encoding="utf8",
-    ) as fd:
-        content = fd.read()
-        return [
-            line.strip().replace('",', "").replace('"', "")
-            for line in content.split("\n")
-            if filter_line(line)
-        ]
-
-
-# To prevent duplicating the list of commmands between `compiletest` and `htmldocck`, we put
-# it into a common file which is included in rust code and parsed here.
-# FIXME: This setup is temporary until we figure out how to improve this situation.
-#        See <https://github.com/rust-lang/rust/issues/125813#issuecomment-2141953780>.
-KNOWN_DIRECTIVE_NAMES = get_known_directive_names()
-
 LINE_PATTERN = re.compile(
     r"""
     //@\s+
-    (?P<negated>!?)(?P<cmd>[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)
-    (?P<args>.*)$
+    (?P<negated>!?)(?P<cmd>.+?)
+    (?:[\s:](?P<args>.*))?$
 """,
     re.X | re.UNICODE,
 )
 
 DEPRECATED_LINE_PATTERN = re.compile(
-    r"""
-    //\s+@
-""",
+    r"//\s+@",
     re.X | re.UNICODE,
 )
 
@@ -209,12 +178,7 @@ def get_commands(template):
 
             cmd = m.group("cmd")
             negated = m.group("negated") == "!"
-            if not negated and cmd in KNOWN_DIRECTIVE_NAMES:
-                continue
-            args = m.group("args")
-            if args and not args[:1].isspace():
-                print_err(lineno, line, "Invalid template syntax")
-                continue
+            args = m.group("args") or ""
             try:
                 args = shlex.split(args)
             except UnicodeEncodeError:
@@ -564,10 +528,14 @@ def check_command(c, cache):
             # hasraw/matchesraw <path> <pat> = string test
             elif len(c.args) == 2 and "raw" in c.cmd:
                 cerr = "`PATTERN` did not match"
+                if c.negated:
+                    cerr = "`PATTERN` unexpectedly matched"
                 ret = check_string(cache.get_file(c.args[0]), c.args[1], regexp)
             # has/matches <path> <pat> <match> = XML tree test
             elif len(c.args) == 3 and "raw" not in c.cmd:
                 cerr = "`XPATH PATTERN` did not match"
+                if c.negated:
+                    cerr = "`XPATH PATTERN` unexpectedly matched"
                 ret = get_nb_matching_elements(cache, c, regexp, True) != 0
             else:
                 raise InvalidCheck("Invalid number of {} arguments".format(c.cmd))
@@ -632,14 +600,11 @@ def check_command(c, cache):
             else:
                 raise InvalidCheck("Invalid number of {} arguments".format(c.cmd))
 
-        elif c.cmd == "valid-html":
-            raise InvalidCheck("Unimplemented valid-html")
-
-        elif c.cmd == "valid-links":
-            raise InvalidCheck("Unimplemented valid-links")
-
         else:
-            raise InvalidCheck("Unrecognized {}".format(c.cmd))
+            # Ignore unknown directives as they might be compiletest directives
+            # since they share the same `//@` prefix by convention. In any case,
+            # compiletest rejects unknown directives for us.
+            return
 
         if ret == c.negated:
             raise FailedCheck(cerr)
diff --git a/src/etc/rust_analyzer_eglot.el b/src/etc/rust_analyzer_eglot.el
index 3cb229cd98c..3151cb1a6e7 100644
--- a/src/etc/rust_analyzer_eglot.el
+++ b/src/etc/rust_analyzer_eglot.el
@@ -23,7 +23,8 @@
                                                   :overrideCommand ["python3"
                                                                     "x.py"
                                                                     "check"
-                                                                    "--json-output"])
+                                                                    "--json-output"
+                                                                    "--compile-time-deps"])]
                                         :sysrootSrc "./library"
                                         :extraEnv (:RUSTC_BOOTSTRAP "1"))
                  :rustc ( :source "./Cargo.toml" )))))))
diff --git a/src/etc/rust_analyzer_helix.toml b/src/etc/rust_analyzer_helix.toml
index 1a6a14991ec..8c1782a1abc 100644
--- a/src/etc/rust_analyzer_helix.toml
+++ b/src/etc/rust_analyzer_helix.toml
@@ -59,4 +59,5 @@ overrideCommand = [
     "--json-output",
     "--build-dir",
     "build/rust-analyzer",
+    "--compile-time-deps"
 ]
diff --git a/src/etc/rust_analyzer_settings.json b/src/etc/rust_analyzer_settings.json
index a960cc01732..b31169857c5 100644
--- a/src/etc/rust_analyzer_settings.json
+++ b/src/etc/rust_analyzer_settings.json
@@ -27,7 +27,8 @@
         "python3",
         "x.py",
         "check",
-        "--json-output"
+        "--json-output",
+        "--compile-time-deps"
     ],
     "rust-analyzer.cargo.sysrootSrc": "./library",
     "rust-analyzer.rustc.source": "./Cargo.toml",
diff --git a/src/etc/rust_analyzer_zed.json b/src/etc/rust_analyzer_zed.json
index 27fc524e9b5..7eace92500e 100644
--- a/src/etc/rust_analyzer_zed.json
+++ b/src/etc/rust_analyzer_zed.json
@@ -7,7 +7,7 @@
             "enable": true,
             "invocationLocation": "root",
             "invocationStrategy": "once",
-            "overrideCommand": ["python3", "x.py", "check", "--json-output"]
+            "overrideCommand": ["python3", "x.py", "check", "--json-output", "--compile-time-deps"]
           },
           "extraEnv": {
             "RUSTC_BOOTSTRAP": "1"
diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs
index 96199cb972a..9603399f235 100644
--- a/src/librustdoc/clean/inline.rs
+++ b/src/librustdoc/clean/inline.rs
@@ -152,8 +152,14 @@ pub(crate) fn try_inline(
     };
 
     cx.inlined.insert(did.into());
-    let mut item =
-        crate::clean::generate_item_with_correct_attrs(cx, kind, did, name, import_def_id, None);
+    let mut item = crate::clean::generate_item_with_correct_attrs(
+        cx,
+        kind,
+        did,
+        name,
+        import_def_id.as_slice(),
+        None,
+    );
     // The visibility needs to reflect the one from the reexport and not from the "source" DefId.
     item.inner.inline_stmt_id = import_def_id;
     ret.push(item);
@@ -493,7 +499,7 @@ pub(crate) fn build_impl(
             impl_
                 .items
                 .iter()
-                .map(|item| tcx.hir_impl_item(item.id))
+                .map(|&item| tcx.hir_impl_item(item))
                 .filter(|item| {
                     // Filter out impl items whose corresponding trait item has `doc(hidden)`
                     // not to document such impl items.
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index c2f3da18cd3..1265a39d27b 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -94,12 +94,12 @@ pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext<
     // This covers the case where somebody does an import which should pull in an item,
     // but there's already an item with the same namespace and same name. Rust gives
     // priority to the not-imported one, so we should, too.
-    items.extend(doc.items.values().flat_map(|(item, renamed, import_id)| {
+    items.extend(doc.items.values().flat_map(|(item, renamed, import_ids)| {
         // First, lower everything other than glob imports.
         if matches!(item.kind, hir::ItemKind::Use(_, hir::UseKind::Glob)) {
             return Vec::new();
         }
-        let v = clean_maybe_renamed_item(cx, item, *renamed, *import_id);
+        let v = clean_maybe_renamed_item(cx, item, *renamed, import_ids);
         for item in &v {
             if let Some(name) = item.name
                 && (cx.render_options.document_hidden || !item.is_doc_hidden())
@@ -162,7 +162,7 @@ pub(crate) fn clean_doc_module<'tcx>(doc: &DocModule<'tcx>, cx: &mut DocContext<
         kind,
         doc.def_id.to_def_id(),
         doc.name,
-        doc.import_id,
+        doc.import_id.as_slice(),
         doc.renamed,
     )
 }
@@ -182,22 +182,29 @@ fn generate_item_with_correct_attrs(
     kind: ItemKind,
     def_id: DefId,
     name: Symbol,
-    import_id: Option<LocalDefId>,
+    import_ids: &[LocalDefId],
     renamed: Option<Symbol>,
 ) -> Item {
     let target_attrs = inline::load_attrs(cx, def_id);
-    let attrs = if let Some(import_id) = import_id {
-        // glob reexports are treated the same as `#[doc(inline)]` items.
-        //
-        // For glob re-exports the item may or may not exist to be re-exported (potentially the cfgs
-        // on the path up until the glob can be removed, and only cfgs on the globbed item itself
-        // matter), for non-inlined re-exports see #85043.
-        let is_inline = hir_attr_lists(inline::load_attrs(cx, import_id.to_def_id()), sym::doc)
-            .get_word_attr(sym::inline)
-            .is_some()
-            || (is_glob_import(cx.tcx, import_id)
-                && (cx.render_options.document_hidden || !cx.tcx.is_doc_hidden(def_id)));
-        let mut attrs = get_all_import_attributes(cx, import_id, def_id, is_inline);
+    let attrs = if !import_ids.is_empty() {
+        let mut attrs = Vec::with_capacity(import_ids.len());
+        let mut is_inline = false;
+
+        for import_id in import_ids.iter().copied() {
+            // glob reexports are treated the same as `#[doc(inline)]` items.
+            //
+            // For glob re-exports the item may or may not exist to be re-exported (potentially the
+            // cfgs on the path up until the glob can be removed, and only cfgs on the globbed item
+            // itself matter), for non-inlined re-exports see #85043.
+            let import_is_inline =
+                hir_attr_lists(inline::load_attrs(cx, import_id.to_def_id()), sym::doc)
+                    .get_word_attr(sym::inline)
+                    .is_some()
+                    || (is_glob_import(cx.tcx, import_id)
+                        && (cx.render_options.document_hidden || !cx.tcx.is_doc_hidden(def_id)));
+            attrs.extend(get_all_import_attributes(cx, import_id, def_id, is_inline));
+            is_inline = is_inline || import_is_inline;
+        }
         add_without_unwanted_attributes(&mut attrs, target_attrs, is_inline, None);
         attrs
     } else {
@@ -216,7 +223,8 @@ fn generate_item_with_correct_attrs(
 
     let name = renamed.or(Some(name));
     let mut item = Item::from_def_id_and_attrs_and_parts(def_id, name, kind, attrs, cfg);
-    item.inner.inline_stmt_id = import_id;
+    // FIXME (GuillaumeGomez): Should we also make `inline_stmt_id` a `Vec` instead of an `Option`?
+    item.inner.inline_stmt_id = import_ids.first().copied();
     item
 }
 
@@ -382,7 +390,8 @@ pub(crate) fn clean_predicate<'tcx>(
         ty::ClauseKind::ConstEvaluatable(..)
         | ty::ClauseKind::WellFormed(..)
         | ty::ClauseKind::ConstArgHasType(..)
-        // FIXME(const_trait_impl): We can probably use this `HostEffect` pred to render `[const]`.
+        | ty::ClauseKind::UnstableFeature(..)
+        // FIXME(const_trait_impl): We can probably use this `HostEffect` pred to render `~const`.
         | ty::ClauseKind::HostEffect(_) => None,
     }
 }
@@ -2754,7 +2763,7 @@ fn clean_maybe_renamed_item<'tcx>(
     cx: &mut DocContext<'tcx>,
     item: &hir::Item<'tcx>,
     renamed: Option<Symbol>,
-    import_id: Option<LocalDefId>,
+    import_ids: &[LocalDefId],
 ) -> Vec<Item> {
     use hir::ItemKind;
     fn get_name(
@@ -2825,7 +2834,7 @@ fn clean_maybe_renamed_item<'tcx>(
                     })),
                     item.owner_id.def_id.to_def_id(),
                     name,
-                    import_id,
+                    import_ids,
                     renamed,
                 ));
                 return ret;
@@ -2856,10 +2865,10 @@ fn clean_maybe_renamed_item<'tcx>(
             ItemKind::Fn { ref sig, generics, body: body_id, .. } => {
                 clean_fn_or_proc_macro(item, sig, generics, body_id, &mut name, cx)
             }
-            ItemKind::Trait(_, _, _, generics, bounds, item_ids) => {
+            ItemKind::Trait(_, _, _, _, generics, bounds, item_ids) => {
                 let items = item_ids
                     .iter()
-                    .map(|ti| clean_trait_item(cx.tcx.hir_trait_item(ti.id), cx))
+                    .map(|&ti| clean_trait_item(cx.tcx.hir_trait_item(ti), cx))
                     .collect();
 
                 TraitItem(Box::new(Trait {
@@ -2880,7 +2889,7 @@ fn clean_maybe_renamed_item<'tcx>(
             kind,
             item.owner_id.def_id.to_def_id(),
             name,
-            import_id,
+            import_ids,
             renamed,
         )]
     })
@@ -2902,7 +2911,7 @@ fn clean_impl<'tcx>(
     let items = impl_
         .items
         .iter()
-        .map(|ii| clean_impl_item(tcx.hir_impl_item(ii.id), cx))
+        .map(|&ii| clean_impl_item(tcx.hir_impl_item(ii), cx))
         .collect::<Vec<_>>();
 
     // If this impl block is an implementation of the Deref trait, then we
@@ -3151,7 +3160,7 @@ fn clean_maybe_renamed_foreign_item<'tcx>(
             kind,
             item.owner_id.def_id.to_def_id(),
             item.ident.name,
-            import_id,
+            import_id.as_slice(),
             renamed,
         )
     })
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index a05aab22f1e..09647492d93 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -648,7 +648,7 @@ impl Item {
             let sig = tcx.fn_sig(def_id).skip_binder();
             let constness = if tcx.is_const_fn(def_id) {
                 // rustc's `is_const_fn` returns `true` for associated functions that have an `impl const` parent
-                // or that have a `#[const_trait]` parent. Do not display those as `const` in rustdoc because we
+                // or that have a `const trait` parent. Do not display those as `const` in rustdoc because we
                 // won't be printing correct syntax plus the syntax is unstable.
                 match tcx.opt_associated_item(def_id) {
                     Some(ty::AssocItem {
@@ -759,76 +759,48 @@ impl Item {
         Some(tcx.visibility(def_id))
     }
 
-    fn attributes_without_repr(&self, tcx: TyCtxt<'_>, is_json: bool) -> Vec<String> {
-        const ALLOWED_ATTRIBUTES: &[Symbol] =
-            &[sym::export_name, sym::link_section, sym::no_mangle, sym::non_exhaustive];
+    /// Get a list of attributes excluding `#[repr]` to display.
+    ///
+    /// Only used by the HTML output-format.
+    fn attributes_without_repr(&self) -> Vec<String> {
         self.attrs
             .other_attrs
             .iter()
-            .filter_map(|attr| {
-                if let hir::Attribute::Parsed(AttributeKind::LinkSection { name, .. }) = attr {
+            .filter_map(|attr| match attr {
+                hir::Attribute::Parsed(AttributeKind::LinkSection { name, .. }) => {
                     Some(format!("#[link_section = \"{name}\"]"))
                 }
-                // NoMangle is special cased, as it appears in HTML output, and we want to show it in source form, not HIR printing.
-                // It is also used by cargo-semver-checks.
-                else if let hir::Attribute::Parsed(AttributeKind::NoMangle(..)) = attr {
+                hir::Attribute::Parsed(AttributeKind::NoMangle(..)) => {
                     Some("#[no_mangle]".to_string())
-                } else if let hir::Attribute::Parsed(AttributeKind::ExportName { name, .. }) = attr
-                {
+                }
+                hir::Attribute::Parsed(AttributeKind::ExportName { name, .. }) => {
                     Some(format!("#[export_name = \"{name}\"]"))
-                } else if let hir::Attribute::Parsed(AttributeKind::NonExhaustive(..)) = attr {
+                }
+                hir::Attribute::Parsed(AttributeKind::NonExhaustive(..)) => {
                     Some("#[non_exhaustive]".to_string())
-                } else if is_json {
-                    match attr {
-                        // rustdoc-json stores this in `Item::deprecation`, so we
-                        // don't want it it `Item::attrs`.
-                        hir::Attribute::Parsed(AttributeKind::Deprecation { .. }) => None,
-                        // We have separate pretty-printing logic for `#[repr(..)]` attributes.
-                        hir::Attribute::Parsed(AttributeKind::Repr { .. }) => None,
-                        // target_feature is special-cased because cargo-semver-checks uses it
-                        hir::Attribute::Parsed(AttributeKind::TargetFeature(features, _)) => {
-                            let mut output = String::new();
-                            for (i, (feature, _)) in features.iter().enumerate() {
-                                if i != 0 {
-                                    output.push_str(", ");
-                                }
-                                output.push_str(&format!("enable=\"{}\"", feature.as_str()));
-                            }
-                            Some(format!("#[target_feature({output})]"))
-                        }
-                        _ => Some({
-                            let mut s = rustc_hir_pretty::attribute_to_string(&tcx, attr);
-                            assert_eq!(s.pop(), Some('\n'));
-                            s
-                        }),
-                    }
-                } else {
-                    if !attr.has_any_name(ALLOWED_ATTRIBUTES) {
-                        return None;
-                    }
-                    Some(
-                        rustc_hir_pretty::attribute_to_string(&tcx, attr)
-                            .replace("\\\n", "")
-                            .replace('\n', "")
-                            .replace("  ", " "),
-                    )
                 }
+                _ => None,
             })
             .collect()
     }
 
-    pub(crate) fn attributes(&self, tcx: TyCtxt<'_>, cache: &Cache, is_json: bool) -> Vec<String> {
-        let mut attrs = self.attributes_without_repr(tcx, is_json);
+    /// Get a list of attributes to display on this item.
+    ///
+    /// Only used by the HTML output-format.
+    pub(crate) fn attributes(&self, tcx: TyCtxt<'_>, cache: &Cache) -> Vec<String> {
+        let mut attrs = self.attributes_without_repr();
 
-        if let Some(repr_attr) = self.repr(tcx, cache, is_json) {
+        if let Some(repr_attr) = self.repr(tcx, cache) {
             attrs.push(repr_attr);
         }
         attrs
     }
 
     /// Returns a stringified `#[repr(...)]` attribute.
-    pub(crate) fn repr(&self, tcx: TyCtxt<'_>, cache: &Cache, is_json: bool) -> Option<String> {
-        repr_attributes(tcx, cache, self.def_id()?, self.type_(), is_json)
+    ///
+    /// Only used by the HTML output-format.
+    pub(crate) fn repr(&self, tcx: TyCtxt<'_>, cache: &Cache) -> Option<String> {
+        repr_attributes(tcx, cache, self.def_id()?, self.type_())
     }
 
     pub fn is_doc_hidden(&self) -> bool {
@@ -840,12 +812,14 @@ impl Item {
     }
 }
 
+/// Return a string representing the `#[repr]` attribute if present.
+///
+/// Only used by the HTML output-format.
 pub(crate) fn repr_attributes(
     tcx: TyCtxt<'_>,
     cache: &Cache,
     def_id: DefId,
     item_type: ItemType,
-    is_json: bool,
 ) -> Option<String> {
     use rustc_abi::IntegerType;
 
@@ -862,7 +836,6 @@ pub(crate) fn repr_attributes(
         // Render `repr(transparent)` iff the non-1-ZST field is public or at least one
         // field is public in case all fields are 1-ZST fields.
         let render_transparent = cache.document_private
-            || is_json
             || adt
                 .all_fields()
                 .find(|field| {
diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs
index bf3f7607274..fd1b17b6476 100644
--- a/src/librustdoc/clean/utils.rs
+++ b/src/librustdoc/clean/utils.rs
@@ -3,6 +3,7 @@ use std::fmt::{self, Display, Write as _};
 use std::sync::LazyLock as Lazy;
 use std::{ascii, mem};
 
+use rustc_ast::join_path_idents;
 use rustc_ast::tokenstream::TokenTree;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
@@ -24,7 +25,7 @@ use crate::clean::{
     clean_middle_ty, inline,
 };
 use crate::core::DocContext;
-use crate::display::{Joined as _, MaybeDisplay as _};
+use crate::display::Joined as _;
 
 #[cfg(test)]
 mod tests;
@@ -251,13 +252,7 @@ pub(crate) fn qpath_to_string(p: &hir::QPath<'_>) -> String {
         hir::QPath::LangItem(lang_item, ..) => return lang_item.name().to_string(),
     };
 
-    fmt::from_fn(|f| {
-        segments
-            .iter()
-            .map(|seg| (seg.ident.name != kw::PathRoot).then_some(seg.ident).maybe_display())
-            .joined("::", f)
-    })
-    .to_string()
+    join_path_idents(segments.iter().map(|seg| seg.ident))
 }
 
 pub(crate) fn build_deref_target_impls(
diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs
index 4989bd718c9..5191120ebdb 100644
--- a/src/librustdoc/formats/cache.rs
+++ b/src/librustdoc/formats/cache.rs
@@ -1,5 +1,6 @@
 use std::mem;
 
+use rustc_ast::join_path_syms;
 use rustc_attr_data_structures::StabilityLevel;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet};
@@ -13,7 +14,6 @@ use crate::core::DocContext;
 use crate::fold::DocFolder;
 use crate::formats::Impl;
 use crate::formats::item_type::ItemType;
-use crate::html::format::join_with_double_colon;
 use crate::html::markdown::short_markdown_summary;
 use crate::html::render::IndexItem;
 use crate::html::render::search_index::get_function_type_for_search;
@@ -558,7 +558,7 @@ fn add_item_to_search_index(tcx: TyCtxt<'_>, cache: &mut Cache, item: &clean::It
         clean::ItemKind::ImportItem(import) => import.source.did.unwrap_or(item_def_id),
         _ => item_def_id,
     };
-    let path = join_with_double_colon(parent_path);
+    let path = join_path_syms(parent_path);
     let impl_id = if let Some(ParentStackItem::Impl { item_id, .. }) = cache.parent_stack.last() {
         item_id.as_def_id()
     } else {
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index bcb3e57c844..b16485107a0 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -14,6 +14,7 @@ use std::slice;
 
 use itertools::{Either, Itertools};
 use rustc_abi::ExternAbi;
+use rustc_ast::join_path_syms;
 use rustc_attr_data_structures::{ConstStability, StabilityLevel, StableSince};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir as hir;
@@ -25,7 +26,7 @@ use rustc_span::symbol::kw;
 use rustc_span::{Symbol, sym};
 use tracing::{debug, trace};
 
-use super::url_parts_builder::{UrlPartsBuilder, estimate_item_path_byte_length};
+use super::url_parts_builder::UrlPartsBuilder;
 use crate::clean::types::ExternalLocation;
 use crate::clean::utils::find_nearest_parent_module;
 use crate::clean::{self, ExternalCrate, PrimitiveType};
@@ -369,18 +370,6 @@ pub(crate) enum HrefError {
     NotInExternalCache,
 }
 
-// Panics if `syms` is empty.
-pub(crate) fn join_with_double_colon(syms: &[Symbol]) -> String {
-    let mut s = String::with_capacity(estimate_item_path_byte_length(syms.len()));
-    // NOTE: using `Joined::joined` here causes a noticeable perf regression
-    s.push_str(syms[0].as_str());
-    for sym in &syms[1..] {
-        s.push_str("::");
-        s.push_str(sym.as_str());
-    }
-    s
-}
-
 /// This function is to get the external macro path because they are not in the cache used in
 /// `href_with_root_path`.
 fn generate_macro_def_id_path(
@@ -672,7 +661,7 @@ pub(crate) fn link_tooltip(
             write!(f, "{}", cx.tcx().item_name(id))?;
         } else if !fqp.is_empty() {
             write!(f, "{shortty} ")?;
-            fqp.iter().joined("::", f)?;
+            write!(f, "{}", join_path_syms(fqp))?;
         }
         Ok(())
     })
@@ -703,7 +692,7 @@ fn resolved_path(
                     write!(
                         f,
                         "{path}::{anchor}",
-                        path = join_with_double_colon(&fqp[..fqp.len() - 1]),
+                        path = join_path_syms(&fqp[..fqp.len() - 1]),
                         anchor = print_anchor(did, *fqp.last().unwrap(), cx)
                     )
                 } else {
@@ -835,7 +824,7 @@ pub(crate) fn print_anchor(did: DefId, text: Symbol, cx: &Context<'_>) -> impl D
             write!(
                 f,
                 r#"<a class="{short_ty}" href="{url}" title="{short_ty} {path}">{text}</a>"#,
-                path = join_with_double_colon(&fqp),
+                path = join_path_syms(fqp),
                 text = EscapeBodyText(text.as_str()),
             )
         } else {
@@ -1095,7 +1084,7 @@ impl clean::QPathData {
                                     title=\"type {path}::{name}\">{name}</a>",
                         shortty = ItemType::AssocType,
                         name = assoc.name,
-                        path = join_with_double_colon(&path),
+                        path = join_path_syms(path),
                     )
                 } else {
                     write!(f, "{}", assoc.name)
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index 3b4dae841ee..7b814701a73 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -6,6 +6,7 @@ use std::path::{Path, PathBuf};
 use std::sync::mpsc::{Receiver, channel};
 
 use askama::Template;
+use rustc_ast::join_path_syms;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_hir::def_id::{DefIdMap, LOCAL_CRATE};
 use rustc_middle::ty::TyCtxt;
@@ -27,7 +28,6 @@ use crate::formats::FormatRenderer;
 use crate::formats::cache::Cache;
 use crate::formats::item_type::ItemType;
 use crate::html::escape::Escape;
-use crate::html::format::join_with_double_colon;
 use crate::html::markdown::{self, ErrorCodes, IdMap, plain_text_summary};
 use crate::html::render::write_shared::write_shared;
 use crate::html::url_parts_builder::UrlPartsBuilder;
@@ -211,7 +211,7 @@ impl<'tcx> Context<'tcx> {
                 title.push_str(" in ");
             }
             // No need to include the namespace for primitive types and keywords
-            title.push_str(&join_with_double_colon(&self.current));
+            title.push_str(&join_path_syms(&self.current));
         };
         title.push_str(" - Rust");
         let tyname = it.type_();
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 6a1fad06ae3..d8c37137bbc 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -49,6 +49,7 @@ use std::{fs, str};
 
 use askama::Template;
 use itertools::Either;
+use rustc_ast::join_path_syms;
 use rustc_attr_data_structures::{
     ConstStability, DeprecatedSince, Deprecation, RustcVersion, StabilityLevel, StableSince,
 };
@@ -74,9 +75,9 @@ use crate::formats::cache::Cache;
 use crate::formats::item_type::ItemType;
 use crate::html::escape::Escape;
 use crate::html::format::{
-    Ending, HrefError, PrintWithSpace, href, join_with_double_colon, print_abi_with_space,
-    print_constness_with_space, print_default_space, print_generic_bounds, print_where_clause,
-    visibility_print_with_space, write_str,
+    Ending, HrefError, PrintWithSpace, href, print_abi_with_space, print_constness_with_space,
+    print_default_space, print_generic_bounds, print_where_clause, visibility_print_with_space,
+    write_str,
 };
 use crate::html::markdown::{
     HeadingOffset, IdMap, Markdown, MarkdownItemInfo, MarkdownSummaryLine,
@@ -980,7 +981,7 @@ fn assoc_method(
     let name = meth.name.as_ref().unwrap();
     let vis = visibility_print_with_space(meth, cx).to_string();
     let defaultness = print_default_space(meth.is_default());
-    // FIXME: Once https://github.com/rust-lang/rust/issues/67792 is implemented, we can remove
+    // FIXME: Once https://github.com/rust-lang/rust/issues/143874 is implemented, we can remove
     // this condition.
     let constness = match render_mode {
         RenderMode::Normal => print_constness_with_space(
@@ -1191,7 +1192,7 @@ fn render_assoc_item(
 // a whitespace prefix and newline.
 fn render_attributes_in_pre(it: &clean::Item, prefix: &str, cx: &Context<'_>) -> impl fmt::Display {
     fmt::from_fn(move |f| {
-        for a in it.attributes(cx.tcx(), cx.cache(), false) {
+        for a in it.attributes(cx.tcx(), cx.cache()) {
             writeln!(f, "{prefix}{a}")?;
         }
         Ok(())
@@ -1207,7 +1208,7 @@ fn render_code_attribute(code_attr: CodeAttribute, w: &mut impl fmt::Write) {
 // When an attribute is rendered inside a <code> tag, it is formatted using
 // a div to produce a newline after it.
 fn render_attributes_in_code(w: &mut impl fmt::Write, it: &clean::Item, cx: &Context<'_>) {
-    for attr in it.attributes(cx.tcx(), cx.cache(), false) {
+    for attr in it.attributes(cx.tcx(), cx.cache()) {
         render_code_attribute(CodeAttribute(attr), w);
     }
 }
@@ -1219,7 +1220,7 @@ fn render_repr_attributes_in_code(
     def_id: DefId,
     item_type: ItemType,
 ) {
-    if let Some(repr) = clean::repr_attributes(cx.tcx(), cx.cache(), def_id, item_type, false) {
+    if let Some(repr) = clean::repr_attributes(cx.tcx(), cx.cache(), def_id, item_type) {
         render_code_attribute(CodeAttribute(repr), w);
     }
 }
@@ -2152,7 +2153,7 @@ fn render_rightside(
     let tcx = cx.tcx();
 
     fmt::from_fn(move |w| {
-        // FIXME: Once https://github.com/rust-lang/rust/issues/67792 is implemented, we can remove
+        // FIXME: Once https://github.com/rust-lang/rust/issues/143874 is implemented, we can remove
         // this condition.
         let const_stability = match render_mode {
             RenderMode::Normal => item.const_stability(tcx),
@@ -2555,7 +2556,7 @@ fn collect_paths_for_type(first_ty: &clean::Type, cache: &Cache) -> Vec<String>
         let fqp = cache.exact_paths.get(&did).or_else(get_extern);
 
         if let Some(path) = fqp {
-            out.push(join_with_double_colon(path));
+            out.push(join_path_syms(path));
         }
     };
 
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index e33bdc0db32..5fbda4797cc 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -4,6 +4,7 @@ use std::iter;
 
 use askama::Template;
 use rustc_abi::VariantIdx;
+use rustc_ast::join_path_syms;
 use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
 use rustc_hir as hir;
 use rustc_hir::def::CtorKind;
@@ -30,8 +31,8 @@ use crate::formats::Impl;
 use crate::formats::item_type::ItemType;
 use crate::html::escape::{Escape, EscapeBodyTextWithWbr};
 use crate::html::format::{
-    Ending, PrintWithSpace, join_with_double_colon, print_abi_with_space,
-    print_constness_with_space, print_where_clause, visibility_print_with_space,
+    Ending, PrintWithSpace, print_abi_with_space, print_constness_with_space, print_where_clause,
+    visibility_print_with_space,
 };
 use crate::html::markdown::{HeadingOffset, MarkdownSummaryLine};
 use crate::html::render::{document_full, document_item_info};
@@ -1424,7 +1425,7 @@ fn item_type_alias(cx: &Context<'_>, it: &clean::Item, t: &clean::TypeAlias) ->
                 iter::repeat_n("..", cx.current.len()).chain(iter::once("type.impl")).collect();
             js_src_path.extend(target_fqp[..target_fqp.len() - 1].iter().copied());
             js_src_path.push_fmt(format_args!("{target_type}.{}.js", target_fqp.last().unwrap()));
-            let self_path = fmt::from_fn(|f| self_fqp.iter().joined("::", f));
+            let self_path = join_path_syms(self_fqp);
             write!(
                 w,
                 "<script src=\"{src}\" data-self-path=\"{self_path}\" async></script>",
@@ -1487,12 +1488,11 @@ impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> {
                     self.cx.cache(),
                     self.def_id,
                     ItemType::Union,
-                    false,
                 ) {
                     writeln!(f, "{repr}")?;
                 };
             } else {
-                for a in self.it.attributes(self.cx.tcx(), self.cx.cache(), false) {
+                for a in self.it.attributes(self.cx.tcx(), self.cx.cache()) {
                     writeln!(f, "{a}")?;
                 }
             }
@@ -2257,7 +2257,7 @@ pub(crate) fn compare_names(left: &str, right: &str) -> Ordering {
 }
 
 pub(super) fn full_path(cx: &Context<'_>, item: &clean::Item) -> String {
-    let mut s = join_with_double_colon(&cx.current);
+    let mut s = join_path_syms(&cx.current);
     s.push_str("::");
     s.push_str(item.name.unwrap().as_str());
     s
diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs
index aff8684ee3a..80a59fa218c 100644
--- a/src/librustdoc/html/render/search_index.rs
+++ b/src/librustdoc/html/render/search_index.rs
@@ -4,6 +4,7 @@ use std::collections::hash_map::Entry;
 use std::collections::{BTreeMap, VecDeque};
 
 use encode::{bitmap_to_string, write_vlqhex_to_string};
+use rustc_ast::join_path_syms;
 use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
 use rustc_middle::ty::TyCtxt;
 use rustc_span::def_id::DefId;
@@ -17,7 +18,6 @@ use crate::clean::types::{Function, Generics, ItemId, Type, WherePredicate};
 use crate::clean::{self, utils};
 use crate::formats::cache::{Cache, OrphanImplItem};
 use crate::formats::item_type::ItemType;
-use crate::html::format::join_with_double_colon;
 use crate::html::markdown::short_markdown_summary;
 use crate::html::render::ordered_json::OrderedJson;
 use crate::html::render::{self, IndexItem, IndexItemFunctionType, RenderType, RenderTypeId};
@@ -78,7 +78,7 @@ pub(crate) fn build_index(
                 ty: item.type_(),
                 defid: item.item_id.as_def_id(),
                 name: item.name.unwrap(),
-                path: join_with_double_colon(&fqp[..fqp.len() - 1]),
+                path: join_path_syms(&fqp[..fqp.len() - 1]),
                 desc,
                 parent: Some(parent),
                 parent_idx: None,
@@ -416,7 +416,7 @@ pub(crate) fn build_index(
                             if fqp.len() < 2 {
                                 return None;
                             }
-                            join_with_double_colon(&fqp[..fqp.len() - 1])
+                            join_path_syms(&fqp[..fqp.len() - 1])
                         };
                     if path == item.path {
                         return None;
@@ -427,10 +427,10 @@ pub(crate) fn build_index(
                 let i = <isize as TryInto<usize>>::try_into(parent_idx).unwrap();
                 item.path = {
                     let p = &crate_paths[i].1;
-                    join_with_double_colon(&p[..p.len() - 1])
+                    join_path_syms(&p[..p.len() - 1])
                 };
                 item.exact_path =
-                    crate_paths[i].2.as_ref().map(|xp| join_with_double_colon(&xp[..xp.len() - 1]));
+                    crate_paths[i].2.as_ref().map(|xp| join_path_syms(&xp[..xp.len() - 1]));
             }
 
             // Omit the parent path if it is same to that of the prior item.
@@ -549,11 +549,11 @@ pub(crate) fn build_index(
                     });
                     continue;
                 }
-                let full_path = join_with_double_colon(&path[..path.len() - 1]);
+                let full_path = join_path_syms(&path[..path.len() - 1]);
                 let full_exact_path = exact
                     .as_ref()
                     .filter(|exact| exact.last() == path.last() && exact.len() >= 2)
-                    .map(|exact| join_with_double_colon(&exact[..exact.len() - 1]));
+                    .map(|exact| join_path_syms(&exact[..exact.len() - 1]));
                 let exact_path = extra_paths.len() + self.items.len();
                 let exact_path = full_exact_path.as_ref().map(|full_exact_path| match extra_paths
                     .entry(full_exact_path.clone())
diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs
index 41c3f03b91b..1f691392b17 100644
--- a/src/librustdoc/html/render/write_shared.rs
+++ b/src/librustdoc/html/render/write_shared.rs
@@ -25,8 +25,8 @@ use std::str::FromStr;
 use std::{fmt, fs};
 
 use indexmap::IndexMap;
-use itertools::Itertools;
 use regex::Regex;
+use rustc_ast::join_path_syms;
 use rustc_data_structures::flock;
 use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_middle::ty::TyCtxt;
@@ -608,7 +608,7 @@ impl TypeAliasPart {
                     for &(type_alias_fqp, type_alias_item) in type_aliases {
                         cx.id_map.borrow_mut().clear();
                         cx.deref_id_map.borrow_mut().clear();
-                        let type_alias_fqp = (*type_alias_fqp).iter().join("::");
+                        let type_alias_fqp = join_path_syms(type_alias_fqp);
                         if let Some(ret) = &mut ret {
                             ret.aliases.push(type_alias_fqp);
                         } else {
diff --git a/src/librustdoc/html/url_parts_builder.rs b/src/librustdoc/html/url_parts_builder.rs
index 9a533827441..705fa498e8d 100644
--- a/src/librustdoc/html/url_parts_builder.rs
+++ b/src/librustdoc/html/url_parts_builder.rs
@@ -117,7 +117,7 @@ impl UrlPartsBuilder {
 
 /// This is just a guess at the average length of a URL part,
 /// used for [`String::with_capacity`] calls in the [`FromIterator`]
-/// and [`Extend`] impls, and for [estimating item path lengths].
+/// and [`Extend`] impls.
 ///
 /// The value `8` was chosen for two main reasons:
 ///
@@ -125,18 +125,8 @@ impl UrlPartsBuilder {
 /// * jemalloc's size classes are all multiples of eight,
 ///   which means that the amount of memory it allocates will often match
 ///   the amount requested, avoiding wasted bytes.
-///
-/// [estimating item path lengths]: estimate_item_path_byte_length
 const AVG_PART_LENGTH: usize = 8;
 
-/// Estimate the number of bytes in an item's path, based on how many segments it has.
-///
-/// **Note:** This is only to be used with, e.g., [`String::with_capacity()`];
-/// the return value is just a rough estimate.
-pub(crate) const fn estimate_item_path_byte_length(segment_count: usize) -> usize {
-    AVG_PART_LENGTH * segment_count
-}
-
 impl<'a> FromIterator<&'a str> for UrlPartsBuilder {
     fn from_iter<T: IntoIterator<Item = &'a str>>(iter: T) -> Self {
         let iter = iter.into_iter();
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index f51b35097f6..0a84d8caa30 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -2,14 +2,15 @@
 //! the `clean` types but with some fields removed or stringified to simplify the output and not
 //! expose unstable compiler internals.
 
-#![allow(rustc::default_hash_types)]
-
 use rustc_abi::ExternAbi;
 use rustc_ast::ast;
 use rustc_attr_data_structures::{self as attrs, DeprecatedSince};
+use rustc_hir as hir;
 use rustc_hir::def::CtorKind;
 use rustc_hir::def_id::DefId;
+use rustc_hir::{HeaderSafety, Safety};
 use rustc_metadata::rendered_const;
+use rustc_middle::ty::TyCtxt;
 use rustc_middle::{bug, ty};
 use rustc_span::{Pos, kw, sym};
 use rustdoc_json_types::*;
@@ -40,7 +41,12 @@ impl JsonRenderer<'_> {
             })
             .collect();
         let docs = item.opt_doc_value();
-        let attrs = item.attributes(self.tcx, &self.cache, true);
+        let attrs = item
+            .attrs
+            .other_attrs
+            .iter()
+            .filter_map(|a| maybe_from_hir_attr(a, item.item_id, self.tcx))
+            .collect();
         let span = item.span(self.tcx);
         let visibility = item.visibility(self.tcx);
         let clean::ItemInner { name, item_id, .. } = *item.inner;
@@ -383,10 +389,22 @@ impl FromClean<clean::Union> for Union {
 
 impl FromClean<rustc_hir::FnHeader> for FunctionHeader {
     fn from_clean(header: &rustc_hir::FnHeader, renderer: &JsonRenderer<'_>) -> Self {
+        let is_unsafe = match header.safety {
+            HeaderSafety::SafeTargetFeatures => {
+                // The type system's internal implementation details consider
+                // safe functions with the `#[target_feature]` attribute to be analogous
+                // to unsafe functions: `header.is_unsafe()` returns `true` for them.
+                // For rustdoc, this isn't the right decision, so we explicitly return `false`.
+                // Context: https://github.com/rust-lang/rust/issues/142655
+                false
+            }
+            HeaderSafety::Normal(Safety::Safe) => false,
+            HeaderSafety::Normal(Safety::Unsafe) => true,
+        };
         FunctionHeader {
             is_async: header.is_async(),
             is_const: header.is_const(),
-            is_unsafe: header.is_unsafe(),
+            is_unsafe,
             abi: header.abi.into_json(renderer),
         }
     }
@@ -875,3 +893,93 @@ impl FromClean<ItemType> for ItemKind {
         }
     }
 }
+
+/// Maybe convert a attribute from hir to json.
+///
+/// Returns `None` if the attribute shouldn't be in the output.
+fn maybe_from_hir_attr(
+    attr: &hir::Attribute,
+    item_id: ItemId,
+    tcx: TyCtxt<'_>,
+) -> Option<Attribute> {
+    use attrs::AttributeKind as AK;
+
+    let kind = match attr {
+        hir::Attribute::Parsed(kind) => kind,
+
+        hir::Attribute::Unparsed(_) => {
+            // FIXME: We should handle `#[doc(hidden)]`.
+            return Some(other_attr(tcx, attr));
+        }
+    };
+
+    Some(match kind {
+        AK::Deprecation { .. } => return None, // Handled separately into Item::deprecation.
+        AK::DocComment { .. } => unreachable!("doc comments stripped out earlier"),
+
+        AK::MustUse { reason, span: _ } => {
+            Attribute::MustUse { reason: reason.map(|s| s.to_string()) }
+        }
+        AK::Repr { .. } => repr_attr(
+            tcx,
+            item_id.as_def_id().expect("all items that could have #[repr] have a DefId"),
+        ),
+        AK::ExportName { name, span: _ } => Attribute::ExportName(name.to_string()),
+        AK::LinkSection { name, span: _ } => Attribute::LinkSection(name.to_string()),
+        AK::TargetFeature(features, _span) => Attribute::TargetFeature {
+            enable: features.iter().map(|(feat, _span)| feat.to_string()).collect(),
+        },
+
+        AK::NoMangle(_) => Attribute::NoMangle,
+        AK::NonExhaustive(_) => Attribute::NonExhaustive,
+        AK::AutomaticallyDerived(_) => Attribute::AutomaticallyDerived,
+
+        _ => other_attr(tcx, attr),
+    })
+}
+
+fn other_attr(tcx: TyCtxt<'_>, attr: &hir::Attribute) -> Attribute {
+    let mut s = rustc_hir_pretty::attribute_to_string(&tcx, attr);
+    assert_eq!(s.pop(), Some('\n'));
+    Attribute::Other(s)
+}
+
+fn repr_attr(tcx: TyCtxt<'_>, def_id: DefId) -> Attribute {
+    let repr = tcx.adt_def(def_id).repr();
+
+    let kind = if repr.c() {
+        ReprKind::C
+    } else if repr.transparent() {
+        ReprKind::Transparent
+    } else if repr.simd() {
+        ReprKind::Simd
+    } else {
+        ReprKind::Rust
+    };
+
+    let align = repr.align.map(|a| a.bytes());
+    let packed = repr.pack.map(|p| p.bytes());
+    let int = repr.int.map(format_integer_type);
+
+    Attribute::Repr(AttributeRepr { kind, align, packed, int })
+}
+
+fn format_integer_type(it: rustc_abi::IntegerType) -> String {
+    use rustc_abi::Integer::*;
+    use rustc_abi::IntegerType::*;
+    match it {
+        Pointer(true) => "isize",
+        Pointer(false) => "usize",
+        Fixed(I8, true) => "i8",
+        Fixed(I8, false) => "u8",
+        Fixed(I16, true) => "i16",
+        Fixed(I16, false) => "u16",
+        Fixed(I32, true) => "i32",
+        Fixed(I32, false) => "u32",
+        Fixed(I64, true) => "i64",
+        Fixed(I64, false) => "u64",
+        Fixed(I128, true) => "i128",
+        Fixed(I128, false) => "u128",
+    }
+    .to_owned()
+}
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index 2889bfae7b0..9058277d72e 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -32,11 +32,29 @@ pub(crate) struct Module<'hir> {
     pub(crate) def_id: LocalDefId,
     pub(crate) renamed: Option<Symbol>,
     pub(crate) import_id: Option<LocalDefId>,
-    /// The key is the item `ItemId` and the value is: (item, renamed, import_id).
+    /// The key is the item `ItemId` and the value is: (item, renamed, Vec<import_id>).
     /// We use `FxIndexMap` to keep the insert order.
+    ///
+    /// `import_id` needs to be a `Vec` because we live in a dark world where you can have code
+    /// like:
+    ///
+    /// ```
+    /// mod raw {
+    ///     pub fn foo() {}
+    /// }
+    ///
+    /// /// Foobar
+    /// pub use raw::foo;
+    ///
+    /// pub use raw::*;
+    /// ```
+    ///
+    /// So in this case, we don't want to have two items but just one with attributes from all
+    /// non-glob imports to be merged. Glob imports attributes are always ignored, whether they're
+    /// shadowed or not.
     pub(crate) items: FxIndexMap<
         (LocalDefId, Option<Symbol>),
-        (&'hir hir::Item<'hir>, Option<Symbol>, Option<LocalDefId>),
+        (&'hir hir::Item<'hir>, Option<Symbol>, Vec<LocalDefId>),
     >,
 
     /// (def_id, renamed) -> (res, local_import_id)
@@ -154,7 +172,9 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
             {
                 let item = self.cx.tcx.hir_expect_item(local_def_id);
                 let (ident, _, _) = item.expect_macro();
-                top_level_module.items.insert((local_def_id, Some(ident.name)), (item, None, None));
+                top_level_module
+                    .items
+                    .insert((local_def_id, Some(ident.name)), (item, None, Vec::new()));
             }
         }
 
@@ -236,7 +256,6 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
     ) -> bool {
         debug!("maybe_inline_local (renamed: {renamed:?}) res: {res:?}");
 
-        let glob = renamed.is_none();
         if renamed == Some(kw::Underscore) {
             // We never inline `_` reexports.
             return false;
@@ -261,6 +280,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
             return false;
         }
 
+        let is_glob = renamed.is_none();
         let is_hidden = !document_hidden && tcx.is_doc_hidden(ori_res_did);
         let Some(res_did) = ori_res_did.as_local() else {
             // For cross-crate impl inlining we need to know whether items are
@@ -268,7 +288,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
             // made reachable by cross-crate inlining which we're checking here.
             // (this is done here because we need to know this upfront).
             crate::visit_lib::lib_embargo_visit_item(self.cx, ori_res_did);
-            if is_hidden || glob {
+            if is_hidden || is_glob {
                 return false;
             }
             // We store inlined foreign items otherwise, it'd mean that the `use` item would be kept
@@ -316,10 +336,10 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
             // Bang macros are handled a bit on their because of how they are handled by the
             // compiler. If they have `#[doc(hidden)]` and the re-export doesn't have
             // `#[doc(inline)]`, then we don't inline it.
-            Node::Item(_) if is_bang_macro && !please_inline && renamed.is_some() && is_hidden => {
+            Node::Item(_) if is_bang_macro && !please_inline && !is_glob && is_hidden => {
                 return false;
             }
-            Node::Item(&hir::Item { kind: hir::ItemKind::Mod(_, m), .. }) if glob => {
+            Node::Item(&hir::Item { kind: hir::ItemKind::Mod(_, m), .. }) if is_glob => {
                 let prev = mem::replace(&mut self.inlining, true);
                 for &i in m.item_ids {
                     let i = tcx.hir_item(i);
@@ -328,13 +348,13 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
                 self.inlining = prev;
                 true
             }
-            Node::Item(it) if !glob => {
+            Node::Item(it) if !is_glob => {
                 let prev = mem::replace(&mut self.inlining, true);
                 self.visit_item_inner(it, renamed, Some(def_id));
                 self.inlining = prev;
                 true
             }
-            Node::ForeignItem(it) if !glob => {
+            Node::ForeignItem(it) if !is_glob => {
                 let prev = mem::replace(&mut self.inlining, true);
                 self.visit_foreign_item_inner(it, renamed, Some(def_id));
                 self.inlining = prev;
@@ -378,8 +398,8 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
     fn add_to_current_mod(
         &mut self,
         item: &'tcx hir::Item<'_>,
-        renamed: Option<Symbol>,
-        parent_id: Option<LocalDefId>,
+        mut renamed: Option<Symbol>,
+        import_id: Option<LocalDefId>,
     ) {
         if self.is_importable_from_parent
             // If we're inside an item, only impl blocks and `macro_rules!` with the `macro_export`
@@ -392,11 +412,21 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
                 _ => false,
             }
         {
-            self.modules
-                .last_mut()
-                .unwrap()
-                .items
-                .insert((item.owner_id.def_id, renamed), (item, renamed, parent_id));
+            if renamed == item.kind.ident().map(|ident| ident.name) {
+                renamed = None;
+            }
+            let key = (item.owner_id.def_id, renamed);
+            if let Some(import_id) = import_id {
+                self.modules
+                    .last_mut()
+                    .unwrap()
+                    .items
+                    .entry(key)
+                    .and_modify(|v| v.2.push(import_id))
+                    .or_insert_with(|| (item, renamed, vec![import_id]));
+            } else {
+                self.modules.last_mut().unwrap().items.insert(key, (item, renamed, Vec::new()));
+            }
         }
     }
 
@@ -439,8 +469,8 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
 
         match item.kind {
             hir::ItemKind::ForeignMod { items, .. } => {
-                for item in items {
-                    let item = tcx.hir_foreign_item(item.id);
+                for &item in items {
+                    let item = tcx.hir_foreign_item(item);
                     self.visit_foreign_item_inner(item, None, None);
                 }
             }
@@ -468,7 +498,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
                             _ => false,
                         });
                         let ident = match kind {
-                            hir::UseKind::Single(ident) => Some(renamed.unwrap_or(ident.name)),
+                            hir::UseKind::Single(ident) => Some(ident.name),
                             hir::UseKind::Glob => None,
                             hir::UseKind::ListStem => unreachable!(),
                         };
diff --git a/src/llvm-project b/src/llvm-project
-Subproject 9b1bf4cf041c1c1fe62cf03891ac90431615e78
+Subproject d3c793b025645a4565ac59aceb30d2d116ff1a4
diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs
index 0e72ddd9db1..6235b0e8576 100644
--- a/src/rustdoc-json-types/lib.rs
+++ b/src/rustdoc-json-types/lib.rs
@@ -37,8 +37,8 @@ pub type FxHashMap<K, V> = HashMap<K, V>; // re-export for use in src/librustdoc
 // will instead cause conflicts. See #94591 for more. (This paragraph and the "Latest feature" line
 // are deliberately not in a doc comment, because they need not be in public docs.)
 //
-// Latest feature: Pretty printing of no_mangle attributes changed
-pub const FORMAT_VERSION: u32 = 53;
+// Latest feature: Structured Attributes
+pub const FORMAT_VERSION: u32 = 54;
 
 /// The root of the emitted JSON blob.
 ///
@@ -195,13 +195,94 @@ pub struct Item {
     /// - `#[repr(C)]` and other reprs also appear as themselves,
     ///   though potentially with a different order: e.g. `repr(i8, C)` may become `repr(C, i8)`.
     ///   Multiple repr attributes on the same item may be combined into an equivalent single attr.
-    pub attrs: Vec<String>,
+    pub attrs: Vec<Attribute>,
     /// Information about the item’s deprecation, if present.
     pub deprecation: Option<Deprecation>,
     /// The type-specific fields describing this item.
     pub inner: ItemEnum,
 }
 
+#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
+#[serde(rename_all = "snake_case")]
+/// An attribute, e.g. `#[repr(C)]`
+///
+/// This doesn't include:
+/// - `#[doc = "Doc Comment"]` or `/// Doc comment`. These are in [`Item::docs`] instead.
+/// - `#[deprecated]`. These are in [`Item::deprecation`] instead.
+pub enum Attribute {
+    /// `#[non_exhaustive]`
+    NonExhaustive,
+
+    /// `#[must_use]`
+    MustUse { reason: Option<String> },
+
+    /// `#[export_name = "name"]`
+    ExportName(String),
+
+    /// `#[link_section = "name"]`
+    LinkSection(String),
+
+    /// `#[automatically_derived]`
+    AutomaticallyDerived,
+
+    /// `#[repr]`
+    Repr(AttributeRepr),
+
+    /// `#[no_mangle]`
+    NoMangle,
+
+    /// #[target_feature(enable = "feature1", enable = "feature2")]
+    TargetFeature { enable: Vec<String> },
+
+    /// Something else.
+    ///
+    /// Things here are explicitly *not* covered by the [`FORMAT_VERSION`]
+    /// constant, and may change without bumping the format version.
+    ///
+    /// As an implementation detail, this is currently either:
+    /// 1. A HIR debug printing, like `"#[attr = Optimize(Speed)]"`
+    /// 2. The attribute as it appears in source form, like
+    ///    `"#[optimize(speed)]"`.
+    Other(String),
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
+/// The contents of a `#[repr(...)]` attribute.
+///
+/// Used in [`Attribute::Repr`].
+pub struct AttributeRepr {
+    /// The representation, e.g. `#[repr(C)]`, `#[repr(transparent)]`
+    pub kind: ReprKind,
+
+    /// Alignment in bytes, if explicitly specified by `#[repr(align(...)]`.
+    pub align: Option<u64>,
+    /// Alignment in bytes, if explicitly specified by `#[repr(packed(...)]]`.
+    pub packed: Option<u64>,
+
+    /// The integer type for an enum descriminant, if explicitly specified.
+    ///
+    /// e.g. `"i32"`, for `#[repr(C, i32)]`
+    pub int: Option<String>,
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
+#[serde(rename_all = "snake_case")]
+/// The kind of `#[repr]`.
+///
+/// See [AttributeRepr::kind]`.
+pub enum ReprKind {
+    /// `#[repr(Rust)]`
+    ///
+    /// Also the default.
+    Rust,
+    /// `#[repr(C)]`
+    C,
+    /// `#[repr(transparent)]
+    Transparent,
+    /// `#[repr(simd)]`
+    Simd,
+}
+
 /// A range of source code.
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub struct Span {
@@ -1343,7 +1424,7 @@ pub struct Static {
 
     /// Is the static `unsafe`?
     ///
-    /// This is only true if it's in an `extern` block, and not explicity marked
+    /// This is only true if it's in an `extern` block, and not explicitly marked
     /// as `safe`.
     ///
     /// ```rust
diff --git a/src/tools/cargo b/src/tools/cargo
-Subproject 930b4f62cfcd1f0eabdb30a56d91bf6844b739b
+Subproject 6833aa715d724437dc1247d0166afe314ab6854
diff --git a/src/tools/clippy/.github/workflows/feature_freeze.yml b/src/tools/clippy/.github/workflows/feature_freeze.yml
index a5f8d4bc145..7ad58af77d4 100644
--- a/src/tools/clippy/.github/workflows/feature_freeze.yml
+++ b/src/tools/clippy/.github/workflows/feature_freeze.yml
@@ -1,7 +1,11 @@
 name: Feature freeze check
 
 on:
-  pull_request:
+  pull_request_target:
+    types:
+      - opened
+    branches:
+      - master
     paths:
       - 'clippy_lints/src/declared_lints.rs'
 
@@ -9,6 +13,12 @@ jobs:
   auto-comment:
     runs-on: ubuntu-latest
 
+    permissions:
+      pull-requests: write
+
+    # Do not in any case add code that runs anything coming from the  the content
+    # of the pull request, as malicious code would be able to access the private
+    # GitHub token.
     steps:
     - name: Check PR Changes
       id: pr-changes
@@ -19,7 +29,7 @@ jobs:
       run: |
         # Use GitHub API to create a comment on the PR
         PR_NUMBER=${{ github.event.pull_request.number }}
-        COMMENT="**Seems that you are trying to add a new lint!**\nWe are currently in a [feature freeze](https://doc.rust-lang.org/nightly/clippy/development/feature_freeze.html), so we are delaying all lint-adding PRs to August 1st and focusing on bugfixes.\nThanks a lot for your contribution, and sorry for the inconvenience.\nWith ❤ from the Clippy team"
+        COMMENT="**Seems that you are trying to add a new lint!**\nWe are currently in a [feature freeze](https://doc.rust-lang.org/nightly/clippy/development/feature_freeze.html), so we are delaying all lint-adding PRs to September 18 and focusing on bugfixes.\nThanks a lot for your contribution, and sorry for the inconvenience.\nWith ❤ from the Clippy team\n\n@rustbot note Feature-freeze\n@rustbot blocked\n@rustbot label +A-lint\n"
         GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}
         COMMENT_URL="https://api.github.com/repos/${{ github.repository }}/issues/${PR_NUMBER}/comments"
         curl -s -H "Authorization: token ${GITHUB_TOKEN}" -X POST $COMMENT_URL -d "{\"body\":\"$COMMENT\"}"
diff --git a/src/tools/clippy/.github/workflows/lintcheck.yml b/src/tools/clippy/.github/workflows/lintcheck.yml
index 70c805903d3..003d0395739 100644
--- a/src/tools/clippy/.github/workflows/lintcheck.yml
+++ b/src/tools/clippy/.github/workflows/lintcheck.yml
@@ -128,21 +128,27 @@ jobs:
     - name: Download JSON
       uses: actions/download-artifact@v4
 
+    - name: Store PR number
+      run: echo ${{ github.event.pull_request.number }} > pr.txt
+
     - name: Diff results
-      # GH's summery has a maximum size of 1024k:
-      # https://docs.github.com/actions/using-workflows/workflow-commands-for-github-actions#adding-a-markdown-summary
-      # That's why we first log to file and then to the summary and logs
+      # GH's summery has a maximum size of 1MiB:
+      # https://docs.github.com/en/actions/writing-workflows/choosing-what-your-workflow-does/workflow-commands-for-github-actions#step-isolation-and-limits
+      # We upload the full diff as an artifact in case it's truncated
       run: |
-        ./target/debug/lintcheck diff {base,head}/ci_crates_logs.json --truncate >> truncated_diff.md
-        head -c 1024000 truncated_diff.md >> $GITHUB_STEP_SUMMARY
-        cat truncated_diff.md
-        ./target/debug/lintcheck diff {base,head}/ci_crates_logs.json >> full_diff.md
+        ./target/debug/lintcheck diff {base,head}/ci_crates_logs.json --truncate | head -c 1M > $GITHUB_STEP_SUMMARY
+        ./target/debug/lintcheck diff {base,head}/ci_crates_logs.json --write-summary summary.json > full_diff.md
 
     - name: Upload full diff
       uses: actions/upload-artifact@v4
       with:
-        name: diff
-        if-no-files-found: ignore
+        name: full_diff
+        path: full_diff.md
+
+    - name: Upload summary
+      uses: actions/upload-artifact@v4
+      with:
+        name: summary
         path: |
-          full_diff.md
-          truncated_diff.md
+          summary.json
+          pr.txt
diff --git a/src/tools/clippy/.github/workflows/lintcheck_summary.yml b/src/tools/clippy/.github/workflows/lintcheck_summary.yml
new file mode 100644
index 00000000000..52f52e155a0
--- /dev/null
+++ b/src/tools/clippy/.github/workflows/lintcheck_summary.yml
@@ -0,0 +1,106 @@
+name: Lintcheck summary
+
+# The workflow_run event runs in the context of the Clippy repo giving it write
+# access, needed here to create a PR comment when the PR originates from a fork
+#
+# The summary artifact is a JSON file that we verify in this action to prevent
+# the creation of arbitrary comments
+#
+# This action must not checkout/run code from the originating workflow_run
+# or directly interpolate ${{}} untrusted fields into code
+#
+# https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#workflow_run
+# https://docs.github.com/en/actions/security-for-github-actions/security-guides/security-hardening-for-github-actions#understanding-the-risk-of-script-injections
+
+on:
+  workflow_run:
+    workflows: [Lintcheck]
+    types: [completed]
+
+# Restrict the default permission scope https://docs.github.com/en/actions/writing-workflows/workflow-syntax-for-github-actions#defining-access-for-the-github_token-scopes
+permissions:
+  pull-requests: write
+
+jobs:
+  download:
+    runs-on: ubuntu-latest
+    if: ${{ github.event.workflow_run.conclusion == 'success' }}
+    steps:
+      - name: Download artifact
+        uses: actions/download-artifact@v4
+        with:
+          name: summary
+          path: untrusted
+          run-id: ${{ github.event.workflow_run.id }}
+          github-token: ${{ github.token }}
+
+      - name: Format comment
+        uses: actions/github-script@v7
+        with:
+          script: |
+            const fs = require("fs");
+            const assert = require("assert/strict");
+
+            function validateName(s) {
+              assert.match(s, /^[a-z0-9_:]+$/);
+              return s;
+            }
+
+            function validateInt(i) {
+              assert.ok(Number.isInteger(i));
+              return i;
+            }
+
+            function tryReadSummary() {
+              try {
+                return JSON.parse(fs.readFileSync("untrusted/summary.json"));
+              } catch {
+                return null;
+              }
+            }
+
+            const prNumber = parseInt(fs.readFileSync("untrusted/pr.txt"), 10);
+            core.exportVariable("PR", prNumber.toString());
+
+            const untrustedSummary = tryReadSummary();
+            if (!Array.isArray(untrustedSummary)) {
+              return;
+            }
+
+            let summary = `Lintcheck changes for ${context.payload.workflow_run.head_sha}
+            
+            | Lint | Added | Removed | Changed |
+            | ---- | ----: | ------: | ------: |
+            `;
+
+            for (const untrustedRow of untrustedSummary) {
+              const name = validateName(untrustedRow.name);
+
+              const added = validateInt(untrustedRow.added);
+              const removed = validateInt(untrustedRow.removed);
+              const changed = validateInt(untrustedRow.changed);
+
+              const id = name.replace("clippy::", "user-content-").replaceAll("_", "-");
+              const url = `https://github.com/${process.env.GITHUB_REPOSITORY}/actions/runs/${context.payload.workflow_run.id}#${id}`;
+
+              summary += `| [\`${name}\`](${url}) | ${added} | ${removed} | ${changed} |\n`;
+            }
+
+            summary += "\nThis comment will be updated if you push new changes";
+
+            fs.writeFileSync("summary.md", summary);
+
+      - name: Create/update comment
+        run: |
+          if [[ -f summary.md ]]; then
+             gh pr comment "$PR" --body-file summary.md --edit-last --create-if-none
+          else
+            # There were no changes detected by Lintcheck
+            # - If a comment exists from a previous run that did detect changes, edit it (--edit-last)
+            # - If no comment exists do not create one, the `gh` command exits with an error which
+            #   `|| true` ignores
+            gh pr comment "$PR" --body "No changes for ${{ github.event.workflow_run.head_sha }}" --edit-last || true
+          fi
+        env:
+          GH_TOKEN: ${{ github.token }}
+          GH_REPO: ${{ github.repository }}
diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml
index 1278427b5a7..f25f9ab54dd 100644
--- a/src/tools/clippy/Cargo.toml
+++ b/src/tools/clippy/Cargo.toml
@@ -28,13 +28,13 @@ declare_clippy_lint = { path = "declare_clippy_lint" }
 rustc_tools_util = { path = "rustc_tools_util", version = "0.4.2" }
 clippy_lints_internal = { path = "clippy_lints_internal", optional = true }
 tempfile = { version = "3.20", optional = true }
-termize = "0.1"
+termize = "0.2"
 color-print = "0.3.4"
 anstream = "0.6.18"
 
 [dev-dependencies]
 cargo_metadata = "0.18.1"
-ui_test = "0.29.2"
+ui_test = "0.30.2"
 regex = "1.5.5"
 serde = { version = "1.0.145", features = ["derive"] }
 serde_json = "1.0.122"
@@ -45,14 +45,6 @@ itertools = "0.12"
 pulldown-cmark = { version = "0.11", default-features = false, features = ["html"] }
 askama = { version = "0.14", default-features = false, features = ["alloc", "config", "derive"] }
 
-# UI test dependencies
-if_chain = "1.0"
-quote = "1.0.25"
-syn = { version = "2.0", features = ["full"] }
-futures = "0.3"
-parking_lot = "0.12"
-tokio = { version = "1", features = ["io-util"] }
-
 [build-dependencies]
 rustc_tools_util = { path = "rustc_tools_util", version = "0.4.2" }
 
diff --git a/src/tools/clippy/book/src/development/infrastructure/backport.md b/src/tools/clippy/book/src/development/infrastructure/backport.md
index 9526d8af1c9..47ea6a412c5 100644
--- a/src/tools/clippy/book/src/development/infrastructure/backport.md
+++ b/src/tools/clippy/book/src/development/infrastructure/backport.md
@@ -109,4 +109,4 @@ worth backporting this.
 When a PR is backported to Rust `beta`, label the PR with `beta-accepted`. This
 will then get picked up when [writing the changelog].
 
-[writing the changelog]: changelog_update.md#31-include-beta-accepted-prs
+[writing the changelog]: changelog_update.md#4-include-beta-accepted-prs
diff --git a/src/tools/clippy/book/src/development/infrastructure/changelog_update.md b/src/tools/clippy/book/src/development/infrastructure/changelog_update.md
index eede6b78d92..c96ff228b01 100644
--- a/src/tools/clippy/book/src/development/infrastructure/changelog_update.md
+++ b/src/tools/clippy/book/src/development/infrastructure/changelog_update.md
@@ -38,7 +38,7 @@ Usually you want to write the changelog of the **upcoming stable release**. Make
 sure though, that `beta` was already branched in the Rust repository.
 
 To find the commit hash, issue the following command when in a `rust-lang/rust`
-checkout:
+checkout (most of the time on the `upstream/beta` branch):
 ```
 git log --oneline -- src/tools/clippy/ | grep -o "Merge commit '[a-f0-9]*' into .*" | head -1 | sed -e "s/Merge commit '\([a-f0-9]*\)' into .*/\1/g"
 ```
@@ -48,16 +48,13 @@ git log --oneline -- src/tools/clippy/ | grep -o "Merge commit '[a-f0-9]*' into
 Once you've got the correct commit range, run
 
 ```
-util/fetch_prs_between.sh commit1 commit2 > changes.txt
+util/fetch_prs_between.sh start_commit end_commit > changes.txt
 ```
 
-where `commit2` is the commit hash from the previous command and `commit1`
-is the commit hash from the current CHANGELOG file.
+where `end_commit` is the commit hash from the previous command and `start_commit`
+is [the commit hash][beta_section] from the current CHANGELOG file.
 Open `changes.txt` file in your editor of choice.
 
-When updating the changelog it's also a good idea to make sure that `commit1` is
-already correct in the current changelog.
-
 ### 3. Authoring the final changelog
 
 The above script should have dumped all the relevant PRs to the file you
@@ -70,17 +67,7 @@ With the PRs filtered, you can start to take each PR and move the `changelog: `
 content to `CHANGELOG.md`. Adapt the wording as you see fit but try to keep it
 somewhat coherent.
 
-The order should roughly be:
-
-1. New lints
-2. Moves or deprecations of lints
-3. Changes that expand what code existing lints cover
-4. False positive fixes
-5. ICE fixes
-6. Documentation improvements
-7. Others
-
-As section headers, we use:
+The sections order should roughly be:
 
 ```
 ### New Lints
@@ -97,10 +84,10 @@ As section headers, we use:
 ### Others
 ```
 
-Please also be sure to update the Beta/Unreleased sections at the top with the
-relevant commit ranges.
+Please also be sure to update [the `Unreleased/Beta/In Rust Nightly` section][beta_section] at the top with the
+relevant commits ranges and to add the `Rust <version>` section with release date and PR ranges.
 
-#### 3.1 Include `beta-accepted` PRs
+### 4. Include `beta-accepted` PRs
 
 Look for the [`beta-accepted`] label and make sure to also include the PRs with
 that label in the changelog. If you can, remove the `beta-accepted` labels
@@ -109,7 +96,7 @@ that label in the changelog. If you can, remove the `beta-accepted` labels
 > _Note:_ Some of those PRs might even get backported to the previous `beta`.
 > Those have to be included in the changelog of the _previous_ release.
 
-### 4. Update `clippy::version` attributes
+### 5. Update `clippy::version` attributes
 
 Next, make sure to check that the `#[clippy::version]` attributes for the added
 lints contain the correct version. 
@@ -129,3 +116,4 @@ written for. If not, update the version to the changelog version.
 [rust_beta_tools]: https://github.com/rust-lang/rust/tree/beta/src/tools/clippy
 [rust_stable_tools]: https://github.com/rust-lang/rust/releases
 [`beta-accepted`]: https://github.com/rust-lang/rust-clippy/issues?q=label%3Abeta-accepted+
+[beta_section]: https://github.com/rust-lang/rust-clippy/blob/master/CHANGELOG.md#unreleased--beta--in-rust-nightly
diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md
index e9b7f42a183..992ed2c6aaa 100644
--- a/src/tools/clippy/book/src/lint_configuration.md
+++ b/src/tools/clippy/book/src/lint_configuration.md
@@ -892,6 +892,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio
 * [`unnested_or_patterns`](https://rust-lang.github.io/rust-clippy/master/index.html#unnested_or_patterns)
 * [`unused_trait_names`](https://rust-lang.github.io/rust-clippy/master/index.html#unused_trait_names)
 * [`use_self`](https://rust-lang.github.io/rust-clippy/master/index.html#use_self)
+* [`zero_ptr`](https://rust-lang.github.io/rust-clippy/master/index.html#zero_ptr)
 
 
 ## `pass-by-value-size-limit`
diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs
index 841facdca06..555f54bcfb8 100644
--- a/src/tools/clippy/clippy_config/src/conf.rs
+++ b/src/tools/clippy/clippy_config/src/conf.rs
@@ -794,6 +794,7 @@ define_Conf! {
         unnested_or_patterns,
         unused_trait_names,
         use_self,
+        zero_ptr,
     )]
     msrv: Msrv = Msrv::default(),
     /// The minimum size (in bytes) to consider a type for passing by reference instead of by value.
diff --git a/src/tools/clippy/clippy_dev/src/fmt.rs b/src/tools/clippy/clippy_dev/src/fmt.rs
index c1b6b370706..bd9e57c9f6d 100644
--- a/src/tools/clippy/clippy_dev/src/fmt.rs
+++ b/src/tools/clippy/clippy_dev/src/fmt.rs
@@ -223,7 +223,7 @@ fn fmt_conf(check: bool) -> Result<(), Error> {
         if check {
             return Err(Error::CheckFailed);
         }
-        fs::write(path, new_text.as_bytes())?;
+        fs::write(path, new_text)?;
     }
     Ok(())
 }
diff --git a/src/tools/clippy/clippy_dev/src/lib.rs b/src/tools/clippy/clippy_dev/src/lib.rs
index 3361443196a..40aadf4589a 100644
--- a/src/tools/clippy/clippy_dev/src/lib.rs
+++ b/src/tools/clippy/clippy_dev/src/lib.rs
@@ -2,7 +2,6 @@
     rustc_private,
     exit_status_error,
     if_let_guard,
-    let_chains,
     os_str_slice,
     os_string_truncate,
     slice_split_once
diff --git a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs
index 8b6bfaebbe5..a9d3015ce5c 100644
--- a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs
+++ b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs
@@ -8,11 +8,13 @@ use clippy_utils::diagnostics::span_lint_and_note;
 use clippy_utils::is_cfg_test;
 use rustc_attr_data_structures::AttributeKind;
 use rustc_hir::{
-    AssocItemKind, Attribute, FieldDef, HirId, ImplItemRef, IsAuto, Item, ItemKind, Mod, QPath, TraitItemRef, TyKind,
+    Attribute, FieldDef, HirId, IsAuto, ImplItemId, Item, ItemKind, Mod, OwnerId, QPath, TraitItemId, TyKind,
     Variant, VariantData,
 };
+use rustc_middle::ty::AssocKind;
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_session::impl_lint_pass;
+use rustc_span::Ident;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -194,22 +196,22 @@ impl ArbitrarySourceItemOrdering {
     }
 
     /// Produces a linting warning for incorrectly ordered impl items.
-    fn lint_impl_item<T: LintContext>(&self, cx: &T, item: &ImplItemRef, before_item: &ImplItemRef) {
+    fn lint_impl_item(&self, cx: &LateContext<'_>, item: ImplItemId, before_item: ImplItemId) {
         span_lint_and_note(
             cx,
             ARBITRARY_SOURCE_ITEM_ORDERING,
-            item.span,
+            cx.tcx.def_span(item.owner_id),
             format!(
                 "incorrect ordering of impl items (defined order: {:?})",
                 self.assoc_types_order
             ),
-            Some(before_item.span),
-            format!("should be placed before `{}`", before_item.ident.name),
+            Some(cx.tcx.def_span(before_item.owner_id)),
+            format!("should be placed before `{}`", cx.tcx.item_name(before_item.owner_id)),
         );
     }
 
     /// Produces a linting warning for incorrectly ordered item members.
-    fn lint_member_name<T: LintContext>(cx: &T, ident: &rustc_span::Ident, before_ident: &rustc_span::Ident) {
+    fn lint_member_name<T: LintContext>(cx: &T, ident: Ident, before_ident: Ident) {
         span_lint_and_note(
             cx,
             ARBITRARY_SOURCE_ITEM_ORDERING,
@@ -220,7 +222,7 @@ impl ArbitrarySourceItemOrdering {
         );
     }
 
-    fn lint_member_item<T: LintContext>(cx: &T, item: &Item<'_>, before_item: &Item<'_>, msg: &'static str) {
+    fn lint_member_item(cx: &LateContext<'_>, item: &Item<'_>, before_item: &Item<'_>, msg: &'static str) {
         let span = if let Some(ident) = item.kind.ident() {
             ident.span
         } else {
@@ -245,17 +247,17 @@ impl ArbitrarySourceItemOrdering {
     }
 
     /// Produces a linting warning for incorrectly ordered trait items.
-    fn lint_trait_item<T: LintContext>(&self, cx: &T, item: &TraitItemRef, before_item: &TraitItemRef) {
+    fn lint_trait_item(&self, cx: &LateContext<'_>, item: TraitItemId, before_item: TraitItemId) {
         span_lint_and_note(
             cx,
             ARBITRARY_SOURCE_ITEM_ORDERING,
-            item.span,
+            cx.tcx.def_span(item.owner_id),
             format!(
                 "incorrect ordering of trait items (defined order: {:?})",
                 self.assoc_types_order
             ),
-            Some(before_item.span),
-            format!("should be placed before `{}`", before_item.ident.name),
+            Some(cx.tcx.def_span(before_item.owner_id)),
+            format!("should be placed before `{}`", cx.tcx.item_name(before_item.owner_id)),
         );
     }
 }
@@ -266,7 +268,7 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering {
             .tcx
             .hir_attrs(item.hir_id())
             .iter()
-            .any(|attr| matches!(attr, Attribute::Parsed(AttributeKind::Repr{ .. })))
+            .any(|attr| matches!(attr, Attribute::Parsed(AttributeKind::Repr { .. })))
         {
             // Do not lint items with a `#[repr]` attribute as their layout may be imposed by an external API.
             return;
@@ -283,7 +285,7 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering {
                         && cur_v.ident.name.as_str() > variant.ident.name.as_str()
                         && cur_v.span != variant.span
                     {
-                        Self::lint_member_name(cx, &variant.ident, &cur_v.ident);
+                        Self::lint_member_name(cx, variant.ident, cur_v.ident);
                     }
                     cur_v = Some(variant);
                 }
@@ -299,57 +301,61 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering {
                         && cur_f.ident.name.as_str() > field.ident.name.as_str()
                         && cur_f.span != field.span
                     {
-                        Self::lint_member_name(cx, &field.ident, &cur_f.ident);
+                        Self::lint_member_name(cx, field.ident, cur_f.ident);
                     }
                     cur_f = Some(field);
                 }
             },
-            ItemKind::Trait(is_auto, _safety, _ident, _generics, _generic_bounds, item_ref)
+            ItemKind::Trait(_constness, is_auto, _safety, _ident, _generics, _generic_bounds, item_ref)
                 if self.enable_ordering_for_trait && *is_auto == IsAuto::No =>
             {
-                let mut cur_t: Option<&TraitItemRef> = None;
+                let mut cur_t: Option<(TraitItemId, Ident)> = None;
 
-                for item in *item_ref {
-                    if item.span.in_external_macro(cx.sess().source_map()) {
+                for &item in *item_ref {
+                    let span = cx.tcx.def_span(item.owner_id);
+                    let ident = cx.tcx.item_ident(item.owner_id);
+                    if span.in_external_macro(cx.sess().source_map()) {
                         continue;
                     }
 
-                    if let Some(cur_t) = cur_t {
-                        let cur_t_kind = convert_assoc_item_kind(cur_t.kind);
+                    if let Some((cur_t, cur_ident)) = cur_t {
+                        let cur_t_kind = convert_assoc_item_kind(cx, cur_t.owner_id);
                         let cur_t_kind_index = self.assoc_types_order.index_of(&cur_t_kind);
-                        let item_kind = convert_assoc_item_kind(item.kind);
+                        let item_kind = convert_assoc_item_kind(cx, item.owner_id);
                         let item_kind_index = self.assoc_types_order.index_of(&item_kind);
 
-                        if cur_t_kind == item_kind && cur_t.ident.name.as_str() > item.ident.name.as_str() {
-                            Self::lint_member_name(cx, &item.ident, &cur_t.ident);
+                        if cur_t_kind == item_kind && cur_ident.name.as_str() > ident.name.as_str() {
+                            Self::lint_member_name(cx, ident, cur_ident);
                         } else if cur_t_kind_index > item_kind_index {
                             self.lint_trait_item(cx, item, cur_t);
                         }
                     }
-                    cur_t = Some(item);
+                    cur_t = Some((item, ident));
                 }
             },
             ItemKind::Impl(trait_impl) if self.enable_ordering_for_impl => {
-                let mut cur_t: Option<&ImplItemRef> = None;
+                let mut cur_t: Option<(ImplItemId, Ident)> = None;
 
-                for item in trait_impl.items {
-                    if item.span.in_external_macro(cx.sess().source_map()) {
+                for &item in trait_impl.items {
+                    let span = cx.tcx.def_span(item.owner_id);
+                    let ident = cx.tcx.item_ident(item.owner_id);
+                    if span.in_external_macro(cx.sess().source_map()) {
                         continue;
                     }
 
-                    if let Some(cur_t) = cur_t {
-                        let cur_t_kind = convert_assoc_item_kind(cur_t.kind);
+                    if let Some((cur_t, cur_ident)) = cur_t {
+                        let cur_t_kind = convert_assoc_item_kind(cx, cur_t.owner_id);
                         let cur_t_kind_index = self.assoc_types_order.index_of(&cur_t_kind);
-                        let item_kind = convert_assoc_item_kind(item.kind);
+                        let item_kind = convert_assoc_item_kind(cx, item.owner_id);
                         let item_kind_index = self.assoc_types_order.index_of(&item_kind);
 
-                        if cur_t_kind == item_kind && cur_t.ident.name.as_str() > item.ident.name.as_str() {
-                            Self::lint_member_name(cx, &item.ident, &cur_t.ident);
+                        if cur_t_kind == item_kind && cur_ident.name.as_str() > ident.name.as_str() {
+                            Self::lint_member_name(cx, ident, cur_ident);
                         } else if cur_t_kind_index > item_kind_index {
                             self.lint_impl_item(cx, item, cur_t);
                         }
                     }
-                    cur_t = Some(item);
+                    cur_t = Some((item, ident));
                 }
             },
             _ => {}, // Catch-all for `ItemKinds` that don't have fields.
@@ -458,18 +464,19 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering {
     }
 }
 
-/// Converts a [`rustc_hir::AssocItemKind`] to a
-/// [`SourceItemOrderingTraitAssocItemKind`].
+/// Converts a [`ty::AssocKind`] to a [`SourceItemOrderingTraitAssocItemKind`].
 ///
 /// This is implemented here because `rustc_hir` is not a dependency of
 /// `clippy_config`.
-fn convert_assoc_item_kind(value: AssocItemKind) -> SourceItemOrderingTraitAssocItemKind {
+fn convert_assoc_item_kind(cx: &LateContext<'_>, owner_id: OwnerId) -> SourceItemOrderingTraitAssocItemKind {
+    let kind = cx.tcx.associated_item(owner_id.def_id).kind;
+
     #[allow(clippy::enum_glob_use)] // Very local glob use for legibility.
     use SourceItemOrderingTraitAssocItemKind::*;
-    match value {
-        AssocItemKind::Const => Const,
-        AssocItemKind::Type => Type,
-        AssocItemKind::Fn { .. } => Fn,
+    match kind {
+        AssocKind::Const{..} => Const,
+        AssocKind::Type {..}=> Type,
+        AssocKind::Fn { .. } => Fn,
     }
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs
index bf43234ff50..61c2fc49bd7 100644
--- a/src/tools/clippy/clippy_lints/src/booleans.rs
+++ b/src/tools/clippy/clippy_lints/src/booleans.rs
@@ -1,5 +1,6 @@
 use clippy_config::Conf;
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
+use clippy_utils::higher::has_let_expr;
 use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::SpanRangeExt;
 use clippy_utils::sugg::Sugg;
@@ -646,7 +647,9 @@ impl<'tcx> Visitor<'tcx> for NonminimalBoolVisitor<'_, 'tcx> {
     fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
         if !e.span.from_expansion() {
             match &e.kind {
-                ExprKind::Binary(binop, _, _) if binop.node == BinOpKind::Or || binop.node == BinOpKind::And => {
+                ExprKind::Binary(binop, _, _)
+                    if binop.node == BinOpKind::Or || binop.node == BinOpKind::And && !has_let_expr(e) =>
+                {
                     self.bool_expr(e);
                 },
                 ExprKind::Unary(UnOp::Not, inner) => {
diff --git a/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs
index ad0a4f8cdf3..e3b125a8d5b 100644
--- a/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/borrow_as_ptr.rs
@@ -18,7 +18,8 @@ pub(super) fn check<'tcx>(
     cast_to: &'tcx Ty<'_>,
     msrv: Msrv,
 ) -> bool {
-    if matches!(cast_to.kind, TyKind::Ptr(_))
+    if let TyKind::Ptr(target) = cast_to.kind
+        && !matches!(target.ty.kind, TyKind::TraitObject(..))
         && let ExprKind::AddrOf(BorrowKind::Ref, mutability, e) = cast_expr.kind
         && !is_lint_allowed(cx, BORROW_AS_PTR, expr.hir_id)
     {
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 a2ecb5fb44a..2eebe849232 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
@@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
 use clippy_utils::source::snippet;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::{get_discriminant_value, is_isize_or_usize};
-use clippy_utils::{expr_or_init, sym};
+use clippy_utils::{expr_or_init, is_in_const_context, sym};
 use rustc_abi::IntegerType;
 use rustc_errors::{Applicability, Diag};
 use rustc_hir::def::{DefKind, Res};
@@ -168,7 +168,9 @@ pub(super) fn check(
 
     span_lint_and_then(cx, CAST_POSSIBLE_TRUNCATION, expr.span, msg, |diag| {
         diag.help("if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...");
-        if !cast_from.is_floating_point() {
+        // TODO: Remove the condition for const contexts when `try_from` and other commonly used methods
+        // become const fn.
+        if !is_in_const_context(cx) && !cast_from.is_floating_point() {
             offer_suggestion(cx, expr, cast_expr, cast_to_span, diag);
         }
     });
diff --git a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast.rs b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast.rs
index 105477093b5..55e27a05f3c 100644
--- a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast.rs
@@ -17,7 +17,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
         ty::FnDef(..) | ty::FnPtr(..) => {
             let mut applicability = Applicability::MaybeIncorrect;
 
-            if to_nbits >= cx.tcx.data_layout.pointer_size.bits() && !cast_to.is_usize() {
+            if to_nbits >= cx.tcx.data_layout.pointer_size().bits() && !cast_to.is_usize() {
                 let from_snippet = snippet_with_applicability(cx, cast_expr.span, "x", &mut applicability);
                 span_lint_and_sugg(
                     cx,
diff --git a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs
index 700b7d0d426..4da79205e20 100644
--- a/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs
@@ -17,7 +17,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
             let mut applicability = Applicability::MaybeIncorrect;
             let from_snippet = snippet_with_applicability(cx, cast_expr.span, "x", &mut applicability);
 
-            if to_nbits < cx.tcx.data_layout.pointer_size.bits() {
+            if to_nbits < cx.tcx.data_layout.pointer_size().bits() {
                 span_lint_and_sugg(
                     cx,
                     FN_TO_NUMERIC_CAST_WITH_TRUNCATION,
diff --git a/src/tools/clippy/clippy_lints/src/casts/mod.rs b/src/tools/clippy/clippy_lints/src/casts/mod.rs
index daae9a8bb08..37accff5eaa 100644
--- a/src/tools/clippy/clippy_lints/src/casts/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/mod.rs
@@ -878,7 +878,7 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
             confusing_method_to_numeric_cast::check(cx, expr, cast_from_expr, cast_from, cast_to);
             fn_to_numeric_cast::check(cx, expr, cast_from_expr, cast_from, cast_to);
             fn_to_numeric_cast_with_truncation::check(cx, expr, cast_from_expr, cast_from, cast_to);
-            zero_ptr::check(cx, expr, cast_from_expr, cast_to_hir);
+            zero_ptr::check(cx, expr, cast_from_expr, cast_to_hir, self.msrv);
 
             if self.msrv.meets(cx, msrvs::MANUAL_DANGLING_PTR) {
                 manual_dangling_ptr::check(cx, expr, cast_from_expr, cast_to_hir);
diff --git a/src/tools/clippy/clippy_lints/src/casts/utils.rs b/src/tools/clippy/clippy_lints/src/casts/utils.rs
index 318a1646477..d846d78b9ee 100644
--- a/src/tools/clippy/clippy_lints/src/casts/utils.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/utils.rs
@@ -5,7 +5,7 @@ use rustc_middle::ty::{self, AdtDef, IntTy, Ty, TyCtxt, UintTy, VariantDiscr};
 /// integral type.
 pub(super) fn int_ty_to_nbits(tcx: TyCtxt<'_>, ty: Ty<'_>) -> Option<u64> {
     match ty.kind() {
-        ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => Some(tcx.data_layout.pointer_size.bits()),
+        ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => Some(tcx.data_layout.pointer_size().bits()),
         ty::Int(i) => i.bit_width(),
         ty::Uint(i) => i.bit_width(),
         _ => None,
diff --git a/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs
index a34af6bc226..f4738e7b0d5 100644
--- a/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/zero_ptr.rs
@@ -1,4 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::SpanRangeExt;
 use clippy_utils::{is_in_const_context, is_integer_literal, std_or_core};
 use rustc_errors::Applicability;
@@ -7,10 +8,10 @@ use rustc_lint::LateContext;
 
 use super::ZERO_PTR;
 
-pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_>) {
+pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_>, msrv: Msrv) {
     if let TyKind::Ptr(ref mut_ty) = to.kind
         && is_integer_literal(from, 0)
-        && !is_in_const_context(cx)
+        && (!is_in_const_context(cx) || msrv.meets(cx, msrvs::PTR_NULL))
         && let Some(std_or_core) = std_or_core(cx)
     {
         let (msg, sugg_fn) = match mut_ty.mutbl {
diff --git a/src/tools/clippy/clippy_lints/src/coerce_container_to_any.rs b/src/tools/clippy/clippy_lints/src/coerce_container_to_any.rs
index 2b659253763..6217fc4c897 100644
--- a/src/tools/clippy/clippy_lints/src/coerce_container_to_any.rs
+++ b/src/tools/clippy/clippy_lints/src/coerce_container_to_any.rs
@@ -1,9 +1,10 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet;
+use clippy_utils::sugg::{self, Sugg};
 use clippy_utils::sym;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::adjustment::{Adjust, PointerCoercion};
 use rustc_middle::ty::{self, ExistentialPredicate, Ty, TyCtxt};
 use rustc_session::declare_lint_pass;
 
@@ -49,23 +50,18 @@ declare_lint_pass!(CoerceContainerToAny => [COERCE_CONTAINER_TO_ANY]);
 
 impl<'tcx> LateLintPass<'tcx> for CoerceContainerToAny {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
-        // If this expression has an effective type of `&dyn Any` ...
-        {
-            let coerced_ty = cx.typeck_results().expr_ty_adjusted(e);
-
-            let ty::Ref(_, coerced_ref_ty, _) = *coerced_ty.kind() else {
-                return;
-            };
-            if !is_dyn_any(cx.tcx, coerced_ref_ty) {
-                return;
-            }
+        // If this expression was coerced to `&dyn Any` ...
+        if !cx.typeck_results().expr_adjustments(e).last().is_some_and(|adj| {
+            matches!(adj.kind, Adjust::Pointer(PointerCoercion::Unsize)) && is_ref_dyn_any(cx.tcx, adj.target)
+        }) {
+            return;
         }
 
         let expr_ty = cx.typeck_results().expr_ty(e);
         let ty::Ref(_, expr_ref_ty, _) = *expr_ty.kind() else {
             return;
         };
-        // ... but only due to coercion ...
+        // ... but it's not actually `&dyn Any` ...
         if is_dyn_any(cx.tcx, expr_ref_ty) {
             return;
         }
@@ -78,23 +74,37 @@ impl<'tcx> LateLintPass<'tcx> for CoerceContainerToAny {
         }
 
         // ... that's probably not intended.
-        let (span, deref_count) = match e.kind {
+        let (target_expr, deref_count) = match e.kind {
             // If `e` was already an `&` expression, skip `*&` in the suggestion
-            ExprKind::AddrOf(_, _, referent) => (referent.span, depth),
-            _ => (e.span, depth + 1),
+            ExprKind::AddrOf(_, _, referent) => (referent, depth),
+            _ => (e, depth + 1),
         };
+        let ty::Ref(_, _, mutability) = *cx.typeck_results().expr_ty_adjusted(e).kind() else {
+            return;
+        };
+        let sugg = sugg::make_unop(
+            &format!("{}{}", mutability.ref_prefix_str(), str::repeat("*", deref_count)),
+            Sugg::hir(cx, target_expr, ".."),
+        );
         span_lint_and_sugg(
             cx,
             COERCE_CONTAINER_TO_ANY,
             e.span,
-            format!("coercing `{expr_ty}` to `&dyn Any`"),
+            format!("coercing `{expr_ty}` to `{}dyn Any`", mutability.ref_prefix_str()),
             "consider dereferencing",
-            format!("&{}{}", str::repeat("*", deref_count), snippet(cx, span, "x")),
+            sugg.to_string(),
             Applicability::MaybeIncorrect,
         );
     }
 }
 
+fn is_ref_dyn_any(tcx: TyCtxt<'_>, ty: Ty<'_>) -> bool {
+    let ty::Ref(_, ref_ty, _) = *ty.kind() else {
+        return false;
+    };
+    is_dyn_any(tcx, ref_ty)
+}
+
 fn is_dyn_any(tcx: TyCtxt<'_>, ty: Ty<'_>) -> bool {
     let ty::Dynamic(traits, ..) = ty.kind() else {
         return false;
diff --git a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs
index 5c64216dd92..d5d937d9133 100644
--- a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs
+++ b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs
@@ -14,18 +14,25 @@ use rustc_span::def_id::LocalDefId;
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for methods with high cognitive complexity.
+    /// We used to think it measured how hard a method is to understand.
     ///
     /// ### Why is this bad?
-    /// Methods of high cognitive complexity tend to be hard to
-    /// both read and maintain. Also LLVM will tend to optimize small methods better.
+    /// Ideally, we would like to be able to measure how hard a function is
+    /// to understand given its context (what we call its Cognitive Complexity).
+    /// But that's not what this lint does. See "Known problems"
     ///
     /// ### Known problems
-    /// Sometimes it's hard to find a way to reduce the
-    /// complexity.
+    /// The true Cognitive Complexity of a method is not something we can
+    /// calculate using modern technology. This lint has been left in the
+    /// `nursery` so as to not mislead users into using this lint as a
+    /// measurement tool.
     ///
-    /// ### Example
-    /// You'll see it when you get the warning.
+    /// For more detailed information, see [rust-clippy#3793](https://github.com/rust-lang/rust-clippy/issues/3793)
+    ///
+    /// ### Lints to consider instead of this
+    ///
+    /// * [`excessive_nesting`](https://rust-lang.github.io/rust-clippy/master/index.html#excessive_nesting)
+    /// * [`too_many_lines`](https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines)
     #[clippy::version = "1.35.0"]
     pub COGNITIVE_COMPLEXITY,
     nursery,
diff --git a/src/tools/clippy/clippy_lints/src/derivable_impls.rs b/src/tools/clippy/clippy_lints/src/derivable_impls.rs
index 10331b3855b..0a481ddcd12 100644
--- a/src/tools/clippy/clippy_lints/src/derivable_impls.rs
+++ b/src/tools/clippy/clippy_lints/src/derivable_impls.rs
@@ -192,7 +192,7 @@ impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
             && !item.span.from_expansion()
             && let Some(def_id) = trait_ref.trait_def_id()
             && cx.tcx.is_diagnostic_item(sym::Default, def_id)
-            && let impl_item_hir = child.id.hir_id()
+            && let impl_item_hir = child.hir_id()
             && let Node::ImplItem(impl_item) = cx.tcx.hir_node(impl_item_hir)
             && let ImplItemKind::Fn(_, b) = &impl_item.kind
             && let Body { value: func_expr, .. } = cx.tcx.hir_body(*b)
diff --git a/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs b/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs
index d1a8590c59b..cf964d4b580 100644
--- a/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs
+++ b/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs
@@ -89,6 +89,10 @@ impl EarlyLintPass for DisallowedScriptIdents {
             // Fast path for ascii-only idents.
             if !symbol_str.is_ascii()
                 && let Some(script) = symbol_str.chars().find_map(|c| {
+                    if c.is_ascii() {
+                        return None;
+                    }
+
                     c.script_extension()
                         .iter()
                         .find(|script| !self.whitelist.contains(script))
diff --git a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs
index 9ee32fced8c..3033ac0d0b0 100644
--- a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs
@@ -3,7 +3,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_note};
 use clippy_utils::macros::{is_panic, root_macro_call_first_node};
 use clippy_utils::ty::{get_type_diagnostic_name, implements_trait_with_env, is_type_diagnostic_item};
 use clippy_utils::visitors::for_each_expr;
-use clippy_utils::{fulfill_or_allowed, is_doc_hidden, method_chain_args, return_ty};
+use clippy_utils::{fulfill_or_allowed, is_doc_hidden, is_inside_always_const_context, method_chain_args, return_ty};
 use rustc_hir::{BodyId, FnSig, OwnerId, Safety};
 use rustc_lint::LateContext;
 use rustc_middle::ty;
@@ -99,13 +99,16 @@ fn find_panic(cx: &LateContext<'_>, body_id: BodyId) -> Option<Span> {
     let mut panic_span = None;
     let typeck = cx.tcx.typeck_body(body_id);
     for_each_expr(cx, cx.tcx.hir_body(body_id), |expr| {
+        if is_inside_always_const_context(cx.tcx, expr.hir_id) {
+            return ControlFlow::<!>::Continue(());
+        }
+
         if let Some(macro_call) = root_macro_call_first_node(cx, expr)
             && (is_panic(cx, macro_call.def_id)
                 || matches!(
                     cx.tcx.get_diagnostic_name(macro_call.def_id),
                     Some(sym::assert_macro | sym::assert_eq_macro | sym::assert_ne_macro)
                 ))
-            && !cx.tcx.hir_is_inside_const_context(expr.hir_id)
             && !fulfill_or_allowed(cx, MISSING_PANICS_DOC, [expr.hir_id])
             && panic_span.is_none()
         {
diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs
index 5ea55e102df..22b781b8929 100644
--- a/src/tools/clippy/clippy_lints/src/doc/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs
@@ -740,7 +740,7 @@ impl<'tcx> LateLintPass<'tcx> for Documentation {
                             );
                         }
                     },
-                    ItemKind::Trait(_, unsafety, ..) => match (headers.safety, unsafety) {
+                    ItemKind::Trait(_, _, unsafety, ..) => match (headers.safety, unsafety) {
                         (false, Safety::Unsafe) => span_lint(
                             cx,
                             MISSING_SAFETY_DOC,
@@ -1249,7 +1249,9 @@ fn looks_like_refdef(doc: &str, range: Range<usize>) -> Option<Range<usize>> {
             b'[' => {
                 start = Some(i + offset);
             },
-            b']' if let Some(start) = start => {
+            b']' if let Some(start) = start
+                && doc.as_bytes().get(i + offset + 1) == Some(&b':') =>
+            {
                 return Some(start..i + offset + 1);
             },
             _ => {},
diff --git a/src/tools/clippy/clippy_lints/src/empty_drop.rs b/src/tools/clippy/clippy_lints/src/empty_drop.rs
index d557a36c7ac..4e948701da4 100644
--- a/src/tools/clippy/clippy_lints/src/empty_drop.rs
+++ b/src/tools/clippy/clippy_lints/src/empty_drop.rs
@@ -41,7 +41,7 @@ impl LateLintPass<'_> for EmptyDrop {
             ..
         }) = item.kind
             && trait_ref.trait_def_id() == cx.tcx.lang_items().drop_trait()
-            && let impl_item_hir = child.id.hir_id()
+            && let impl_item_hir = child.hir_id()
             && let Node::ImplItem(impl_item) = cx.tcx.hir_node(impl_item_hir)
             && let ImplItemKind::Fn(_, b) = &impl_item.kind
             && let Body { value: func_expr, .. } = cx.tcx.hir_body(*b)
diff --git a/src/tools/clippy/clippy_lints/src/enum_clike.rs b/src/tools/clippy/clippy_lints/src/enum_clike.rs
index 098571a5351..c828fc57f76 100644
--- a/src/tools/clippy/clippy_lints/src/enum_clike.rs
+++ b/src/tools/clippy/clippy_lints/src/enum_clike.rs
@@ -35,7 +35,7 @@ declare_lint_pass!(UnportableVariant => [ENUM_CLIKE_UNPORTABLE_VARIANT]);
 impl<'tcx> LateLintPass<'tcx> for UnportableVariant {
     #[expect(clippy::cast_possible_wrap)]
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
-        if cx.tcx.data_layout.pointer_size.bits() != 64 {
+        if cx.tcx.data_layout.pointer_size().bits() != 64 {
             return;
         }
         if let ItemKind::Enum(_, _, def) = &item.kind {
diff --git a/src/tools/clippy/clippy_lints/src/escape.rs b/src/tools/clippy/clippy_lints/src/escape.rs
index 2cb3b32babe..db2fea1aae9 100644
--- a/src/tools/clippy/clippy_lints/src/escape.rs
+++ b/src/tools/clippy/clippy_lints/src/escape.rs
@@ -1,7 +1,8 @@
 use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_hir;
 use rustc_abi::ExternAbi;
-use rustc_hir::{AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node, Pat, PatKind, intravisit};
+use rustc_hir::{Body, FnDecl, HirId, HirIdSet, Node, Pat, PatKind, intravisit};
+use rustc_hir::def::DefKind;
 use rustc_hir_typeck::expr_use_visitor::{Delegate, ExprUseVisitor, PlaceBase, PlaceWithHirId};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::mir::FakeReadCause;
@@ -84,23 +85,18 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal {
             .def_id;
 
         let mut trait_self_ty = None;
-        if let Node::Item(item) = cx.tcx.hir_node_by_def_id(parent_id) {
+        match cx.tcx.def_kind(parent_id) {
             // If the method is an impl for a trait, don't warn.
-            if let ItemKind::Impl(Impl { of_trait: Some(_), .. }) = item.kind {
-                return;
+            DefKind::Impl { of_trait: true } => {
+                return
             }
 
             // find `self` ty for this trait if relevant
-            if let ItemKind::Trait(_, _, _, _, _, items) = item.kind {
-                for trait_item in items {
-                    if trait_item.id.owner_id.def_id == fn_def_id
-                        // be sure we have `self` parameter in this function
-                        && trait_item.kind == (AssocItemKind::Fn { has_self: true })
-                    {
-                        trait_self_ty = Some(TraitRef::identity(cx.tcx, trait_item.id.owner_id.to_def_id()).self_ty());
-                    }
-                }
+            DefKind::Trait => {
+                trait_self_ty = Some(TraitRef::identity(cx.tcx, parent_id.to_def_id()).self_ty());
             }
+
+            _ => {}
         }
 
         let mut v = EscapeDelegate {
diff --git a/src/tools/clippy/clippy_lints/src/exhaustive_items.rs b/src/tools/clippy/clippy_lints/src/exhaustive_items.rs
index 7dda3e0fdb9..8ad09279071 100644
--- a/src/tools/clippy/clippy_lints/src/exhaustive_items.rs
+++ b/src/tools/clippy/clippy_lints/src/exhaustive_items.rs
@@ -1,12 +1,10 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::indent_of;
+use rustc_attr_data_structures::{AttributeKind, find_attr};
 use rustc_errors::Applicability;
 use rustc_hir::{Item, ItemKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
-use rustc_attr_data_structures::AttributeKind;
-use rustc_attr_data_structures::find_attr;
-
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/exit.rs b/src/tools/clippy/clippy_lints/src/exit.rs
index cc8e4d7d9e2..487db69027a 100644
--- a/src/tools/clippy/clippy_lints/src/exit.rs
+++ b/src/tools/clippy/clippy_lints/src/exit.rs
@@ -1,5 +1,4 @@
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::is_entrypoint_fn;
 use rustc_hir::{Expr, ExprKind, Item, ItemKind, OwnerNode};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
@@ -7,7 +6,8 @@ use rustc_span::sym;
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Detects calls to the `exit()` function which terminates the program.
+    /// Detects calls to the `exit()` function that are not in the `main` function. Calls to `exit()`
+    /// immediately terminate the program.
     ///
     /// ### Why restrict this?
     /// `exit()` immediately terminates the program with no information other than an exit code.
@@ -15,11 +15,24 @@ declare_clippy_lint! {
     ///
     /// Codebases may use this lint to require that all exits are performed either by panicking
     /// (which produces a message, a code location, and optionally a backtrace)
-    /// or by returning from `main()` (which is a single place to look).
+    /// or by calling `exit()` from `main()` (which is a single place to look).
     ///
-    /// ### Example
+    /// ### Good example
     /// ```no_run
-    /// std::process::exit(0)
+    /// fn main() {
+    ///     std::process::exit(0);
+    /// }
+    /// ```
+    ///
+    /// ### Bad example
+    /// ```no_run
+    /// fn main() {
+    ///     other_function();
+    /// }
+    ///
+    /// fn other_function() {
+    ///     std::process::exit(0);
+    /// }
     /// ```
     ///
     /// Use instead:
@@ -36,7 +49,7 @@ declare_clippy_lint! {
     #[clippy::version = "1.41.0"]
     pub EXIT,
     restriction,
-    "detects `std::process::exit` calls"
+    "detects `std::process::exit` calls outside of `main`"
 }
 
 declare_lint_pass!(Exit => [EXIT]);
@@ -48,10 +61,14 @@ impl<'tcx> LateLintPass<'tcx> for Exit {
             && let Some(def_id) = cx.qpath_res(path, path_expr.hir_id).opt_def_id()
             && cx.tcx.is_diagnostic_item(sym::process_exit, def_id)
             && let parent = cx.tcx.hir_get_parent_item(e.hir_id)
-            && let OwnerNode::Item(Item{kind: ItemKind::Fn{ .. }, ..}) = cx.tcx.hir_owner_node(parent)
-            // If the next item up is a function we check if it is an entry point
+            && let OwnerNode::Item(Item{kind: ItemKind::Fn{ ident, .. }, ..}) = cx.tcx.hir_owner_node(parent)
+            // If the next item up is a function we check if it isn't named "main"
             // and only then emit a linter warning
-            && !is_entrypoint_fn(cx, parent.to_def_id())
+
+            // if you instead check for the parent of the `exit()` call being the entrypoint function, as this worked before,
+            // in compilation contexts like --all-targets (which include --tests), you get false positives
+            // because in a test context, main is not the entrypoint function
+            && ident.name != sym::main
         {
             span_lint(cx, EXIT, e.span, "usage of `process::exit`");
         }
diff --git a/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs b/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs
index 68d0cd19c8a..552cd721f4e 100644
--- a/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs
+++ b/src/tools/clippy/clippy_lints/src/fallible_impl_from.rs
@@ -52,20 +52,20 @@ declare_lint_pass!(FallibleImplFrom => [FALLIBLE_IMPL_FROM]);
 impl<'tcx> LateLintPass<'tcx> for FallibleImplFrom {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) {
         // check for `impl From<???> for ..`
-        if let hir::ItemKind::Impl(impl_) = &item.kind
+        if let hir::ItemKind::Impl(_) = &item.kind
             && let Some(impl_trait_ref) = cx.tcx.impl_trait_ref(item.owner_id)
             && cx
                 .tcx
                 .is_diagnostic_item(sym::From, impl_trait_ref.skip_binder().def_id)
         {
-            lint_impl_body(cx, item.span, impl_.items);
+            lint_impl_body(cx, item.owner_id, item.span);
         }
     }
 }
 
-fn lint_impl_body(cx: &LateContext<'_>, impl_span: Span, impl_items: &[hir::ImplItemRef]) {
+fn lint_impl_body(cx: &LateContext<'_>, item_def_id: hir::OwnerId, impl_span: Span) {
     use rustc_hir::intravisit::{self, Visitor};
-    use rustc_hir::{Expr, ImplItemKind};
+    use rustc_hir::Expr;
 
     struct FindPanicUnwrap<'a, 'tcx> {
         lcx: &'a LateContext<'tcx>,
@@ -96,35 +96,35 @@ fn lint_impl_body(cx: &LateContext<'_>, impl_span: Span, impl_items: &[hir::Impl
         }
     }
 
-    for impl_item in impl_items {
-        if impl_item.ident.name == sym::from
-            && let ImplItemKind::Fn(_, body_id) = cx.tcx.hir_impl_item(impl_item.id).kind
-        {
-            // check the body for `begin_panic` or `unwrap`
-            let body = cx.tcx.hir_body(body_id);
-            let mut fpu = FindPanicUnwrap {
-                lcx: cx,
-                typeck_results: cx.tcx.typeck(impl_item.id.owner_id.def_id),
-                result: Vec::new(),
-            };
-            fpu.visit_expr(body.value);
+    for impl_item in cx.tcx.associated_items(item_def_id)
+        .filter_by_name_unhygienic_and_kind(sym::from, ty::AssocTag::Fn)
+    {
+        let impl_item_def_id= impl_item.def_id.expect_local();
 
-            // if we've found one, lint
-            if !fpu.result.is_empty() {
-                span_lint_and_then(
-                    cx,
-                    FALLIBLE_IMPL_FROM,
-                    impl_span,
-                    "consider implementing `TryFrom` instead",
-                    move |diag| {
-                        diag.help(
-                            "`From` is intended for infallible conversions only. \
-                            Use `TryFrom` if there's a possibility for the conversion to fail",
-                        );
-                        diag.span_note(fpu.result, "potential failure(s)");
-                    },
-                );
-            }
+        // check the body for `begin_panic` or `unwrap`
+        let body = cx.tcx.hir_body_owned_by(impl_item_def_id);
+        let mut fpu = FindPanicUnwrap {
+            lcx: cx,
+            typeck_results: cx.tcx.typeck(impl_item_def_id),
+            result: Vec::new(),
+        };
+        fpu.visit_expr(body.value);
+
+        // if we've found one, lint
+        if !fpu.result.is_empty() {
+            span_lint_and_then(
+                cx,
+                FALLIBLE_IMPL_FROM,
+                impl_span,
+                "consider implementing `TryFrom` instead",
+                move |diag| {
+                    diag.help(
+                        "`From` is intended for infallible conversions only. \
+                        Use `TryFrom` if there's a possibility for the conversion to fail",
+                    );
+                    diag.span_note(fpu.result, "potential failure(s)");
+                },
+            );
         }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs
index b3c9e860758..d5abaa547e8 100644
--- a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs
+++ b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs
@@ -2,8 +2,8 @@ use clippy_utils::consts::Constant::{F32, F64, Int};
 use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::{
-    eq_expr_value, get_parent_expr, higher, is_in_const_context, is_inherent_method_call, is_no_std_crate,
-    numeric_literal, peel_blocks, sugg, sym,
+    eq_expr_value, get_parent_expr, has_ambiguous_literal_in_expr, higher, is_in_const_context,
+    is_inherent_method_call, is_no_std_crate, numeric_literal, peel_blocks, sugg, sym,
 };
 use rustc_ast::ast;
 use rustc_errors::Applicability;
@@ -455,7 +455,6 @@ fn is_float_mul_expr<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<(&'
     None
 }
 
-// TODO: Fix rust-lang/rust-clippy#4735
 fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) {
     if let ExprKind::Binary(
         Spanned {
@@ -491,6 +490,14 @@ fn check_mul_add(cx: &LateContext<'_>, expr: &Expr<'_>) {
             return;
         };
 
+        // Check if any variable in the expression has an ambiguous type (could be f32 or f64)
+        // see: https://github.com/rust-lang/rust-clippy/issues/14897
+        if (matches!(recv.kind, ExprKind::Path(_)) || matches!(recv.kind, ExprKind::Call(_, _)))
+            && has_ambiguous_literal_in_expr(cx, recv)
+        {
+            return;
+        }
+
         span_lint_and_sugg(
             cx,
             SUBOPTIMAL_FLOPS,
diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs
index 0c39aae9ca9..16c58ecb455 100644
--- a/src/tools/clippy/clippy_lints/src/format_args.rs
+++ b/src/tools/clippy/clippy_lints/src/format_args.rs
@@ -30,6 +30,7 @@ use rustc_span::edition::Edition::Edition2021;
 use rustc_span::{Span, Symbol, sym};
 use rustc_trait_selection::infer::TyCtxtInferExt;
 use rustc_trait_selection::traits::{Obligation, ObligationCause, Selection, SelectionContext};
+use rustc_attr_data_structures::{AttributeKind, find_attr};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -656,7 +657,7 @@ impl<'tcx> FormatArgsExpr<'_, 'tcx> {
                     };
                     let selection = SelectionContext::new(&infcx).select(&obligation);
                     let derived = if let Ok(Some(Selection::UserDefined(data))) = selection {
-                        tcx.has_attr(data.impl_def_id, sym::automatically_derived)
+                        find_attr!(tcx.get_all_attrs(data.impl_def_id), AttributeKind::AutomaticallyDerived(..))
                     } else {
                         false
                     };
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 be887b03ae4..85b40ba7419 100644
--- a/src/tools/clippy/clippy_lints/src/from_over_into.rs
+++ b/src/tools/clippy/clippy_lints/src/from_over_into.rs
@@ -9,7 +9,7 @@ use clippy_utils::source::SpanRangeExt;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{Visitor, walk_path};
 use rustc_hir::{
-    FnRetTy, GenericArg, GenericArgs, HirId, Impl, ImplItemKind, ImplItemRef, Item, ItemKind, PatKind, Path,
+    FnRetTy, GenericArg, GenericArgs, HirId, Impl, ImplItemKind, ImplItemId, Item, ItemKind, PatKind, Path,
     PathSegment, Ty, TyKind,
 };
 use rustc_lint::{LateContext, LateLintPass};
@@ -102,7 +102,7 @@ impl<'tcx> LateLintPass<'tcx> for FromOverInto {
                         middle_trait_ref.self_ty()
                     );
                     if let Some(suggestions) =
-                        convert_to_from(cx, into_trait_seg, target_ty.as_unambig_ty(), self_ty, impl_item_ref)
+                        convert_to_from(cx, into_trait_seg, target_ty.as_unambig_ty(), self_ty, *impl_item_ref)
                     {
                         diag.multipart_suggestion(message, suggestions, Applicability::MachineApplicable);
                     } else {
@@ -164,14 +164,14 @@ fn convert_to_from(
     into_trait_seg: &PathSegment<'_>,
     target_ty: &Ty<'_>,
     self_ty: &Ty<'_>,
-    impl_item_ref: &ImplItemRef,
+    impl_item_ref: ImplItemId,
 ) -> Option<Vec<(Span, String)>> {
     if !target_ty.find_self_aliases().is_empty() {
         // It's tricky to expand self-aliases correctly, we'll ignore it to not cause a
         // bad suggestion/fix.
         return None;
     }
-    let impl_item = cx.tcx.hir_impl_item(impl_item_ref.id);
+    let impl_item = cx.tcx.hir_impl_item(impl_item_ref);
     let ImplItemKind::Fn(ref sig, body_id) = impl_item.kind else {
         return None;
     };
diff --git a/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs b/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs
index 2d22bb157a9..0d6191f2c97 100644
--- a/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
 use rustc_errors::{Applicability, MultiSpan};
 use rustc_hir::def_id::{DefId, DefIdSet};
 use rustc_hir::hir_id::OwnerId;
-use rustc_hir::{Impl, ImplItem, ImplItemKind, ImplItemRef, ItemKind, Node, TraitRef};
+use rustc_hir::{Impl, ImplItem, ImplItemKind, ItemKind, Node, TraitRef};
 use rustc_lint::LateContext;
 use rustc_span::Span;
 use rustc_span::symbol::{Ident, kw};
@@ -15,11 +15,10 @@ pub(super) fn check_impl_item(cx: &LateContext<'_>, item: &ImplItem<'_>, ignored
         && let parent_node = cx.tcx.parent_hir_node(item.hir_id())
         && let Node::Item(parent_item) = parent_node
         && let ItemKind::Impl(Impl {
-            items,
             of_trait: Some(trait_ref),
             ..
         }) = &parent_item.kind
-        && let Some(did) = trait_item_def_id_of_impl(items, item.owner_id)
+        && let Some(did) = trait_item_def_id_of_impl(cx, item.owner_id)
         && !is_from_ignored_trait(trait_ref, ignored_traits)
     {
         let mut param_idents_iter = cx.tcx.hir_body_param_idents(body_id);
@@ -93,14 +92,8 @@ impl RenamedFnArgs {
 }
 
 /// Get the [`trait_item_def_id`](ImplItemRef::trait_item_def_id) of a relevant impl item.
-fn trait_item_def_id_of_impl(items: &[ImplItemRef], target: OwnerId) -> Option<DefId> {
-    items.iter().find_map(|item| {
-        if item.id.owner_id == target {
-            item.trait_item_def_id
-        } else {
-            None
-        }
-    })
+fn trait_item_def_id_of_impl(cx: &LateContext<'_>, target: OwnerId) -> Option<DefId> {
+    cx.tcx.associated_item(target).trait_item_def_id
 }
 
 fn is_from_ignored_trait(of_trait: &TraitRef<'_>, ignored_traits: &DefIdSet) -> bool {
diff --git a/src/tools/clippy/clippy_lints/src/if_not_else.rs b/src/tools/clippy/clippy_lints/src/if_not_else.rs
index ab7a965b367..25fed0d4dd1 100644
--- a/src/tools/clippy/clippy_lints/src/if_not_else.rs
+++ b/src/tools/clippy/clippy_lints/src/if_not_else.rs
@@ -51,7 +51,6 @@ declare_lint_pass!(IfNotElse => [IF_NOT_ELSE]);
 impl LateLintPass<'_> for IfNotElse {
     fn check_expr(&mut self, cx: &LateContext<'_>, e: &Expr<'_>) {
         if let ExprKind::If(cond, cond_inner, Some(els)) = e.kind
-            && let ExprKind::DropTemps(cond) = cond.kind
             && let ExprKind::Block(..) = els.kind
         {
             let (msg, help) = match cond.kind {
diff --git a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs
index cab7a9fb709..b3c90f364e8 100644
--- a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs
+++ b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs
@@ -130,7 +130,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher {
                     });
 
                     let mut ctr_vis = ImplicitHasherConstructorVisitor::new(cx, target);
-                    for item in impl_.items.iter().map(|item| cx.tcx.hir_impl_item(item.id)) {
+                    for item in impl_.items.iter().map(|&item| cx.tcx.hir_impl_item(item)) {
                         ctr_vis.visit_impl_item(item);
                     }
 
diff --git a/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs b/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs
index 185fc2aa2d4..0fdbf679738 100644
--- a/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs
+++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_add.rs
@@ -41,8 +41,7 @@ declare_lint_pass!(ImplicitSaturatingAdd => [IMPLICIT_SATURATING_ADD]);
 impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
         if let ExprKind::If(cond, then, None) = expr.kind
-            && let ExprKind::DropTemps(expr1) = cond.kind
-            && let Some((c, op_node, l)) = get_const(cx, expr1)
+            && let Some((c, op_node, l)) = get_const(cx, cond)
             && let BinOpKind::Ne | BinOpKind::Lt = op_node
             && let ExprKind::Block(block, None) = then.kind
             && let Block {
@@ -66,7 +65,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingAdd {
             && Some(c) == get_int_max(ty)
             && let ctxt = expr.span.ctxt()
             && ex.span.ctxt() == ctxt
-            && expr1.span.ctxt() == ctxt
+            && cond.span.ctxt() == ctxt
             && clippy_utils::SpanlessEq::new(cx).eq_expr(l, target)
             && AssignOpKind::AddAssign == op1.node
             && let ExprKind::Lit(lit) = value.kind
diff --git a/src/tools/clippy/clippy_lints/src/infallible_try_from.rs b/src/tools/clippy/clippy_lints/src/infallible_try_from.rs
index b54c289fa7e..e79fcec6e6a 100644
--- a/src/tools/clippy/clippy_lints/src/infallible_try_from.rs
+++ b/src/tools/clippy/clippy_lints/src/infallible_try_from.rs
@@ -1,8 +1,9 @@
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::sym;
 use rustc_errors::MultiSpan;
-use rustc_hir::{AssocItemKind, Item, ItemKind};
+use rustc_hir::{Item, ItemKind};
 use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::AssocTag;
 use rustc_session::declare_lint_pass;
 
 declare_clippy_lint! {
@@ -51,25 +52,23 @@ impl<'tcx> LateLintPass<'tcx> for InfallibleTryFrom {
         if !cx.tcx.is_diagnostic_item(sym::TryFrom, trait_def_id) {
             return;
         }
-        for ii in imp.items {
-            if ii.kind == AssocItemKind::Type {
-                let ii = cx.tcx.hir_impl_item(ii.id);
-                if ii.ident.name != sym::Error {
-                    continue;
-                }
-                let ii_ty = ii.expect_type();
-                let ii_ty_span = ii_ty.span;
-                let ii_ty = clippy_utils::ty::ty_from_hir_ty(cx, ii_ty);
-                if !ii_ty.is_inhabited_from(cx.tcx, ii.owner_id.to_def_id(), cx.typing_env()) {
-                    let mut span = MultiSpan::from_span(cx.tcx.def_span(item.owner_id.to_def_id()));
-                    span.push_span_label(ii_ty_span, "infallible error type");
-                    span_lint(
-                        cx,
-                        INFALLIBLE_TRY_FROM,
-                        span,
-                        "infallible TryFrom impl; consider implementing From, instead",
-                    );
-                }
+        for ii in cx.tcx.associated_items(item.owner_id.def_id)
+            .filter_by_name_unhygienic_and_kind(sym::Error, AssocTag::Type)
+        {
+            let ii_ty = cx.tcx.type_of(ii.def_id).instantiate_identity();
+            if !ii_ty.is_inhabited_from(cx.tcx, ii.def_id, cx.typing_env()) {
+                let mut span = MultiSpan::from_span(cx.tcx.def_span(item.owner_id.to_def_id()));
+                let ii_ty_span = cx.tcx.hir_node_by_def_id(ii.def_id.expect_local())
+                    .expect_impl_item()
+                    .expect_type()
+                    .span;
+                span.push_span_label(ii_ty_span, "infallible error type");
+                span_lint(
+                    cx,
+                    INFALLIBLE_TRY_FROM,
+                    span,
+                    "infallible TryFrom impl; consider implementing From, instead",
+                );
             }
         }
     }
diff --git a/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs
index 900b20aa9cf..03038f0ab49 100644
--- a/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs
+++ b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs
@@ -139,13 +139,12 @@ impl LateLintPass<'_> for IterWithoutIntoIter {
                 // We can't check inherent impls for slices, but we know that they have an `iter(_mut)` method
                 ty.peel_refs().is_slice() || get_adt_inherent_method(cx, ty, expected_method_name).is_some()
             })
-            && let Some(iter_assoc_span) = imp.items.iter().find_map(|item| {
-                if item.ident.name == sym::IntoIter {
-                    Some(cx.tcx.hir_impl_item(item.id).expect_type().span)
-                } else {
-                    None
-                }
-            })
+            && let Some(iter_assoc_span) = cx.tcx.associated_items(item.owner_id)
+                .filter_by_name_unhygienic_and_kind(sym::IntoIter, ty::AssocTag::Type)
+                .next()
+                .map(|assoc_item| {
+                    cx.tcx.hir_node_by_def_id(assoc_item.def_id.expect_local()).expect_impl_item().expect_type().span
+                })
             && is_ty_exported(cx, ty)
         {
             span_lint_and_then(
diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs
index aded31971ce..1bf03480c82 100644
--- a/src/tools/clippy/clippy_lints/src/len_zero.rs
+++ b/src/tools/clippy/clippy_lints/src/len_zero.rs
@@ -10,14 +10,15 @@ use rustc_errors::Applicability;
 use rustc_hir::def::Res;
 use rustc_hir::def_id::{DefId, DefIdSet};
 use rustc_hir::{
-    AssocItemKind, BinOpKind, Expr, ExprKind, FnRetTy, GenericArg, GenericBound, HirId, ImplItem, ImplItemKind,
+    BinOpKind, Expr, ExprKind, FnRetTy, GenericArg, GenericBound, HirId, ImplItem, ImplItemKind,
     ImplicitSelfKind, Item, ItemKind, Mutability, Node, OpaqueTyOrigin, PatExprKind, PatKind, PathSegment, PrimTy,
-    QPath, TraitItemRef, TyKind,
+    QPath, TraitItemId, TyKind,
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::{self, FnSig, Ty};
 use rustc_session::declare_lint_pass;
 use rustc_span::source_map::Spanned;
+use rustc_span::symbol::kw;
 use rustc_span::{Ident, Span, Symbol};
 use rustc_trait_selection::traits::supertrait_def_ids;
 
@@ -124,7 +125,7 @@ declare_lint_pass!(LenZero => [LEN_ZERO, LEN_WITHOUT_IS_EMPTY, COMPARISON_TO_EMP
 
 impl<'tcx> LateLintPass<'tcx> for LenZero {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
-        if let ItemKind::Trait(_, _, ident, _, _, trait_items) = item.kind
+        if let ItemKind::Trait(_, _, _, ident, _, _, trait_items) = item.kind
             && !item.span.from_expansion()
         {
             check_trait_items(cx, item, ident, trait_items);
@@ -264,22 +265,13 @@ fn span_without_enclosing_paren(cx: &LateContext<'_>, span: Span) -> Span {
     }
 }
 
-fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, ident: Ident, trait_items: &[TraitItemRef]) {
-    fn is_named_self(cx: &LateContext<'_>, item: &TraitItemRef, name: Symbol) -> bool {
-        item.ident.name == name
-            && if let AssocItemKind::Fn { has_self } = item.kind {
-                has_self && {
-                    cx.tcx
-                        .fn_sig(item.id.owner_id)
-                        .skip_binder()
-                        .inputs()
-                        .skip_binder()
-                        .len()
-                        == 1
-                }
-            } else {
-                false
-            }
+fn check_trait_items(cx: &LateContext<'_>, visited_trait: &Item<'_>, ident: Ident, trait_items: &[TraitItemId]) {
+    fn is_named_self(cx: &LateContext<'_>, item: &TraitItemId, name: Symbol) -> bool {
+        cx.tcx.item_name(item.owner_id) == name
+            && matches!(
+                cx.tcx.fn_arg_idents(item.owner_id),
+                [Some(Ident { name: kw::SelfLower, .. })],
+            )
     }
 
     // fill the set with current and super traits
diff --git a/src/tools/clippy/clippy_lints/src/let_if_seq.rs b/src/tools/clippy/clippy_lints/src/let_if_seq.rs
index 5db28e9ae9b..e480c8fbed5 100644
--- a/src/tools/clippy/clippy_lints/src/let_if_seq.rs
+++ b/src/tools/clippy/clippy_lints/src/let_if_seq.rs
@@ -62,15 +62,8 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq {
             if let hir::StmtKind::Let(local) = stmt.kind
                 && let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind
                 && let hir::StmtKind::Expr(if_) = next.kind
-                && let hir::ExprKind::If(
-                    hir::Expr {
-                        kind: hir::ExprKind::DropTemps(cond),
-                        ..
-                    },
-                    then,
-                    else_,
-                ) = if_.kind
-                && !is_local_used(cx, *cond, canonical_id)
+                && let hir::ExprKind::If(cond, then, else_) = if_.kind
+                && !is_local_used(cx, cond, canonical_id)
                 && let hir::ExprKind::Block(then, _) = then.kind
                 && let Some(value) = check_assign(cx, canonical_id, then)
                 && !is_local_used(cx, value, canonical_id)
diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs
index caf17c10484..35c9d2fd4eb 100644
--- a/src/tools/clippy/clippy_lints/src/lifetimes.rs
+++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs
@@ -716,7 +716,7 @@ fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<'
         walk_trait_ref(&mut checker, trait_ref);
     }
     walk_unambig_ty(&mut checker, impl_.self_ty);
-    for item in impl_.items {
+    for &item in impl_.items {
         walk_impl_item_ref(&mut checker, item);
     }
 
diff --git a/src/tools/clippy/clippy_lints/src/loops/empty_loop.rs b/src/tools/clippy/clippy_lints/src/loops/empty_loop.rs
index 823cf0f4322..e809987d75a 100644
--- a/src/tools/clippy/clippy_lints/src/loops/empty_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/empty_loop.rs
@@ -1,11 +1,22 @@
 use super::EMPTY_LOOP;
 use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::{is_in_panic_handler, is_no_std_crate};
+use clippy_utils::{is_in_panic_handler, is_no_std_crate, sym};
 
-use rustc_hir::{Block, Expr};
+use rustc_hir::{Block, Expr, ItemKind, Node};
 use rustc_lint::LateContext;
 
 pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, loop_block: &Block<'_>) {
+    let parent_hir_id = cx.tcx.parent_hir_id(expr.hir_id);
+    if let Node::Item(parent_node) = cx.tcx.hir_node(parent_hir_id)
+        && matches!(parent_node.kind, ItemKind::Fn { .. })
+        && let attrs = cx.tcx.hir_attrs(parent_hir_id)
+        && attrs.iter().any(|attr| attr.has_name(sym::rustc_intrinsic))
+    {
+        // Intrinsic functions are expanded into an empty loop when lowering the AST
+        // to simplify the job of later passes which might expect any function to have a body.
+        return;
+    }
+
     if loop_block.stmts.is_empty() && loop_block.expr.is_none() && !is_in_panic_handler(cx, expr) {
         let msg = "empty `loop {}` wastes CPU cycles";
         let help = if is_no_std_crate(cx) {
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 9ff82cdcb66..1f9a943f13d 100644
--- a/src/tools/clippy/clippy_lints/src/manual_let_else.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_let_else.rs
@@ -245,17 +245,36 @@ fn replace_in_pattern(
         }
 
         match pat.kind {
-            PatKind::Binding(_ann, _id, binding_name, opt_subpt) => {
-                let Some((pat_to_put, binding_mode)) = ident_map.get(&binding_name.name) else {
-                    break 'a;
-                };
-                let sn_pfx = binding_mode.prefix_str();
-                let (sn_ptp, _) = snippet_with_context(cx, pat_to_put.span, span.ctxt(), "", app);
-                if let Some(subpt) = opt_subpt {
-                    let subpt = replace_in_pattern(cx, span, ident_map, subpt, app, false);
-                    return format!("{sn_pfx}{sn_ptp} @ {subpt}");
+            PatKind::Binding(ann, _id, binding_name, opt_subpt) => {
+                match (ident_map.get(&binding_name.name), opt_subpt) {
+                    (Some((pat_to_put, binding_mode)), opt_subpt) => {
+                        let sn_pfx = binding_mode.prefix_str();
+                        let (sn_ptp, _) = snippet_with_context(cx, pat_to_put.span, span.ctxt(), "", app);
+                        if let Some(subpt) = opt_subpt {
+                            let subpt = replace_in_pattern(cx, span, ident_map, subpt, app, false);
+                            return format!("{sn_pfx}{sn_ptp} @ {subpt}");
+                        }
+                        return format!("{sn_pfx}{sn_ptp}");
+                    },
+                    (None, Some(subpt)) => {
+                        let subpt = replace_in_pattern(cx, span, ident_map, subpt, app, false);
+                        // scanning for a value that matches is not sensitive to order
+                        #[expect(rustc::potential_query_instability)]
+                        if ident_map.values().any(|(other_pat, _)| {
+                            if let PatKind::Binding(_, _, other_name, _) = other_pat.kind {
+                                other_name == binding_name
+                            } else {
+                                false
+                            }
+                        }) {
+                            // this name is shadowed, and, therefore, not usable
+                            return subpt;
+                        }
+                        let binding_pfx = ann.prefix_str();
+                        return format!("{binding_pfx}{binding_name} @ {subpt}");
+                    },
+                    (None, None) => break 'a,
                 }
-                return format!("{sn_pfx}{sn_ptp}");
             },
             PatKind::Or(pats) => {
                 let patterns = pats
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 51696b38880..2d52a93f34e 100644
--- a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
@@ -4,16 +4,15 @@ use clippy_utils::is_doc_hidden;
 use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_indent;
 use itertools::Itertools;
+use rustc_attr_data_structures::{AttributeKind, find_attr};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
 use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
 use rustc_hir::{Expr, ExprKind, Item, ItemKind, QPath, TyKind, VariantData};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
-use rustc_span::def_id::LocalDefId;
 use rustc_span::Span;
-use rustc_attr_data_structures::find_attr;
-use rustc_attr_data_structures::AttributeKind;
+use rustc_span::def_id::LocalDefId;
 
 declare_clippy_lint! {
     /// ### What it does
diff --git a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs
index ede68f30941..ae277da089f 100644
--- a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs
@@ -332,9 +332,7 @@ impl<'a> NormalizedPat<'a> {
                 // TODO: Handle negative integers. They're currently treated as a wild match.
                 PatExprKind::Lit { lit, negated: false } => match lit.node {
                     LitKind::Str(sym, _) => Self::LitStr(sym),
-                    LitKind::ByteStr(byte_sym, _) | LitKind::CStr(byte_sym, _) => {
-                        Self::LitBytes(byte_sym)
-                    }
+                    LitKind::ByteStr(byte_sym, _) | LitKind::CStr(byte_sym, _) => Self::LitBytes(byte_sym),
                     LitKind::Byte(val) => Self::LitInt(val.into()),
                     LitKind::Char(val) => Self::LitInt(val.into()),
                     LitKind::Int(val, _) => Self::LitInt(val.get()),
diff --git a/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs b/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs
index 045363058d1..d664eaaac70 100644
--- a/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/from_iter_instead_of_collect.rs
@@ -4,6 +4,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::implements_trait;
 use clippy_utils::{is_path_diagnostic_item, sugg};
+use rustc_ast::join_path_idents;
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
 use rustc_hir::{self as hir, Expr, ExprKind, GenericArg, QPath, TyKind};
@@ -47,7 +48,7 @@ fn build_full_type(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, app: &mut Applica
         && let QPath::Resolved(None, ty_path) = &ty_qpath
         && let Res::Def(_, ty_did) = ty_path.res
     {
-        let mut ty_str = itertools::join(ty_path.segments.iter().map(|s| s.ident), "::");
+        let mut ty_str = join_path_idents(ty_path.segments.iter().map(|seg| seg.ident));
         let mut first = true;
         let mut append = |arg: &str| {
             write!(&mut ty_str, "{}{arg}", [", ", "<"][usize::from(first)]).unwrap();
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 4a61c223d2c..93325ca488e 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,14 +1,16 @@
 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, snippet_opt};
+use clippy_utils::source::{snippet, snippet_with_applicability};
+use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{get_parent_expr, sym};
+use rustc_ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
-use rustc_hir::{BinOpKind, Expr, ExprKind, QPath};
+use rustc_hir::{BinOpKind, Closure, Expr, ExprKind, QPath};
 use rustc_lint::LateContext;
 use rustc_middle::ty;
-use rustc_span::{BytePos, Span, sym};
+use rustc_span::{Span, Symbol};
 
 use super::MANUAL_IS_VARIANT_AND;
 
@@ -62,54 +64,154 @@ pub(super) fn check(
     );
 }
 
-fn emit_lint(cx: &LateContext<'_>, op: BinOpKind, parent: &Expr<'_>, method_span: Span, is_option: bool) {
-    if let Some(before_map_snippet) = snippet_opt(cx, parent.span.with_hi(method_span.lo()))
-        && let Some(after_map_snippet) = snippet_opt(cx, method_span.with_lo(method_span.lo() + BytePos(3)))
-    {
-        span_lint_and_sugg(
-            cx,
-            MANUAL_IS_VARIANT_AND,
-            parent.span,
-            format!(
-                "called `.map() {}= {}()`",
-                if op == BinOpKind::Eq { '=' } else { '!' },
-                if is_option { "Some" } else { "Ok" },
-            ),
-            "use",
-            if is_option && op == BinOpKind::Ne {
-                format!("{before_map_snippet}is_none_or{after_map_snippet}",)
-            } else {
+#[derive(Clone, Copy, PartialEq)]
+enum Flavor {
+    Option,
+    Result,
+}
+
+impl Flavor {
+    const fn symbol(self) -> Symbol {
+        match self {
+            Self::Option => sym::Option,
+            Self::Result => sym::Result,
+        }
+    }
+
+    const fn positive(self) -> Symbol {
+        match self {
+            Self::Option => sym::Some,
+            Self::Result => sym::Ok,
+        }
+    }
+}
+
+#[derive(Clone, Copy, PartialEq)]
+enum Op {
+    Eq,
+    Ne,
+}
+
+impl std::fmt::Display for Op {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Self::Eq => write!(f, "=="),
+            Self::Ne => write!(f, "!="),
+        }
+    }
+}
+
+impl TryFrom<BinOpKind> for Op {
+    type Error = ();
+    fn try_from(op: BinOpKind) -> Result<Self, Self::Error> {
+        match op {
+            BinOpKind::Eq => Ok(Self::Eq),
+            BinOpKind::Ne => Ok(Self::Ne),
+            _ => Err(()),
+        }
+    }
+}
+
+/// Represents the argument of the `.map()` function, as a closure or as a path
+/// in case η-reduction is used.
+enum MapFunc<'hir> {
+    Closure(&'hir Closure<'hir>),
+    Path(&'hir Expr<'hir>),
+}
+
+impl<'hir> TryFrom<&'hir Expr<'hir>> for MapFunc<'hir> {
+    type Error = ();
+
+    fn try_from(expr: &'hir Expr<'hir>) -> Result<Self, Self::Error> {
+        match expr.kind {
+            ExprKind::Closure(closure) => Ok(Self::Closure(closure)),
+            ExprKind::Path(_) => Ok(Self::Path(expr)),
+            _ => Err(()),
+        }
+    }
+}
+
+impl<'hir> MapFunc<'hir> {
+    /// Build a suggestion suitable for use in a `.map()`-like function. η-expansion will be applied
+    /// as needed.
+    fn sugg(self, cx: &LateContext<'hir>, invert: bool, app: &mut Applicability) -> String {
+        match self {
+            Self::Closure(closure) => {
+                let body = Sugg::hir_with_applicability(cx, cx.tcx.hir_body(closure.body).value, "..", app);
                 format!(
-                    "{}{before_map_snippet}{}{after_map_snippet}",
-                    if op == BinOpKind::Eq { "" } else { "!" },
-                    if is_option { "is_some_and" } else { "is_ok_and" },
+                    "{} {}",
+                    snippet_with_applicability(cx, closure.fn_decl_span, "|..|", app),
+                    if invert { !body } else { body }
                 )
             },
-            Applicability::MachineApplicable,
-        );
+            Self::Path(expr) => {
+                let path = snippet_with_applicability(cx, expr.span, "_", app);
+                if invert {
+                    format!("|x| !{path}(x)")
+                } else {
+                    path.to_string()
+                }
+            },
+        }
     }
 }
 
+fn emit_lint<'tcx>(
+    cx: &LateContext<'tcx>,
+    span: Span,
+    op: Op,
+    flavor: Flavor,
+    in_some_or_ok: bool,
+    map_func: MapFunc<'tcx>,
+    recv: &Expr<'_>,
+) {
+    let mut app = Applicability::MachineApplicable;
+    let recv = snippet_with_applicability(cx, recv.span, "_", &mut app);
+
+    let (invert_expr, method, invert_body) = match (flavor, op) {
+        (Flavor::Option, Op::Eq) => (false, "is_some_and", !in_some_or_ok),
+        (Flavor::Option, Op::Ne) => (false, "is_none_or", in_some_or_ok),
+        (Flavor::Result, Op::Eq) => (false, "is_ok_and", !in_some_or_ok),
+        (Flavor::Result, Op::Ne) => (true, "is_ok_and", !in_some_or_ok),
+    };
+    span_lint_and_sugg(
+        cx,
+        MANUAL_IS_VARIANT_AND,
+        span,
+        format!("called `.map() {op} {pos}()`", pos = flavor.positive(),),
+        "use",
+        format!(
+            "{inversion}{recv}.{method}({body})",
+            inversion = if invert_expr { "!" } else { "" },
+            body = map_func.sugg(cx, invert_body, &mut app),
+        ),
+        app,
+    );
+}
+
 pub(super) fn check_map(cx: &LateContext<'_>, expr: &Expr<'_>) {
     if let Some(parent_expr) = get_parent_expr(cx, expr)
         && let ExprKind::Binary(op, left, right) = parent_expr.kind
-        && matches!(op.node, BinOpKind::Eq | BinOpKind::Ne)
         && op.span.eq_ctxt(expr.span)
+        && let Ok(op) = Op::try_from(op.node)
     {
         // Check `left` and `right` expression in any order, and for `Option` and `Result`
         for (expr1, expr2) in [(left, right), (right, left)] {
-            for item in [sym::Option, sym::Result] {
-                if let ExprKind::Call(call, ..) = expr1.kind
+            for flavor in [Flavor::Option, Flavor::Result] {
+                if let ExprKind::Call(call, [arg]) = expr1.kind
+                    && let ExprKind::Lit(lit) = arg.kind
+                    && let LitKind::Bool(bool_cst) = lit.node
                     && let ExprKind::Path(QPath::Resolved(_, path)) = call.kind
                     && let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), _) = path.res
                     && let ty = cx.typeck_results().expr_ty(expr1)
                     && let ty::Adt(adt, args) = ty.kind()
-                    && cx.tcx.is_diagnostic_item(item, adt.did())
+                    && cx.tcx.is_diagnostic_item(flavor.symbol(), adt.did())
                     && args.type_at(0).is_bool()
-                    && let ExprKind::MethodCall(_, recv, _, span) = expr2.kind
-                    && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), item)
+                    && let ExprKind::MethodCall(_, recv, [map_expr], _) = expr2.kind
+                    && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), flavor.symbol())
+                    && let Ok(map_func) = MapFunc::try_from(map_expr)
                 {
-                    return emit_lint(cx, op.node, parent_expr, span, item == sym::Option);
+                    return emit_lint(cx, parent_expr.span, op, flavor, bool_cst, map_func, recv);
                 }
             }
         }
diff --git a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs
index 2139466ce74..6ce7dd3d4d0 100644
--- a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs
@@ -136,7 +136,7 @@ pub(super) fn check<'tcx>(
         fun_span: Option<Span>,
     ) -> bool {
         // (path, fn_has_argument, methods, suffix)
-        const KNOW_TYPES: [(Symbol, bool, &[Symbol], &str); 5] = [
+        const KNOW_TYPES: [(Symbol, bool, &[Symbol], &str); 7] = [
             (sym::BTreeEntry, false, &[sym::or_insert], "with"),
             (sym::HashMapEntry, false, &[sym::or_insert], "with"),
             (
@@ -146,7 +146,9 @@ pub(super) fn check<'tcx>(
                 "else",
             ),
             (sym::Option, false, &[sym::get_or_insert], "with"),
+            (sym::Option, true, &[sym::and], "then"),
             (sym::Result, true, &[sym::map_or, sym::or, sym::unwrap_or], "else"),
+            (sym::Result, true, &[sym::and], "then"),
         ];
 
         if KNOW_TYPES.iter().any(|k| k.2.contains(&name))
diff --git a/src/tools/clippy/clippy_lints/src/methods/return_and_then.rs b/src/tools/clippy/clippy_lints/src/methods/return_and_then.rs
index df8544f9220..54f38a322b8 100644
--- a/src/tools/clippy/clippy_lints/src/methods/return_and_then.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/return_and_then.rs
@@ -1,5 +1,5 @@
 use rustc_errors::Applicability;
-use rustc_hir as hir;
+use rustc_hir::{self as hir, Node};
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, GenericArg, Ty};
 use rustc_span::sym;
@@ -9,7 +9,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::{indent_of, reindent_multiline, snippet_with_applicability};
 use clippy_utils::ty::get_type_diagnostic_name;
 use clippy_utils::visitors::for_each_unconsumed_temporary;
-use clippy_utils::{get_parent_expr, peel_blocks};
+use clippy_utils::{peel_blocks, potential_return_of_enclosing_body};
 
 use super::RETURN_AND_THEN;
 
@@ -21,7 +21,7 @@ pub(super) fn check<'tcx>(
     recv: &'tcx hir::Expr<'tcx>,
     arg: &'tcx hir::Expr<'_>,
 ) {
-    if cx.tcx.hir_get_fn_id_for_return_block(expr.hir_id).is_none() {
+    if !potential_return_of_enclosing_body(cx, expr) {
         return;
     }
 
@@ -55,15 +55,28 @@ pub(super) fn check<'tcx>(
         None => &body_snip,
     };
 
-    // If suggestion is going to get inserted as part of a `return` expression, it must be blockified.
-    let sugg = if let Some(parent_expr) = get_parent_expr(cx, expr) {
-        let base_indent = indent_of(cx, parent_expr.span);
+    // If suggestion is going to get inserted as part of a `return` expression or as a match expression
+    // arm, it must be blockified.
+    let (parent_span_for_indent, opening_paren, closing_paren) = match cx.tcx.parent_hir_node(expr.hir_id) {
+        Node::Expr(parent_expr) if matches!(parent_expr.kind, hir::ExprKind::Break(..)) => {
+            (Some(parent_expr.span), "(", ")")
+        },
+        Node::Expr(parent_expr) => (Some(parent_expr.span), "", ""),
+        Node::Arm(match_arm) => (Some(match_arm.span), "", ""),
+        _ => (None, "", ""),
+    };
+    let sugg = if let Some(span) = parent_span_for_indent {
+        let base_indent = indent_of(cx, span);
         let inner_indent = base_indent.map(|i| i + 4);
         format!(
             "{}\n{}\n{}",
-            reindent_multiline(&format!("{{\nlet {arg_snip} = {recv_snip}?;"), true, inner_indent),
+            reindent_multiline(
+                &format!("{opening_paren}{{\nlet {arg_snip} = {recv_snip}?;"),
+                true,
+                inner_indent
+            ),
             reindent_multiline(inner, false, inner_indent),
-            reindent_multiline("}", false, base_indent),
+            reindent_multiline(&format!("}}{closing_paren}"), false, base_indent),
         )
     } else {
         format!(
diff --git a/src/tools/clippy/clippy_lints/src/methods/swap_with_temporary.rs b/src/tools/clippy/clippy_lints/src/methods/swap_with_temporary.rs
index de729fb343a..e378cbd6ae0 100644
--- a/src/tools/clippy/clippy_lints/src/methods/swap_with_temporary.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/swap_with_temporary.rs
@@ -4,6 +4,7 @@ use rustc_ast::BorrowKind;
 use rustc_errors::{Applicability, Diag};
 use rustc_hir::{Expr, ExprKind, Node, QPath};
 use rustc_lint::LateContext;
+use rustc_middle::ty::adjustment::Adjust;
 use rustc_span::sym;
 
 use super::SWAP_WITH_TEMPORARY;
@@ -11,12 +12,12 @@ use super::SWAP_WITH_TEMPORARY;
 const MSG_TEMPORARY: &str = "this expression returns a temporary value";
 const MSG_TEMPORARY_REFMUT: &str = "this is a mutable reference to a temporary value";
 
-pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args: &[Expr<'_>]) {
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>, func: &Expr<'_>, args: &'tcx [Expr<'_>]) {
     if let ExprKind::Path(QPath::Resolved(_, func_path)) = func.kind
         && let Some(func_def_id) = func_path.res.opt_def_id()
         && cx.tcx.is_diagnostic_item(sym::mem_swap, func_def_id)
     {
-        match (ArgKind::new(&args[0]), ArgKind::new(&args[1])) {
+        match (ArgKind::new(cx, &args[0]), ArgKind::new(cx, &args[1])) {
             (ArgKind::RefMutToTemp(left_temp), ArgKind::RefMutToTemp(right_temp)) => {
                 emit_lint_useless(cx, expr, &args[0], &args[1], left_temp, right_temp);
             },
@@ -28,10 +29,10 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args
 }
 
 enum ArgKind<'tcx> {
-    // Mutable reference to a place, coming from a macro
-    RefMutToPlaceAsMacro(&'tcx Expr<'tcx>),
-    // Place behind a mutable reference
-    RefMutToPlace(&'tcx Expr<'tcx>),
+    // Mutable reference to a place, coming from a macro, and number of dereferences to use
+    RefMutToPlaceAsMacro(&'tcx Expr<'tcx>, usize),
+    // Place behind a mutable reference, and number of dereferences to use
+    RefMutToPlace(&'tcx Expr<'tcx>, usize),
     // Temporary value behind a mutable reference
     RefMutToTemp(&'tcx Expr<'tcx>),
     // Any other case
@@ -39,13 +40,29 @@ enum ArgKind<'tcx> {
 }
 
 impl<'tcx> ArgKind<'tcx> {
-    fn new(arg: &'tcx Expr<'tcx>) -> Self {
-        if let ExprKind::AddrOf(BorrowKind::Ref, _, target) = arg.kind {
-            if target.is_syntactic_place_expr() {
+    /// Build a new `ArgKind` from `arg`. There must be no false positive when returning a
+    /// `ArgKind::RefMutToTemp` variant, as this may cause a spurious lint to be emitted.
+    fn new(cx: &LateContext<'tcx>, arg: &'tcx Expr<'tcx>) -> Self {
+        if let ExprKind::AddrOf(BorrowKind::Ref, _, target) = arg.kind
+            && let adjustments = cx.typeck_results().expr_adjustments(arg)
+            && adjustments
+                .first()
+                .is_some_and(|adj| matches!(adj.kind, Adjust::Deref(None)))
+            && adjustments
+                .last()
+                .is_some_and(|adj| matches!(adj.kind, Adjust::Borrow(_)))
+        {
+            let extra_derefs = adjustments[1..adjustments.len() - 1]
+                .iter()
+                .filter(|adj| matches!(adj.kind, Adjust::Deref(_)))
+                .count();
+            // If a deref is used, `arg` might be a place expression. For example, a mutex guard
+            // would dereference into the mutex content which is probably not temporary.
+            if target.is_syntactic_place_expr() || extra_derefs > 0 {
                 if arg.span.from_expansion() {
-                    ArgKind::RefMutToPlaceAsMacro(arg)
+                    ArgKind::RefMutToPlaceAsMacro(arg, extra_derefs)
                 } else {
-                    ArgKind::RefMutToPlace(target)
+                    ArgKind::RefMutToPlace(target, extra_derefs)
                 }
             } else {
                 ArgKind::RefMutToTemp(target)
@@ -106,10 +123,15 @@ fn emit_lint_assign(cx: &LateContext<'_>, expr: &Expr<'_>, target: &ArgKind<'_>,
                 let mut applicability = Applicability::MachineApplicable;
                 let ctxt = expr.span.ctxt();
                 let assign_target = match target {
-                    ArgKind::Expr(target) | ArgKind::RefMutToPlaceAsMacro(target) => {
-                        Sugg::hir_with_context(cx, target, ctxt, "_", &mut applicability).deref()
-                    },
-                    ArgKind::RefMutToPlace(target) => Sugg::hir_with_context(cx, target, ctxt, "_", &mut applicability),
+                    ArgKind::Expr(target) => Sugg::hir_with_context(cx, target, ctxt, "_", &mut applicability).deref(),
+                    ArgKind::RefMutToPlaceAsMacro(arg, derefs) => (0..*derefs).fold(
+                        Sugg::hir_with_context(cx, arg, ctxt, "_", &mut applicability).deref(),
+                        |sugg, _| sugg.deref(),
+                    ),
+                    ArgKind::RefMutToPlace(target, derefs) => (0..*derefs).fold(
+                        Sugg::hir_with_context(cx, target, ctxt, "_", &mut applicability),
+                        |sugg, _| sugg.deref(),
+                    ),
                     ArgKind::RefMutToTemp(_) => unreachable!(),
                 };
                 let assign_source = Sugg::hir_with_context(cx, temp, ctxt, "_", &mut applicability);
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 b90748dd158..4a9007c607c 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
@@ -62,7 +62,8 @@ pub(super) fn check<'a>(
 
     let ext_def_span = def.span.until(map.span);
 
-    let (sugg, method, applicability) = if let ExprKind::Closure(map_closure) = map.kind
+    let (sugg, method, applicability) = if cx.typeck_results().expr_adjustments(recv).is_empty()
+            && 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
diff --git a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs
index d4d33029dbd..760ecf07589 100644
--- a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs
@@ -8,7 +8,7 @@ use rustc_ast::LitKind;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{
-    Block, Expr, ExprKind, Impl, ImplItem, ImplItemKind, Item, ItemKind, LangItem, Node, QPath, TyKind, VariantData,
+    Block, Expr, ExprKind, Impl, Item, ItemKind, LangItem, Node, QPath, TyKind, VariantData,
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::{Ty, TypeckResults};
@@ -200,7 +200,7 @@ fn check_struct<'tcx>(
 impl<'tcx> LateLintPass<'tcx> for MissingFieldsInDebug {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
         // is this an `impl Debug for X` block?
-        if let ItemKind::Impl(Impl { of_trait: Some(trait_ref), self_ty, items, .. }) = item.kind
+        if let ItemKind::Impl(Impl { of_trait: Some(trait_ref), self_ty, .. }) = item.kind
             && let Res::Def(DefKind::Trait, trait_def_id) = trait_ref.path.res
             && let TyKind::Path(QPath::Resolved(_, self_path)) = &self_ty.kind
             // make sure that the self type is either a struct, an enum or a union
@@ -212,9 +212,8 @@ impl<'tcx> LateLintPass<'tcx> for MissingFieldsInDebug {
             && !cx.tcx.is_automatically_derived(item.owner_id.to_def_id())
             && !item.span.from_expansion()
             // find `Debug::fmt` function
-            && let Some(fmt_item) = items.iter().find(|i| i.ident.name == sym::fmt)
-            && let ImplItem { kind: ImplItemKind::Fn(_, body_id), .. } = cx.tcx.hir_impl_item(fmt_item.id)
-            && let body = cx.tcx.hir_body(*body_id)
+            && let Some(fmt_item) = cx.tcx.associated_items(item.owner_id).filter_by_name_unhygienic(sym::fmt).next()
+            && let body = cx.tcx.hir_body_owned_by(fmt_item.def_id.expect_local())
             && let ExprKind::Block(block, _) = body.value.kind
             // inspect `self`
             && let self_ty = cx.tcx.type_of(self_path_did).skip_binder().peel_refs()
@@ -222,7 +221,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingFieldsInDebug {
             && let Some(self_def_id) = self_adt.did().as_local()
             && let Node::Item(self_item) = cx.tcx.hir_node_by_def_id(self_def_id)
             // NB: can't call cx.typeck_results() as we are not in a body
-            && let typeck_results = cx.tcx.typeck_body(*body_id)
+            && let typeck_results = cx.tcx.typeck_body(body.id())
             && should_lint(cx, typeck_results, block)
             // we intentionally only lint structs, see lint description
             && let ItemKind::Struct(_, _, data) = &self_item.kind
diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs
index 25c95d23436..c4a3d10299b 100644
--- a/src/tools/clippy/clippy_lints/src/missing_inline.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs
@@ -101,19 +101,19 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline {
                 let attrs = cx.tcx.hir_attrs(it.hir_id());
                 check_missing_inline_attrs(cx, attrs, it.span, desc);
             },
-            hir::ItemKind::Trait(ref _is_auto, ref _unsafe, _ident, _generics, _bounds, trait_items) => {
+            hir::ItemKind::Trait(ref _constness, ref _is_auto, ref _unsafe, _ident, _generics, _bounds, trait_items) => {
                 // note: we need to check if the trait is exported so we can't use
                 // `LateLintPass::check_trait_item` here.
-                for tit in trait_items {
-                    let tit_ = cx.tcx.hir_trait_item(tit.id);
+                for &tit in trait_items {
+                    let tit_ = cx.tcx.hir_trait_item(tit);
                     match tit_.kind {
                         hir::TraitItemKind::Const(..) | hir::TraitItemKind::Type(..) => {},
                         hir::TraitItemKind::Fn(..) => {
-                            if cx.tcx.defaultness(tit.id.owner_id).has_value() {
+                            if cx.tcx.defaultness(tit.owner_id).has_value() {
                                 // trait method with default body needs inline in case
                                 // an impl is not provided
                                 let desc = "a default trait method";
-                                let item = cx.tcx.hir_trait_item(tit.id);
+                                let item = cx.tcx.hir_trait_item(tit);
                                 let attrs = cx.tcx.hir_attrs(item.hir_id());
                                 check_missing_inline_attrs(cx, attrs, item.span, desc);
                             }
diff --git a/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs b/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs
index e266c36b6e7..fa61d0fa11a 100644
--- a/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs
@@ -61,15 +61,14 @@ impl<'tcx> LateLintPass<'tcx> for MissingTraitMethods {
         if !is_lint_allowed(cx, MISSING_TRAIT_METHODS, item.hir_id())
             && span_is_local(item.span)
             && let ItemKind::Impl(Impl {
-                items,
                 of_trait: Some(trait_ref),
                 ..
             }) = item.kind
             && let Some(trait_id) = trait_ref.trait_def_id()
         {
-            let trait_item_ids: DefIdSet = items
-                .iter()
-                .filter_map(|impl_item| impl_item.trait_item_def_id)
+            let trait_item_ids: DefIdSet = cx.tcx.associated_items(item.owner_id)
+                .in_definition_order()
+                .filter_map(|assoc_item| assoc_item.trait_item_def_id)
                 .collect();
 
             for assoc in cx
diff --git a/src/tools/clippy/clippy_lints/src/needless_bool.rs b/src/tools/clippy/clippy_lints/src/needless_bool.rs
index 3ed4b1c2ea9..b3aa1a7286a 100644
--- a/src/tools/clippy/clippy_lints/src/needless_bool.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_bool.rs
@@ -199,11 +199,16 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool {
                 let mut applicability = Applicability::MachineApplicable;
                 let cond = Sugg::hir_with_applicability(cx, cond, "..", &mut applicability);
                 let lhs = snippet_with_applicability(cx, lhs_a.span, "..", &mut applicability);
-                let sugg = if a == b {
+                let mut sugg = if a == b {
                     format!("{cond}; {lhs} = {a:?};")
                 } else {
                     format!("{lhs} = {};", if a { cond } else { !cond })
                 };
+
+                if is_else_clause(cx.tcx, e) {
+                    sugg = format!("{{ {sugg} }}");
+                }
+
                 span_lint_and_sugg(
                     cx,
                     NEEDLESS_BOOL_ASSIGN,
diff --git a/src/tools/clippy/clippy_lints/src/neg_multiply.rs b/src/tools/clippy/clippy_lints/src/neg_multiply.rs
index 442280f9998..946114e1041 100644
--- a/src/tools/clippy/clippy_lints/src/neg_multiply.rs
+++ b/src/tools/clippy/clippy_lints/src/neg_multiply.rs
@@ -1,13 +1,13 @@
 use clippy_utils::consts::{self, Constant};
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_with_context;
+use clippy_utils::get_parent_expr;
+use clippy_utils::source::{snippet, snippet_with_context};
 use clippy_utils::sugg::has_enclosing_paren;
 use rustc_ast::util::parser::ExprPrecedence;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
-use rustc_span::Span;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -33,6 +33,19 @@ declare_clippy_lint! {
 
 declare_lint_pass!(NegMultiply => [NEG_MULTIPLY]);
 
+fn is_in_parens_with_postfix(cx: &LateContext<'_>, mul_expr: &Expr<'_>) -> bool {
+    if let Some(parent) = get_parent_expr(cx, mul_expr) {
+        let mult_snippet = snippet(cx, mul_expr.span, "");
+        if has_enclosing_paren(&mult_snippet)
+            && let ExprKind::MethodCall(_, _, _, _) = parent.kind
+        {
+            return true;
+        }
+    }
+
+    false
+}
+
 impl<'tcx> LateLintPass<'tcx> for NegMultiply {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
         if let ExprKind::Binary(ref op, left, right) = e.kind
@@ -40,15 +53,15 @@ impl<'tcx> LateLintPass<'tcx> for NegMultiply {
         {
             match (&left.kind, &right.kind) {
                 (&ExprKind::Unary(..), &ExprKind::Unary(..)) => {},
-                (&ExprKind::Unary(UnOp::Neg, lit), _) => check_mul(cx, e.span, lit, right),
-                (_, &ExprKind::Unary(UnOp::Neg, lit)) => check_mul(cx, e.span, lit, left),
+                (&ExprKind::Unary(UnOp::Neg, lit), _) => check_mul(cx, e, lit, right),
+                (_, &ExprKind::Unary(UnOp::Neg, lit)) => check_mul(cx, e, lit, left),
                 _ => {},
             }
         }
     }
 }
 
-fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) {
+fn check_mul(cx: &LateContext<'_>, mul_expr: &Expr<'_>, lit: &Expr<'_>, exp: &Expr<'_>) {
     const F16_ONE: u16 = 1.0_f16.to_bits();
     const F128_ONE: u128 = 1.0_f128.to_bits();
     if let ExprKind::Lit(l) = lit.kind
@@ -63,8 +76,19 @@ fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) {
         && cx.typeck_results().expr_ty(exp).is_numeric()
     {
         let mut applicability = Applicability::MachineApplicable;
-        let (snip, from_macro) = snippet_with_context(cx, exp.span, span.ctxt(), "..", &mut applicability);
-        let suggestion = if !from_macro && cx.precedence(exp) < ExprPrecedence::Prefix && !has_enclosing_paren(&snip) {
+        let (snip, from_macro) = snippet_with_context(cx, exp.span, mul_expr.span.ctxt(), "..", &mut applicability);
+
+        let needs_parens_for_postfix = is_in_parens_with_postfix(cx, mul_expr);
+
+        let suggestion = if needs_parens_for_postfix {
+            // Special case: when the multiplication is in parentheses followed by a method call
+            // we need to preserve the grouping but negate the inner expression.
+            // Consider this expression: `((a.delta - 0.5).abs() * -1.0).total_cmp(&1.0)`
+            // We need to end up with: `(-(a.delta - 0.5).abs()).total_cmp(&1.0)`
+            // Otherwise, without the parentheses we would try to negate an Ordering:
+            // `-(a.delta - 0.5).abs().total_cmp(&1.0)`
+            format!("(-{snip})")
+        } else if !from_macro && cx.precedence(exp) < ExprPrecedence::Prefix && !has_enclosing_paren(&snip) {
             format!("-({snip})")
         } else {
             format!("-{snip}")
@@ -72,7 +96,7 @@ fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) {
         span_lint_and_sugg(
             cx,
             NEG_MULTIPLY,
-            span,
+            mul_expr.span,
             "this multiplication by -1 can be written more succinctly",
             "consider using",
             suggestion,
diff --git a/src/tools/clippy/clippy_lints/src/new_without_default.rs b/src/tools/clippy/clippy_lints/src/new_without_default.rs
index 4b73a4455f5..3b86f1d1f59 100644
--- a/src/tools/clippy/clippy_lints/src/new_without_default.rs
+++ b/src/tools/clippy/clippy_lints/src/new_without_default.rs
@@ -6,6 +6,7 @@ use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_hir::HirIdSet;
 use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::ty::AssocKind;
 use rustc_session::impl_lint_pass;
 use rustc_span::sym;
 
@@ -61,18 +62,18 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
             of_trait: None,
             generics,
             self_ty: impl_self_ty,
-            items,
             ..
         }) = item.kind
         {
-            for assoc_item in *items {
-                if assoc_item.kind == (hir::AssocItemKind::Fn { has_self: false }) {
-                    let impl_item = cx.tcx.hir_impl_item(assoc_item.id);
+            for assoc_item in cx.tcx.associated_items(item.owner_id.def_id)
+                .filter_by_name_unhygienic(sym::new)
+            {
+                if let AssocKind::Fn { has_self: false, .. } = assoc_item.kind {
+                    let impl_item = cx.tcx.hir_node_by_def_id(assoc_item.def_id.expect_local()).expect_impl_item();
                     if impl_item.span.in_external_macro(cx.sess().source_map()) {
                         return;
                     }
                     if let hir::ImplItemKind::Fn(ref sig, _) = impl_item.kind {
-                        let name = impl_item.ident.name;
                         let id = impl_item.owner_id;
                         if sig.header.is_unsafe() {
                             // can't be implemented for unsafe new
@@ -88,11 +89,9 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
                             return;
                         }
                         if sig.decl.inputs.is_empty()
-                            && name == sym::new
                             && cx.effective_visibilities.is_reachable(impl_item.owner_id.def_id)
-                            && let self_def_id = cx.tcx.hir_get_parent_item(id.into())
-                            && let self_ty = cx.tcx.type_of(self_def_id).instantiate_identity()
-                            && self_ty == return_ty(cx, id)
+                            && let self_ty = cx.tcx.type_of(item.owner_id).instantiate_identity()
+                            && self_ty == return_ty(cx, impl_item.owner_id)
                             && let Some(default_trait_id) = cx.tcx.get_diagnostic_item(sym::Default)
                         {
                             if self.impling_types.is_none() {
@@ -111,7 +110,7 @@ impl<'tcx> LateLintPass<'tcx> for NewWithoutDefault {
                             // Check if a Default implementation exists for the Self type, regardless of
                             // generics
                             if let Some(ref impling_types) = self.impling_types
-                                && let self_def = cx.tcx.type_of(self_def_id).instantiate_identity()
+                                && let self_def = cx.tcx.type_of(item.owner_id).instantiate_identity()
                                 && let Some(self_def) = self_def.ty_adt_def()
                                 && let Some(self_local_did) = self_def.did().as_local()
                                 && let self_id = cx.tcx.local_def_id_to_hir_id(self_local_did)
diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs
index 02c48166131..72e6503e7e4 100644
--- a/src/tools/clippy/clippy_lints/src/no_effect.rs
+++ b/src/tools/clippy/clippy_lints/src/no_effect.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
 use clippy_utils::source::SpanRangeExt;
-use clippy_utils::ty::has_drop;
+use clippy_utils::ty::{expr_type_is_certain, has_drop};
 use clippy_utils::{
     in_automatically_derived, is_inside_always_const_context, is_lint_allowed, path_to_local, peel_blocks,
 };
@@ -340,11 +340,13 @@ fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Vec
         },
         ExprKind::Array(v) | ExprKind::Tup(v) => Some(v.iter().collect()),
         ExprKind::Repeat(inner, _)
-        | ExprKind::Cast(inner, _)
         | ExprKind::Type(inner, _)
         | ExprKind::Unary(_, inner)
         | ExprKind::Field(inner, _)
         | ExprKind::AddrOf(_, _, inner) => reduce_expression(cx, inner).or_else(|| Some(vec![inner])),
+        ExprKind::Cast(inner, _) if expr_type_is_certain(cx, inner) => {
+            reduce_expression(cx, inner).or_else(|| Some(vec![inner]))
+        },
         ExprKind::Struct(_, fields, ref base) => {
             if has_drop(cx, cx.typeck_results().expr_ty(expr)) {
                 None
diff --git a/src/tools/clippy/clippy_lints/src/operators/op_ref.rs b/src/tools/clippy/clippy_lints/src/operators/op_ref.rs
index 0faa7b9e646..21e1ab0f4f2 100644
--- a/src/tools/clippy/clippy_lints/src/operators/op_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/op_ref.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::get_enclosing_block;
-use clippy_utils::source::snippet;
+use clippy_utils::source::snippet_with_context;
 use clippy_utils::ty::{implements_trait, is_copy};
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
@@ -61,12 +61,13 @@ pub(crate) fn check<'tcx>(
                         e.span,
                         "needlessly taken reference of both operands",
                         |diag| {
-                            let lsnip = snippet(cx, l.span, "...").to_string();
-                            let rsnip = snippet(cx, r.span, "...").to_string();
+                            let mut applicability = Applicability::MachineApplicable;
+                            let (lsnip, _) = snippet_with_context(cx, l.span, e.span.ctxt(), "...", &mut applicability);
+                            let (rsnip, _) = snippet_with_context(cx, r.span, e.span.ctxt(), "...", &mut applicability);
                             diag.multipart_suggestion(
                                 "use the values directly",
-                                vec![(left.span, lsnip), (right.span, rsnip)],
-                                Applicability::MachineApplicable,
+                                vec![(left.span, lsnip.to_string()), (right.span, rsnip.to_string())],
+                                applicability,
                             );
                         },
                     );
@@ -80,13 +81,9 @@ pub(crate) fn check<'tcx>(
                         e.span,
                         "needlessly taken reference of left operand",
                         |diag| {
-                            let lsnip = snippet(cx, l.span, "...").to_string();
-                            diag.span_suggestion(
-                                left.span,
-                                "use the left value directly",
-                                lsnip,
-                                Applicability::MachineApplicable,
-                            );
+                            let mut applicability = Applicability::MachineApplicable;
+                            let (lsnip, _) = snippet_with_context(cx, l.span, e.span.ctxt(), "...", &mut applicability);
+                            diag.span_suggestion(left.span, "use the left value directly", lsnip, applicability);
                         },
                     );
                 } else if !lcpy
@@ -99,7 +96,8 @@ pub(crate) fn check<'tcx>(
                         e.span,
                         "needlessly taken reference of right operand",
                         |diag| {
-                            let rsnip = snippet(cx, r.span, "...").to_string();
+                            let mut applicability = Applicability::MachineApplicable;
+                            let (rsnip, _) = snippet_with_context(cx, r.span, e.span.ctxt(), "...", &mut applicability);
                             diag.span_suggestion(
                                 right.span,
                                 "use the right value directly",
@@ -131,7 +129,8 @@ pub(crate) fn check<'tcx>(
                         e.span,
                         "needlessly taken reference of left operand",
                         |diag| {
-                            let lsnip = snippet(cx, l.span, "...").to_string();
+                            let mut applicability = Applicability::MachineApplicable;
+                            let (lsnip, _) = snippet_with_context(cx, l.span, e.span.ctxt(), "...", &mut applicability);
                             diag.span_suggestion(
                                 left.span,
                                 "use the left value directly",
@@ -158,7 +157,8 @@ pub(crate) fn check<'tcx>(
                     && implements_trait(cx, cx.typeck_results().expr_ty(left), trait_id, &[rty.into()])
                 {
                     span_lint_and_then(cx, OP_REF, e.span, "taken reference of right operand", |diag| {
-                        let rsnip = snippet(cx, r.span, "...").to_string();
+                        let mut applicability = Applicability::MachineApplicable;
+                        let (rsnip, _) = snippet_with_context(cx, r.span, e.span.ctxt(), "...", &mut applicability);
                         diag.span_suggestion(
                             right.span,
                             "use the right value directly",
diff --git a/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs b/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs
index 8eaf65e6306..301b2cd4bf2 100644
--- a/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs
+++ b/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs
@@ -43,12 +43,12 @@ impl<'tcx> LateLintPass<'tcx> for PartialEqNeImpl {
             && trait_ref.path.res.def_id() == eq_trait
         {
             for impl_item in *impl_items {
-                if impl_item.ident.name == sym::ne {
+                if cx.tcx.item_name(impl_item.owner_id) == sym::ne {
                     span_lint_hir(
                         cx,
                         PARTIALEQ_NE_IMPL,
-                        impl_item.id.hir_id(),
-                        impl_item.span,
+                        impl_item.hir_id(),
+                        cx.tcx.def_span(impl_item.owner_id),
                         "re-implementing `PartialEq::ne` is unnecessary",
                     );
                 }
diff --git a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs
index 84597269a58..1c23fe998ad 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs
@@ -5,9 +5,7 @@ use hir::Param;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_hir::intravisit::{Visitor as HirVisitor, Visitor};
-use rustc_hir::{
-    ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, ExprKind, Node, intravisit as hir_visit,
-};
+use rustc_hir::{ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, ExprKind, intravisit as hir_visit};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::hir::nested_filter;
 use rustc_middle::ty;
@@ -198,15 +196,15 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall {
                             hint = hint.asyncify();
                         }
 
-                        let is_in_fn_call_arg = if let Node::Expr(expr) = cx.tcx.parent_hir_node(expr.hir_id) {
-                            matches!(expr.kind, ExprKind::Call(_, _))
-                        } else {
-                            false
-                        };
-
-                        // avoid clippy::double_parens
-                        if !is_in_fn_call_arg {
-                            hint = hint.maybe_paren();
+                        // If the closure body is a block with a single expression, suggest just the inner expression,
+                        // not the block. Example: `(|| { Some(true) })()` should suggest
+                        // `Some(true)`
+                        if let ExprKind::Block(block, _) = body.kind
+                            && block.stmts.is_empty()
+                            && let Some(expr) = block.expr
+                        {
+                            hint = Sugg::hir_with_context(cx, expr, full_expr.span.ctxt(), "..", &mut applicability)
+                                .maybe_paren();
                         }
 
                         diag.span_suggestion(full_expr.span, "try doing something like", hint, applicability);
diff --git a/src/tools/clippy/clippy_lints/src/same_name_method.rs b/src/tools/clippy/clippy_lints/src/same_name_method.rs
index 226e8ff6adb..85fde780e68 100644
--- a/src/tools/clippy/clippy_lints/src/same_name_method.rs
+++ b/src/tools/clippy/clippy_lints/src/same_name_method.rs
@@ -3,7 +3,7 @@ use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{HirId, Impl, ItemKind, Node, Path, QPath, TraitRef, TyKind};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::AssocItem;
+use rustc_middle::ty::{AssocKind, AssocItem};
 use rustc_session::declare_lint_pass;
 use rustc_span::Span;
 use rustc_span::symbol::Symbol;
@@ -54,7 +54,6 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
             if matches!(cx.tcx.def_kind(id.owner_id), DefKind::Impl { .. })
                 && let item = cx.tcx.hir_item(id)
                 && let ItemKind::Impl(Impl {
-                    items,
                     of_trait,
                     self_ty,
                     ..
@@ -115,13 +114,11 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
                             }
                         };
 
-                        for impl_item_ref in (*items)
-                            .iter()
-                            .filter(|impl_item_ref| matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. }))
-                        {
-                            let method_name = impl_item_ref.ident.name;
-                            methods_in_trait.remove(&method_name);
-                            check_trait_method(method_name, impl_item_ref.span);
+                        for assoc_item in cx.tcx.associated_items(id.owner_id).in_definition_order() {
+                            if let AssocKind::Fn { name, .. } = assoc_item.kind {
+                                methods_in_trait.remove(&name);
+                                check_trait_method(name, cx.tcx.def_span(assoc_item.def_id));
+                            }
                         }
 
                         for method_name in methods_in_trait {
@@ -129,14 +126,11 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
                         }
                     },
                     None => {
-                        for impl_item_ref in (*items)
-                            .iter()
-                            .filter(|impl_item_ref| matches!(impl_item_ref.kind, rustc_hir::AssocItemKind::Fn { .. }))
-                        {
-                            let method_name = impl_item_ref.ident.name;
-                            let impl_span = impl_item_ref.span;
-                            let hir_id = impl_item_ref.id.hir_id();
-                            if let Some(trait_spans) = existing_name.trait_methods.get(&method_name) {
+                        for assoc_item in cx.tcx.associated_items(id.owner_id).in_definition_order() {
+                            let AssocKind::Fn { name, .. } = assoc_item.kind else { continue };
+                            let impl_span = cx.tcx.def_span(assoc_item.def_id);
+                            let hir_id = cx.tcx.local_def_id_to_hir_id(assoc_item.def_id.expect_local());
+                            if let Some(trait_spans) = existing_name.trait_methods.get(&name) {
                                 span_lint_hir_and_then(
                                     cx,
                                     SAME_NAME_METHOD,
@@ -148,12 +142,12 @@ impl<'tcx> LateLintPass<'tcx> for SameNameMethod {
                                         // iterate on trait_spans?
                                         diag.span_note(
                                             trait_spans[0],
-                                            format!("existing `{method_name}` defined here"),
+                                            format!("existing `{name}` defined here"),
                                         );
                                     },
                                 );
                             }
-                            existing_name.impl_methods.insert(method_name, (impl_span, hir_id));
+                            existing_name.impl_methods.insert(name, (impl_span, hir_id));
                         }
                     },
                 }
diff --git a/src/tools/clippy/clippy_lints/src/serde_api.rs b/src/tools/clippy/clippy_lints/src/serde_api.rs
index b36a5d6d502..2de22e4b6a3 100644
--- a/src/tools/clippy/clippy_lints/src/serde_api.rs
+++ b/src/tools/clippy/clippy_lints/src/serde_api.rs
@@ -36,9 +36,9 @@ impl<'tcx> LateLintPass<'tcx> for SerdeApi {
                 let mut seen_str = None;
                 let mut seen_string = None;
                 for item in *items {
-                    match item.ident.name {
-                        sym::visit_str => seen_str = Some(item.span),
-                        sym::visit_string => seen_string = Some(item.span),
+                    match cx.tcx.item_name(item.owner_id) {
+                        sym::visit_str => seen_str = Some(cx.tcx.def_span(item.owner_id)),
+                        sym::visit_string => seen_string = Some(cx.tcx.def_span(item.owner_id)),
                         _ => {},
                     }
                 }
diff --git a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs
index 442b3250d86..cf70e883bd0 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,10 +1,10 @@
 use clippy_config::Conf;
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
 use clippy_utils::is_from_proc_macro;
 use clippy_utils::msrvs::Msrv;
 use rustc_attr_data_structures::{StabilityLevel, StableSince};
 use rustc_errors::Applicability;
-use rustc_hir::def::Res;
+use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
 use rustc_hir::{Block, Body, HirId, Path, PathSegment};
 use rustc_lint::{LateContext, LateLintPass, Lint, LintContext};
@@ -88,49 +88,52 @@ declare_clippy_lint! {
 }
 
 pub struct StdReexports {
-    lint_point: (Span, Option<LintPoint>),
+    lint_points: Option<(Span, Vec<LintPoint>)>,
     msrv: Msrv,
 }
 
 impl StdReexports {
     pub fn new(conf: &'static Conf) -> Self {
         Self {
-            lint_point: Default::default(),
+            lint_points: Option::default(),
             msrv: conf.msrv,
         }
     }
 
-    fn lint_if_finish(&mut self, cx: &LateContext<'_>, (span, item): (Span, Option<LintPoint>)) {
-        if span.source_equal(self.lint_point.0) {
-            return;
+    fn lint_if_finish(&mut self, cx: &LateContext<'_>, krate: Span, lint_point: LintPoint) {
+        match &mut self.lint_points {
+            Some((prev_krate, prev_lints)) if prev_krate.overlaps(krate) => {
+                prev_lints.push(lint_point);
+            },
+            _ => emit_lints(cx, self.lint_points.replace((krate, vec![lint_point]))),
         }
-
-        if !self.lint_point.0.is_dummy() {
-            emit_lints(cx, &self.lint_point);
-        }
-
-        self.lint_point = (span, item);
     }
 }
 
 impl_lint_pass!(StdReexports => [STD_INSTEAD_OF_CORE, STD_INSTEAD_OF_ALLOC, ALLOC_INSTEAD_OF_CORE]);
 
-type LintPoint = (&'static Lint, &'static str, &'static str);
+#[derive(Debug)]
+enum LintPoint {
+    Available(Span, &'static Lint, &'static str, &'static str),
+    Conflict,
+}
 
 impl<'tcx> LateLintPass<'tcx> for StdReexports {
     fn check_path(&mut self, cx: &LateContext<'tcx>, path: &Path<'tcx>, _: HirId) {
-        if let Res::Def(_, def_id) = path.res
+        if let Res::Def(def_kind, def_id) = path.res
             && let Some(first_segment) = get_first_segment(path)
             && is_stable(cx, def_id, self.msrv)
             && !path.span.in_external_macro(cx.sess().source_map())
             && !is_from_proc_macro(cx, &first_segment.ident)
+            && !matches!(def_kind, DefKind::Macro(_))
+            && let Some(last_segment) = path.segments.last()
         {
             let (lint, used_mod, replace_with) = match first_segment.ident.name {
                 sym::std => match cx.tcx.crate_name(def_id.krate) {
                     sym::core => (STD_INSTEAD_OF_CORE, "std", "core"),
                     sym::alloc => (STD_INSTEAD_OF_ALLOC, "std", "alloc"),
                     _ => {
-                        self.lint_if_finish(cx, (first_segment.ident.span, None));
+                        self.lint_if_finish(cx, first_segment.ident.span, LintPoint::Conflict);
                         return;
                     },
                 },
@@ -138,44 +141,84 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports {
                     if cx.tcx.crate_name(def_id.krate) == sym::core {
                         (ALLOC_INSTEAD_OF_CORE, "alloc", "core")
                     } else {
-                        self.lint_if_finish(cx, (first_segment.ident.span, None));
+                        self.lint_if_finish(cx, first_segment.ident.span, LintPoint::Conflict);
                         return;
                     }
                 },
-                _ => return,
+                _ => {
+                    self.lint_if_finish(cx, first_segment.ident.span, LintPoint::Conflict);
+                    return;
+                },
             };
 
-            self.lint_if_finish(cx, (first_segment.ident.span, Some((lint, used_mod, replace_with))));
+            self.lint_if_finish(
+                cx,
+                first_segment.ident.span,
+                LintPoint::Available(last_segment.ident.span, lint, used_mod, replace_with),
+            );
         }
     }
 
     fn check_block_post(&mut self, cx: &LateContext<'tcx>, _: &Block<'tcx>) {
-        self.lint_if_finish(cx, Default::default());
+        emit_lints(cx, self.lint_points.take());
     }
 
     fn check_body_post(&mut self, cx: &LateContext<'tcx>, _: &Body<'tcx>) {
-        self.lint_if_finish(cx, Default::default());
+        emit_lints(cx, self.lint_points.take());
     }
 
     fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
-        self.lint_if_finish(cx, Default::default());
+        emit_lints(cx, self.lint_points.take());
     }
 }
 
-fn emit_lints(cx: &LateContext<'_>, (span, item): &(Span, Option<LintPoint>)) {
-    let Some((lint, used_mod, replace_with)) = item else {
+fn emit_lints(cx: &LateContext<'_>, lint_points: Option<(Span, Vec<LintPoint>)>) {
+    let Some((krate_span, lint_points)) = lint_points else {
         return;
     };
 
-    span_lint_and_sugg(
-        cx,
-        lint,
-        *span,
-        format!("used import from `{used_mod}` instead of `{replace_with}`"),
-        format!("consider importing the item from `{replace_with}`"),
-        (*replace_with).to_string(),
-        Applicability::MachineApplicable,
-    );
+    let mut lint: Option<(&'static Lint, &'static str, &'static str)> = None;
+    let mut has_conflict = false;
+    for lint_point in &lint_points {
+        match lint_point {
+            LintPoint::Available(_, l, used_mod, replace_with)
+                if lint.is_none_or(|(prev_l, ..)| l.name == prev_l.name) =>
+            {
+                lint = Some((l, used_mod, replace_with));
+            },
+            _ => {
+                has_conflict = true;
+                break;
+            },
+        }
+    }
+
+    if !has_conflict && let Some((lint, used_mod, replace_with)) = lint {
+        span_lint_and_sugg(
+            cx,
+            lint,
+            krate_span,
+            format!("used import from `{used_mod}` instead of `{replace_with}`"),
+            format!("consider importing the item from `{replace_with}`"),
+            (*replace_with).to_string(),
+            Applicability::MachineApplicable,
+        );
+        return;
+    }
+
+    for lint_point in lint_points {
+        let LintPoint::Available(span, lint, used_mod, replace_with) = lint_point else {
+            continue;
+        };
+        span_lint_and_help(
+            cx,
+            lint,
+            span,
+            format!("used import from `{used_mod}` instead of `{replace_with}`"),
+            None,
+            format!("consider importing the item from `{replace_with}`"),
+        );
+    }
 }
 
 /// Returns the first named segment of a [`Path`].
diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs
index 45e54302e32..9182a55081f 100644
--- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs
+++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs
@@ -112,7 +112,7 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
         // special handling for self trait bounds as these are not considered generics
         // ie. trait Foo: Display {}
         if let Item {
-            kind: ItemKind::Trait(_, _, _, _, bounds, ..),
+            kind: ItemKind::Trait(_, _, _, _, _, bounds, ..),
             ..
         } = item
         {
@@ -133,7 +133,7 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
                     ..
                 }) = segments.first()
                 && let Some(Node::Item(Item {
-                    kind: ItemKind::Trait(_, _, _, _, self_bounds, _),
+                    kind: ItemKind::Trait(_, _, _, _, _, self_bounds, _),
                     ..
                 })) = cx.tcx.hir_get_if_local(*def_id)
             {
diff --git a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
index 6cc4b589a72..cf603c6190b 100644
--- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
+++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
@@ -9,7 +9,7 @@ use clippy_utils::visitors::{Descend, for_each_expr};
 use hir::HirId;
 use rustc_hir as hir;
 use rustc_hir::{Block, BlockCheckMode, ItemKind, Node, UnsafeSource};
-use rustc_lexer::{TokenKind, tokenize};
+use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_session::impl_lint_pass;
 use rustc_span::{BytePos, Pos, RelativeBytePos, Span, SyntaxContext};
@@ -143,7 +143,8 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks {
         if let Some(tail) = block.expr
             && !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, tail.hir_id)
             && !tail.span.in_external_macro(cx.tcx.sess.source_map())
-            && let HasSafetyComment::Yes(pos) = stmt_has_safety_comment(cx, tail.span, tail.hir_id)
+            && let HasSafetyComment::Yes(pos) =
+                stmt_has_safety_comment(cx, tail.span, tail.hir_id, self.accept_comment_above_attributes)
             && let Some(help_span) = expr_has_unnecessary_safety_comment(cx, tail, pos)
         {
             span_lint_and_then(
@@ -167,7 +168,8 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks {
         };
         if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, stmt.hir_id)
             && !stmt.span.in_external_macro(cx.tcx.sess.source_map())
-            && let HasSafetyComment::Yes(pos) = stmt_has_safety_comment(cx, stmt.span, stmt.hir_id)
+            && let HasSafetyComment::Yes(pos) =
+                stmt_has_safety_comment(cx, stmt.span, stmt.hir_id, self.accept_comment_above_attributes)
             && let Some(help_span) = expr_has_unnecessary_safety_comment(cx, expr, pos)
         {
             span_lint_and_then(
@@ -540,7 +542,12 @@ fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> HasSaf
 
 /// Checks if the lines immediately preceding the item contain a safety comment.
 #[allow(clippy::collapsible_match)]
-fn stmt_has_safety_comment(cx: &LateContext<'_>, span: Span, hir_id: HirId) -> HasSafetyComment {
+fn stmt_has_safety_comment(
+    cx: &LateContext<'_>,
+    span: Span,
+    hir_id: HirId,
+    accept_comment_above_attributes: bool,
+) -> HasSafetyComment {
     match span_from_macro_expansion_has_safety_comment(cx, span) {
         HasSafetyComment::Maybe => (),
         has_safety_comment => return has_safety_comment,
@@ -555,6 +562,13 @@ fn stmt_has_safety_comment(cx: &LateContext<'_>, span: Span, hir_id: HirId) -> H
         _ => return HasSafetyComment::Maybe,
     };
 
+    // if span_with_attrs_has_safety_comment(cx, span, hir_id, accept_comment_above_attrib
+    // }
+    let mut span = span;
+    if accept_comment_above_attributes {
+        span = include_attrs_in_span(cx, hir_id, span);
+    }
+
     let source_map = cx.sess().source_map();
     if let Some(comment_start) = comment_start
         && let Ok(unsafe_line) = source_map.lookup_line(span.lo())
@@ -746,7 +760,7 @@ fn text_has_safety_comment(src: &str, line_starts: &[RelativeBytePos], start_pos
     loop {
         if line.starts_with("/*") {
             let src = &src[line_start..line_starts.last().unwrap().to_usize()];
-            let mut tokens = tokenize(src);
+            let mut tokens = tokenize(src, FrontmatterAllowed::No);
             return (src[..tokens.next().unwrap().len as usize]
                 .to_ascii_uppercase()
                 .contains("SAFETY:")
diff --git a/src/tools/clippy/clippy_lints/src/unused_async.rs b/src/tools/clippy/clippy_lints/src/unused_async.rs
index 8ceaa3dc58e..e67afc7f5a8 100644
--- a/src/tools/clippy/clippy_lints/src/unused_async.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_async.rs
@@ -169,7 +169,7 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync {
         let iter = self
             .unused_async_fns
             .iter()
-            .filter(|UnusedAsyncFn { def_id, .. }| (!self.async_fns_as_value.contains(def_id)));
+            .filter(|UnusedAsyncFn { def_id, .. }| !self.async_fns_as_value.contains(def_id));
 
         for fun in iter {
             span_lint_hir_and_then(
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 02281b9e922..944cd91a7fd 100644
--- a/src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs
+++ b/src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs
@@ -131,7 +131,7 @@ impl LateLintPass<'_> for UpperCaseAcronyms {
             return;
         }
         match it.kind {
-            ItemKind::TyAlias(ident, ..) | ItemKind::Struct(ident, ..) | ItemKind::Trait(_, _, ident, ..) => {
+            ItemKind::TyAlias(ident, ..) | ItemKind::Struct(ident, ..) | ItemKind::Trait(_, _, _, ident, ..) => {
                 check_ident(cx, &ident, it.hir_id(), self.upper_case_acronyms_aggressive);
             },
             ItemKind::Enum(ident, _, ref enumdef) => {
diff --git a/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs b/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs
index 8f314ce7a60..6629a67f78b 100644
--- a/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs
@@ -3,7 +3,7 @@ use clippy_utils::source::SpanRangeExt;
 use itertools::Itertools;
 use rustc_ast::{Crate, Expr, ExprKind, FormatArgs};
 use rustc_data_structures::fx::FxHashMap;
-use rustc_lexer::{TokenKind, tokenize};
+use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize};
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::impl_lint_pass;
 use rustc_span::{Span, hygiene};
@@ -82,7 +82,7 @@ fn has_span_from_proc_macro(cx: &EarlyContext<'_>, args: &FormatArgs) -> bool {
         .all(|sp| {
             sp.check_source_text(cx, |src| {
                 // text should be either `, name` or `, name =`
-                let mut iter = tokenize(src).filter(|t| {
+                let mut iter = tokenize(src, FrontmatterAllowed::No).filter(|t| {
                     !matches!(
                         t.kind,
                         TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace
diff --git a/src/tools/clippy/clippy_lints_internal/src/derive_deserialize_allowing_unknown.rs b/src/tools/clippy/clippy_lints_internal/src/derive_deserialize_allowing_unknown.rs
index a1dacd359a0..88b099c477f 100644
--- a/src/tools/clippy/clippy_lints_internal/src/derive_deserialize_allowing_unknown.rs
+++ b/src/tools/clippy/clippy_lints_internal/src/derive_deserialize_allowing_unknown.rs
@@ -88,7 +88,7 @@ impl<'tcx> LateLintPass<'tcx> for DeriveDeserializeAllowingUnknown {
         }
 
         // Is it derived?
-        if !cx.tcx.has_attr(item.owner_id, sym::automatically_derived) {
+        if !find_attr!(cx.tcx.get_all_attrs(item.owner_id), AttributeKind::AutomaticallyDerived(..)) {
             return;
         }
 
diff --git a/src/tools/clippy/clippy_lints_internal/src/symbols.rs b/src/tools/clippy/clippy_lints_internal/src/symbols.rs
index 7b5d58824c3..74712097e71 100644
--- a/src/tools/clippy/clippy_lints_internal/src/symbols.rs
+++ b/src/tools/clippy/clippy_lints_internal/src/symbols.rs
@@ -65,7 +65,7 @@ pub struct Symbols {
 impl_lint_pass!(Symbols => [INTERNING_LITERALS, SYMBOL_AS_STR]);
 
 impl Symbols {
-    fn lit_suggestion(&self, lit: &Lit) -> Option<(Span, String)> {
+    fn lit_suggestion(&self, lit: Lit) -> Option<(Span, String)> {
         if let LitKind::Str(name, _) = lit.node {
             let sugg = if let Some((prefix, name)) = self.symbol_map.get(&name.as_u32()) {
                 format!("{prefix}::{name}")
diff --git a/src/tools/clippy/clippy_test_deps/Cargo.lock b/src/tools/clippy/clippy_test_deps/Cargo.lock
new file mode 100644
index 00000000000..a591dae3a1a
--- /dev/null
+++ b/src/tools/clippy/clippy_test_deps/Cargo.lock
@@ -0,0 +1,505 @@
+# This file is automatically @generated by Cargo.
+# It is not intended for manual editing.
+version = 4
+
+[[package]]
+name = "addr2line"
+version = "0.24.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1"
+dependencies = [
+ "gimli",
+]
+
+[[package]]
+name = "adler2"
+version = "2.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
+
+[[package]]
+name = "aho-corasick"
+version = "1.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "autocfg"
+version = "1.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
+
+[[package]]
+name = "backtrace"
+version = "0.3.75"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002"
+dependencies = [
+ "addr2line",
+ "cfg-if",
+ "libc",
+ "miniz_oxide",
+ "object",
+ "rustc-demangle",
+ "windows-targets",
+]
+
+[[package]]
+name = "bitflags"
+version = "2.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
+
+[[package]]
+name = "bytes"
+version = "1.10.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a"
+
+[[package]]
+name = "cfg-if"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9555578bc9e57714c812a1f84e4fc5b4d21fcb063490c624de019f7464c91268"
+
+[[package]]
+name = "clippy_test_deps"
+version = "0.1.0"
+dependencies = [
+ "futures",
+ "if_chain",
+ "itertools",
+ "parking_lot",
+ "quote",
+ "regex",
+ "serde",
+ "syn",
+ "tokio",
+]
+
+[[package]]
+name = "either"
+version = "1.15.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
+
+[[package]]
+name = "futures"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-executor",
+ "futures-io",
+ "futures-sink",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-channel"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10"
+dependencies = [
+ "futures-core",
+ "futures-sink",
+]
+
+[[package]]
+name = "futures-core"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e"
+
+[[package]]
+name = "futures-executor"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "futures-util",
+]
+
+[[package]]
+name = "futures-io"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6"
+
+[[package]]
+name = "futures-macro"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "futures-sink"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7"
+
+[[package]]
+name = "futures-task"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
+
+[[package]]
+name = "futures-util"
+version = "0.3.31"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81"
+dependencies = [
+ "futures-channel",
+ "futures-core",
+ "futures-io",
+ "futures-macro",
+ "futures-sink",
+ "futures-task",
+ "memchr",
+ "pin-project-lite",
+ "pin-utils",
+ "slab",
+]
+
+[[package]]
+name = "gimli"
+version = "0.31.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f"
+
+[[package]]
+name = "if_chain"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed"
+
+[[package]]
+name = "io-uring"
+version = "0.7.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b86e202f00093dcba4275d4636b93ef9dd75d025ae560d2521b45ea28ab49013"
+dependencies = [
+ "bitflags",
+ "cfg-if",
+ "libc",
+]
+
+[[package]]
+name = "itertools"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569"
+dependencies = [
+ "either",
+]
+
+[[package]]
+name = "libc"
+version = "0.2.174"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1171693293099992e19cddea4e8b849964e9846f4acee11b3948bcc337be8776"
+
+[[package]]
+name = "lock_api"
+version = "0.4.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765"
+dependencies = [
+ "autocfg",
+ "scopeguard",
+]
+
+[[package]]
+name = "memchr"
+version = "2.7.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0"
+
+[[package]]
+name = "miniz_oxide"
+version = "0.8.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
+dependencies = [
+ "adler2",
+]
+
+[[package]]
+name = "mio"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c"
+dependencies = [
+ "libc",
+ "wasi",
+ "windows-sys",
+]
+
+[[package]]
+name = "object"
+version = "0.36.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87"
+dependencies = [
+ "memchr",
+]
+
+[[package]]
+name = "parking_lot"
+version = "0.12.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall",
+ "smallvec",
+ "windows-targets",
+]
+
+[[package]]
+name = "pin-project-lite"
+version = "0.2.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b"
+
+[[package]]
+name = "pin-utils"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
+
+[[package]]
+name = "proc-macro2"
+version = "1.0.95"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "quote"
+version = "1.0.40"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d"
+dependencies = [
+ "proc-macro2",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.5.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d04b7d0ee6b4a0207a0a7adb104d23ecb0b47d6beae7152d0fa34b692b29fd6"
+dependencies = [
+ "bitflags",
+]
+
+[[package]]
+name = "regex"
+version = "1.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-automata",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-automata"
+version = "0.4.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908"
+dependencies = [
+ "aho-corasick",
+ "memchr",
+ "regex-syntax",
+]
+
+[[package]]
+name = "regex-syntax"
+version = "0.8.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
+
+[[package]]
+name = "rustc-demangle"
+version = "0.1.25"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "989e6739f80c4ad5b13e0fd7fe89531180375b18520cc8c82080e4dc4035b84f"
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "serde"
+version = "1.0.219"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6"
+dependencies = [
+ "serde_derive",
+]
+
+[[package]]
+name = "serde_derive"
+version = "1.0.219"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "slab"
+version = "0.4.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04dc19736151f35336d325007ac991178d504a119863a2fcb3758cdb5e52c50d"
+
+[[package]]
+name = "smallvec"
+version = "1.15.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
+
+[[package]]
+name = "syn"
+version = "2.0.104"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17b6f705963418cdb9927482fa304bc562ece2fdd4f616084c50b7023b435a40"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "unicode-ident",
+]
+
+[[package]]
+name = "tokio"
+version = "1.46.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17"
+dependencies = [
+ "backtrace",
+ "bytes",
+ "io-uring",
+ "libc",
+ "mio",
+ "pin-project-lite",
+ "slab",
+]
+
+[[package]]
+name = "unicode-ident"
+version = "1.0.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512"
+
+[[package]]
+name = "wasi"
+version = "0.11.1+wasi-snapshot-preview1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
+
+[[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets",
+]
+
+[[package]]
+name = "windows-targets"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
+dependencies = [
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
+ "windows_i686_gnullvm",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
+]
+
+[[package]]
+name = "windows_aarch64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+
+[[package]]
+name = "windows_aarch64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+
+[[package]]
+name = "windows_i686_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
+
+[[package]]
+name = "windows_i686_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+
+[[package]]
+name = "windows_i686_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+
+[[package]]
+name = "windows_x86_64_gnu"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+
+[[package]]
+name = "windows_x86_64_gnullvm"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+
+[[package]]
+name = "windows_x86_64_msvc"
+version = "0.52.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
diff --git a/src/tools/clippy/clippy_test_deps/Cargo.toml b/src/tools/clippy/clippy_test_deps/Cargo.toml
new file mode 100644
index 00000000000..a23ffcaf2f9
--- /dev/null
+++ b/src/tools/clippy/clippy_test_deps/Cargo.toml
@@ -0,0 +1,20 @@
+[package]
+name = "clippy_test_deps"
+version = "0.1.0"
+edition = "2021"
+
+# Add dependencies here to make them available in ui tests.
+
+[dependencies]
+regex = "1.5.5"
+serde = { version = "1.0.145", features = ["derive"] }
+if_chain = "1.0"
+quote = "1.0.25"
+syn = { version = "2.0", features = ["full"] }
+futures = "0.3"
+parking_lot = "0.12"
+tokio = { version = "1", features = ["io-util"] }
+itertools = "0.12"
+
+# Make sure we are not part of the rustc workspace.
+[workspace]
diff --git a/src/tools/clippy/clippy_test_deps/src/main.rs b/src/tools/clippy/clippy_test_deps/src/main.rs
new file mode 100644
index 00000000000..f328e4d9d04
--- /dev/null
+++ b/src/tools/clippy/clippy_test_deps/src/main.rs
@@ -0,0 +1 @@
+fn main() {}
diff --git a/src/tools/clippy/clippy_utils/README.md b/src/tools/clippy/clippy_utils/README.md
index 649748d1534..645b644d9f4 100644
--- a/src/tools/clippy/clippy_utils/README.md
+++ b/src/tools/clippy/clippy_utils/README.md
@@ -8,7 +8,7 @@ This crate is only guaranteed to build with this `nightly` toolchain:
 
 <!-- begin autogenerated nightly -->
 ```
-nightly-2025-06-26
+nightly-2025-07-10
 ```
 <!-- end autogenerated nightly -->
 
diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs
index 42254ec8e92..96f0273c439 100644
--- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs
+++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs
@@ -444,6 +444,7 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
         },
         (
             Trait(box ast::Trait {
+                constness: lc,
                 is_auto: la,
                 safety: lu,
                 ident: li,
@@ -452,6 +453,7 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
                 items: lis,
             }),
             Trait(box ast::Trait {
+                constness: rc,
                 is_auto: ra,
                 safety: ru,
                 ident: ri,
@@ -460,7 +462,8 @@ pub fn eq_item_kind(l: &ItemKind, r: &ItemKind) -> bool {
                 items: ris,
             }),
         ) => {
-            la == ra
+            matches!(lc, ast::Const::No) == matches!(rc, ast::Const::No)
+                && la == ra
                 && matches!(lu, Safety::Default) == matches!(ru, Safety::Default)
                 && eq_id(*li, *ri)
                 && eq_generics(lg, rg)
diff --git a/src/tools/clippy/clippy_utils/src/attrs.rs b/src/tools/clippy/clippy_utils/src/attrs.rs
index 4c7a589e185..34472eaab93 100644
--- a/src/tools/clippy/clippy_utils/src/attrs.rs
+++ b/src/tools/clippy/clippy_utils/src/attrs.rs
@@ -1,5 +1,8 @@
+use crate::source::SpanRangeExt;
+use crate::{sym, tokenize_with_text};
 use rustc_ast::attr;
 use rustc_ast::attr::AttributeExt;
+use rustc_attr_data_structures::{AttributeKind, find_attr};
 use rustc_errors::Applicability;
 use rustc_lexer::TokenKind;
 use rustc_lint::LateContext;
@@ -7,10 +10,6 @@ use rustc_middle::ty::{AdtDef, TyCtxt};
 use rustc_session::Session;
 use rustc_span::{Span, Symbol};
 use std::str::FromStr;
-use rustc_attr_data_structures::find_attr;
-use crate::source::SpanRangeExt;
-use crate::{sym, tokenize_with_text};
-use rustc_attr_data_structures::AttributeKind;
 
 /// Deprecation status of attributes known by Clippy.
 pub enum DeprecationStatus {
@@ -168,7 +167,8 @@ pub fn has_non_exhaustive_attr(tcx: TyCtxt<'_>, adt: AdtDef<'_>) -> bool {
     adt.is_variant_list_non_exhaustive()
         || find_attr!(tcx.get_all_attrs(adt.did()), AttributeKind::NonExhaustive(..))
         || adt.variants().iter().any(|variant_def| {
-            variant_def.is_field_list_non_exhaustive() || find_attr!(tcx.get_all_attrs(variant_def.def_id), AttributeKind::NonExhaustive(..))
+            variant_def.is_field_list_non_exhaustive()
+                || find_attr!(tcx.get_all_attrs(variant_def.def_id), AttributeKind::NonExhaustive(..))
         })
         || adt
             .all_fields()
diff --git a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs
index ce61fffe0de..dc31ed08fb7 100644
--- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs
+++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs
@@ -252,11 +252,11 @@ fn item_search_pat(item: &Item<'_>) -> (Pat, Pat) {
         ItemKind::Struct(_, _, VariantData::Struct { .. }) => (Pat::Str("struct"), Pat::Str("}")),
         ItemKind::Struct(..) => (Pat::Str("struct"), Pat::Str(";")),
         ItemKind::Union(..) => (Pat::Str("union"), Pat::Str("}")),
-        ItemKind::Trait(_, Safety::Unsafe, ..)
+        ItemKind::Trait(_, _, Safety::Unsafe, ..)
         | ItemKind::Impl(Impl {
             safety: Safety::Unsafe, ..
         }) => (Pat::Str("unsafe"), Pat::Str("}")),
-        ItemKind::Trait(IsAuto::Yes, ..) => (Pat::Str("auto"), Pat::Str("}")),
+        ItemKind::Trait(_, IsAuto::Yes, ..) => (Pat::Str("auto"), Pat::Str("}")),
         ItemKind::Trait(..) => (Pat::Str("trait"), Pat::Str("}")),
         ItemKind::Impl(_) => (Pat::Str("impl"), Pat::Str("}")),
         _ => return (Pat::Str(""), Pat::Str("")),
diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs
index 09299c869dc..25afa12e95d 100644
--- a/src/tools/clippy/clippy_utils/src/consts.rs
+++ b/src/tools/clippy/clippy_utils/src/consts.rs
@@ -15,7 +15,7 @@ use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{
     BinOpKind, Block, ConstBlock, Expr, ExprKind, HirId, Item, ItemKind, Node, PatExpr, PatExprKind, QPath, UnOp,
 };
-use rustc_lexer::tokenize;
+use rustc_lexer::{FrontmatterAllowed, tokenize};
 use rustc_lint::LateContext;
 use rustc_middle::mir::ConstValue;
 use rustc_middle::mir::interpret::{Scalar, alloc_range};
@@ -304,9 +304,7 @@ pub fn lit_to_mir_constant<'tcx>(lit: &LitKind, ty: Option<Ty<'tcx>>) -> Constan
     match *lit {
         LitKind::Str(ref is, _) => Constant::Str(is.to_string()),
         LitKind::Byte(b) => Constant::Int(u128::from(b)),
-        LitKind::ByteStr(ref s, _) | LitKind::CStr(ref s, _) => {
-            Constant::Binary(s.as_byte_str().to_vec())
-        }
+        LitKind::ByteStr(ref s, _) | LitKind::CStr(ref s, _) => Constant::Binary(s.as_byte_str().to_vec()),
         LitKind::Char(c) => Constant::Char(c),
         LitKind::Int(n, _) => Constant::Int(n.get()),
         LitKind::Float(ref is, LitFloatType::Suffixed(fty)) => match fty {
@@ -568,9 +566,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
                 } else {
                     match &lit.node {
                         LitKind::Str(is, _) => Some(is.is_empty()),
-                        LitKind::ByteStr(s, _) | LitKind::CStr(s, _) => {
-                            Some(s.as_byte_str().is_empty())
-                        }
+                        LitKind::ByteStr(s, _) | LitKind::CStr(s, _) => Some(s.as_byte_str().is_empty()),
                         _ => None,
                     }
                 }
@@ -715,7 +711,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
                     && let Some(src) = src.as_str()
                 {
                     use rustc_lexer::TokenKind::{BlockComment, LineComment, OpenBrace, Semi, Whitespace};
-                    if !tokenize(src)
+                    if !tokenize(src, FrontmatterAllowed::No)
                         .map(|t| t.kind)
                         .filter(|t| !matches!(t, Whitespace | LineComment { .. } | BlockComment { .. } | Semi))
                         .eq([OpenBrace])
@@ -918,7 +914,7 @@ fn mir_is_empty<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option<boo
                     // Get the length from the slice, using the same formula as
                     // [`ConstValue::try_get_slice_bytes_for_diagnostics`].
                     let a = tcx.global_alloc(alloc_id).unwrap_memory().inner();
-                    let ptr_size = tcx.data_layout.pointer_size;
+                    let ptr_size = tcx.data_layout.pointer_size();
                     if a.size() < offset + 2 * ptr_size {
                         // (partially) dangling reference
                         return None;
diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs
index 6971b488013..4e0b00df950 100644
--- a/src/tools/clippy/clippy_utils/src/higher.rs
+++ b/src/tools/clippy/clippy_utils/src/higher.rs
@@ -54,7 +54,7 @@ impl<'tcx> ForLoop<'tcx> {
     }
 }
 
-/// An `if` expression without `DropTemps`
+/// An `if` expression without `let`
 pub struct If<'hir> {
     /// `if` condition
     pub cond: &'hir Expr<'hir>,
@@ -66,16 +66,10 @@ pub struct If<'hir> {
 
 impl<'hir> If<'hir> {
     #[inline]
-    /// Parses an `if` expression
+    /// Parses an `if` expression without `let`
     pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
-        if let ExprKind::If(
-            Expr {
-                kind: ExprKind::DropTemps(cond),
-                ..
-            },
-            then,
-            r#else,
-        ) = expr.kind
+        if let ExprKind::If(cond, then, r#else) = expr.kind
+            && !has_let_expr(cond)
         {
             Some(Self { cond, then, r#else })
         } else {
@@ -198,18 +192,10 @@ impl<'hir> IfOrIfLet<'hir> {
     /// Parses an `if` or `if let` expression
     pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
         if let ExprKind::If(cond, then, r#else) = expr.kind {
-            if let ExprKind::DropTemps(new_cond) = cond.kind {
-                return Some(Self {
-                    cond: new_cond,
-                    then,
-                    r#else,
-                });
-            }
-            if let ExprKind::Let(..) = cond.kind {
-                return Some(Self { cond, then, r#else });
-            }
+            Some(Self { cond, then, r#else })
+        } else {
+            None
         }
-        None
     }
 }
 
@@ -343,15 +329,7 @@ impl<'hir> While<'hir> {
             Block {
                 expr:
                     Some(Expr {
-                        kind:
-                            ExprKind::If(
-                                Expr {
-                                    kind: ExprKind::DropTemps(condition),
-                                    ..
-                                },
-                                body,
-                                _,
-                            ),
+                        kind: ExprKind::If(condition, body, _),
                         ..
                     }),
                 ..
@@ -360,6 +338,7 @@ impl<'hir> While<'hir> {
             LoopSource::While,
             span,
         ) = expr.kind
+            && !has_let_expr(condition)
         {
             return Some(Self { condition, body, span });
         }
@@ -493,3 +472,13 @@ pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -
     }
     None
 }
+
+/// Checks that a condition doesn't have a `let` expression, to keep `If` and `While` from accepting
+/// `if let` and `while let`.
+pub const fn has_let_expr<'tcx>(cond: &'tcx Expr<'tcx>) -> bool {
+    match &cond.kind {
+        ExprKind::Let(_) => true,
+        ExprKind::Binary(_, lhs, rhs) => has_let_expr(lhs) || has_let_expr(rhs),
+        _ => false,
+    }
+}
diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs
index 0ca494f16e3..f0d7fb89c44 100644
--- a/src/tools/clippy/clippy_utils/src/hir_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs
@@ -2,6 +2,7 @@ use crate::consts::ConstEvalCtxt;
 use crate::macros::macro_backtrace;
 use crate::source::{SpanRange, SpanRangeExt, walk_span_to_context};
 use crate::tokenize_with_text;
+use rustc_ast::ast;
 use rustc_ast::ast::InlineAsmTemplatePiece;
 use rustc_data_structures::fx::FxHasher;
 use rustc_hir::MatchSource::TryDesugar;
@@ -9,10 +10,10 @@ use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{
     AssocItemConstraint, BinOpKind, BindingMode, Block, BodyId, Closure, ConstArg, ConstArgKind, Expr, ExprField,
     ExprKind, FnRetTy, GenericArg, GenericArgs, HirId, HirIdMap, InlineAsmOperand, LetExpr, Lifetime, LifetimeKind,
-    Pat, PatExpr, PatExprKind, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, StructTailExpr,
-    TraitBoundModifiers, Ty, TyKind, TyPat, TyPatKind,
+    Node, Pat, PatExpr, PatExprKind, PatField, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind,
+    StructTailExpr, TraitBoundModifiers, Ty, TyKind, TyPat, TyPatKind,
 };
-use rustc_lexer::{TokenKind, tokenize};
+use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize};
 use rustc_lint::LateContext;
 use rustc_middle::ty::TypeckResults;
 use rustc_span::{BytePos, ExpnKind, MacroKind, Symbol, SyntaxContext, sym};
@@ -686,7 +687,7 @@ fn reduce_exprkind<'hir>(cx: &LateContext<'_>, kind: &'hir ExprKind<'hir>) -> &'
             // `{}` => `()`
             ([], None)
                 if block.span.check_source_text(cx, |src| {
-                    tokenize(src)
+                    tokenize(src, FrontmatterAllowed::No)
                         .map(|t| t.kind)
                         .filter(|t| {
                             !matches!(
@@ -1004,8 +1005,8 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
                     self.hash_expr(e);
                 }
             },
-            ExprKind::Match(e, arms, s) => {
-                self.hash_expr(e);
+            ExprKind::Match(scrutinee, arms, _) => {
+                self.hash_expr(scrutinee);
 
                 for arm in *arms {
                     self.hash_pat(arm.pat);
@@ -1014,8 +1015,6 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
                     }
                     self.hash_expr(arm.body);
                 }
-
-                s.hash(&mut self.s);
             },
             ExprKind::MethodCall(path, receiver, args, _fn_span) => {
                 self.hash_name(path.ident.name);
@@ -1058,8 +1057,8 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
             ExprKind::Use(expr, _) => {
                 self.hash_expr(expr);
             },
-            ExprKind::Unary(lop, le) => {
-                std::mem::discriminant(lop).hash(&mut self.s);
+            ExprKind::Unary(l_op, le) => {
+                std::mem::discriminant(l_op).hash(&mut self.s);
                 self.hash_expr(le);
             },
             ExprKind::UnsafeBinderCast(kind, expr, ty) => {
@@ -1394,3 +1393,70 @@ fn eq_span_tokens(
     }
     f(cx, left.into_range(), right.into_range(), pred)
 }
+
+/// Returns true if the expression contains ambiguous literals (unsuffixed float or int literals)
+/// that could be interpreted as either f32/f64 or i32/i64 depending on context.
+pub fn has_ambiguous_literal_in_expr(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    match expr.kind {
+        ExprKind::Path(ref qpath) => {
+            if let Res::Local(hir_id) = cx.qpath_res(qpath, expr.hir_id)
+                && let Node::LetStmt(local) = cx.tcx.parent_hir_node(hir_id)
+                && local.ty.is_none()
+                && let Some(init) = local.init
+            {
+                return has_ambiguous_literal_in_expr(cx, init);
+            }
+            false
+        },
+        ExprKind::Lit(lit) => matches!(
+            lit.node,
+            ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) | ast::LitKind::Int(_, ast::LitIntType::Unsuffixed)
+        ),
+
+        ExprKind::Array(exprs) | ExprKind::Tup(exprs) => exprs.iter().any(|e| has_ambiguous_literal_in_expr(cx, e)),
+
+        ExprKind::Assign(lhs, rhs, _) | ExprKind::AssignOp(_, lhs, rhs) | ExprKind::Binary(_, lhs, rhs) => {
+            has_ambiguous_literal_in_expr(cx, lhs) || has_ambiguous_literal_in_expr(cx, rhs)
+        },
+
+        ExprKind::Unary(_, e)
+        | ExprKind::Cast(e, _)
+        | ExprKind::Type(e, _)
+        | ExprKind::DropTemps(e)
+        | ExprKind::AddrOf(_, _, e)
+        | ExprKind::Field(e, _)
+        | ExprKind::Index(e, _, _)
+        | ExprKind::Yield(e, _) => has_ambiguous_literal_in_expr(cx, e),
+
+        ExprKind::MethodCall(_, receiver, args, _) | ExprKind::Call(receiver, args) => {
+            has_ambiguous_literal_in_expr(cx, receiver) || args.iter().any(|e| has_ambiguous_literal_in_expr(cx, e))
+        },
+
+        ExprKind::Closure(Closure { body, .. }) => {
+            let body = cx.tcx.hir_body(*body);
+            let closure_expr = crate::peel_blocks(body.value);
+            has_ambiguous_literal_in_expr(cx, closure_expr)
+        },
+
+        ExprKind::Block(blk, _) => blk.expr.as_ref().is_some_and(|e| has_ambiguous_literal_in_expr(cx, e)),
+
+        ExprKind::If(cond, then_expr, else_expr) => {
+            has_ambiguous_literal_in_expr(cx, cond)
+                || has_ambiguous_literal_in_expr(cx, then_expr)
+                || else_expr.as_ref().is_some_and(|e| has_ambiguous_literal_in_expr(cx, e))
+        },
+
+        ExprKind::Match(scrutinee, arms, _) => {
+            has_ambiguous_literal_in_expr(cx, scrutinee)
+                || arms.iter().any(|arm| has_ambiguous_literal_in_expr(cx, arm.body))
+        },
+
+        ExprKind::Loop(body, ..) => body.expr.is_some_and(|e| has_ambiguous_literal_in_expr(cx, e)),
+
+        ExprKind::Ret(opt_expr) | ExprKind::Break(_, opt_expr) => {
+            opt_expr.as_ref().is_some_and(|e| has_ambiguous_literal_in_expr(cx, e))
+        },
+
+        _ => false,
+    }
+}
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index c01f0ffaac9..ff1ee663f9b 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -77,7 +77,8 @@ pub mod visitors;
 pub use self::attrs::*;
 pub use self::check_proc_macro::{is_from_proc_macro, is_span_if, is_span_match};
 pub use self::hir_utils::{
-    HirEqInterExpr, SpanlessEq, SpanlessHash, both, count_eq, eq_expr_value, hash_expr, hash_stmt, is_bool, over,
+    HirEqInterExpr, SpanlessEq, SpanlessHash, both, count_eq, eq_expr_value, has_ambiguous_literal_in_expr, hash_expr,
+    hash_stmt, is_bool, over,
 };
 
 use core::mem;
@@ -88,6 +89,7 @@ use std::sync::{Mutex, MutexGuard, OnceLock};
 
 use itertools::Itertools;
 use rustc_abi::Integer;
+use rustc_ast::join_path_syms;
 use rustc_ast::ast::{self, LitKind, RangeLimits};
 use rustc_attr_data_structures::{AttributeKind, find_attr};
 use rustc_data_structures::fx::FxHashMap;
@@ -106,7 +108,7 @@ use rustc_hir::{
     Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem,
     TraitItemKind, TraitRef, TyKind, UnOp, def,
 };
-use rustc_lexer::{TokenKind, tokenize};
+use rustc_lexer::{FrontmatterAllowed, TokenKind, tokenize};
 use rustc_lint::{LateContext, Level, Lint, LintContext};
 use rustc_middle::hir::nested_filter;
 use rustc_middle::hir::place::PlaceBase;
@@ -1784,9 +1786,9 @@ pub fn in_automatically_derived(tcx: TyCtxt<'_>, id: HirId) -> bool {
     tcx.hir_parent_owner_iter(id)
         .filter(|(_, node)| matches!(node, OwnerNode::Item(item) if matches!(item.kind, ItemKind::Impl(_))))
         .any(|(id, _)| {
-            has_attr(
+            find_attr!(
                 tcx.hir_attrs(tcx.local_def_id_to_hir_id(id.def_id)),
-                sym::automatically_derived,
+                AttributeKind::AutomaticallyDerived(..)
             )
         })
 }
@@ -2764,7 +2766,7 @@ pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &Expr<'tcx>) -> ExprUseCtx
 /// Tokenizes the input while keeping the text associated with each token.
 pub fn tokenize_with_text(s: &str) -> impl Iterator<Item = (TokenKind, &str, InnerSpan)> {
     let mut pos = 0;
-    tokenize(s).map(move |t| {
+    tokenize(s, FrontmatterAllowed::No).map(move |t| {
         let end = pos + t.len;
         let range = pos as usize..end as usize;
         let inner = InnerSpan::new(range.start, range.end);
@@ -2779,7 +2781,7 @@ pub fn span_contains_comment(sm: &SourceMap, span: Span) -> bool {
     let Ok(snippet) = sm.span_to_snippet(span) else {
         return false;
     };
-    return tokenize(&snippet).any(|token| {
+    return tokenize(&snippet, FrontmatterAllowed::No).any(|token| {
         matches!(
             token.kind,
             TokenKind::BlockComment { .. } | TokenKind::LineComment { .. }
@@ -3244,8 +3246,8 @@ fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> St
                 // a::b::c  ::d::sym refers to
                 // e::f::sym:: ::
                 // result should be super::super::super::super::e::f
-                if let DefPathData::TypeNs(s) = l {
-                    path.push(s.to_string());
+                if let DefPathData::TypeNs(sym) = l {
+                    path.push(sym);
                 }
                 if let DefPathData::TypeNs(_) = r {
                     go_up_by += 1;
@@ -3255,7 +3257,7 @@ fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> St
             // a::b::sym:: ::    refers to
             // c::d::e  ::f::sym
             // when looking at `f`
-            Left(DefPathData::TypeNs(sym)) => path.push(sym.to_string()),
+            Left(DefPathData::TypeNs(sym)) => path.push(sym),
             // consider:
             // a::b::c  ::d::sym refers to
             // e::f::sym:: ::
@@ -3267,17 +3269,17 @@ fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> St
 
     if go_up_by > max_super {
         // `super` chain would be too long, just use the absolute path instead
-        once(String::from("crate"))
-            .chain(to.data.iter().filter_map(|el| {
+        join_path_syms(
+            once(kw::Crate).chain(to.data.iter().filter_map(|el| {
                 if let DefPathData::TypeNs(sym) = el.data {
-                    Some(sym.to_string())
+                    Some(sym)
                 } else {
                     None
                 }
             }))
-            .join("::")
+        )
     } else {
-        repeat_n(String::from("super"), go_up_by).chain(path).join("::")
+        join_path_syms(repeat_n(kw::Super, go_up_by).chain(path))
     }
 }
 
@@ -3497,3 +3499,64 @@ pub fn is_expr_default<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) ->
         false
     }
 }
+
+/// Checks if `expr` may be directly used as the return value of its enclosing body.
+/// The following cases are covered:
+/// - `expr` as the last expression of the body, or of a block that can be used as the return value
+/// - `return expr`
+/// - then or else part of a `if` in return position
+/// - arm body of a `match` in a return position
+/// - `break expr` or `break 'label expr` if the loop or block being exited is used as a return
+///   value
+///
+/// Contrary to [`TyCtxt::hir_get_fn_id_for_return_block()`], if `expr` is part of a
+/// larger expression, for example a field expression of a `struct`, it will not be
+/// considered as matching the condition and will return `false`.
+///
+/// Also, even if `expr` is assigned to a variable which is later returned, this function
+/// will still return `false` because `expr` is not used *directly* as the return value
+/// as it goes through the intermediate variable.
+pub fn potential_return_of_enclosing_body(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    let enclosing_body_owner = cx
+        .tcx
+        .local_def_id_to_hir_id(cx.tcx.hir_enclosing_body_owner(expr.hir_id));
+    let mut prev_id = expr.hir_id;
+    let mut skip_until_id = None;
+    for (hir_id, node) in cx.tcx.hir_parent_iter(expr.hir_id) {
+        if hir_id == enclosing_body_owner {
+            return true;
+        }
+        if let Some(id) = skip_until_id {
+            prev_id = hir_id;
+            if id == hir_id {
+                skip_until_id = None;
+            }
+            continue;
+        }
+        match node {
+            Node::Block(Block { expr, .. }) if expr.is_some_and(|expr| expr.hir_id == prev_id) => {},
+            Node::Arm(arm) if arm.body.hir_id == prev_id => {},
+            Node::Expr(expr) => match expr.kind {
+                ExprKind::Ret(_) => return true,
+                ExprKind::If(_, then, opt_else)
+                    if then.hir_id == prev_id || opt_else.is_some_and(|els| els.hir_id == prev_id) => {},
+                ExprKind::Match(_, arms, _) if arms.iter().any(|arm| arm.hir_id == prev_id) => {},
+                ExprKind::Block(block, _) if block.hir_id == prev_id => {},
+                ExprKind::Break(
+                    Destination {
+                        target_id: Ok(target_id),
+                        ..
+                    },
+                    _,
+                ) => skip_until_id = Some(target_id),
+                _ => break,
+            },
+            _ => break,
+        }
+        prev_id = hir_id;
+    }
+
+    // `expr` is used as part of "something" and is not returned directly from its
+    // enclosing body.
+    false
+}
diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs
index 7a0bef1a9bb..24ed4c3a8be 100644
--- a/src/tools/clippy/clippy_utils/src/msrvs.rs
+++ b/src/tools/clippy/clippy_utils/src/msrvs.rs
@@ -74,7 +74,7 @@ msrv_aliases! {
     1,28,0 { FROM_BOOL, REPEAT_WITH, SLICE_FROM_REF }
     1,27,0 { ITERATOR_TRY_FOLD }
     1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN }
-    1,24,0 { IS_ASCII_DIGIT }
+    1,24,0 { IS_ASCII_DIGIT, PTR_NULL }
     1,18,0 { HASH_MAP_RETAIN, HASH_SET_RETAIN }
     1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR }
     1,16,0 { STR_REPEAT }
diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs
index 8bbcb220210..c681806517a 100644
--- a/src/tools/clippy/clippy_utils/src/paths.rs
+++ b/src/tools/clippy/clippy_utils/src/paths.rs
@@ -10,7 +10,7 @@ use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::def::Namespace::{MacroNS, TypeNS, ValueNS};
 use rustc_hir::def::{DefKind, Namespace, Res};
 use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
-use rustc_hir::{ImplItemRef, ItemKind, Node, OwnerId, TraitItemRef, UseKind};
+use rustc_hir::{ItemKind, Node, UseKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty::fast_reject::SimplifiedType;
 use rustc_middle::ty::{FloatTy, IntTy, Ty, TyCtxt, UintTy};
@@ -284,14 +284,6 @@ fn local_item_child_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, ns: PathNS, n
         _ => return None,
     };
 
-    let res = |ident: Ident, owner_id: OwnerId| {
-        if ident.name == name && ns.matches(tcx.def_kind(owner_id).ns()) {
-            Some(owner_id.to_def_id())
-        } else {
-            None
-        }
-    };
-
     match item_kind {
         ItemKind::Mod(_, r#mod) => r#mod.item_ids.iter().find_map(|&item_id| {
             let item = tcx.hir_item(item_id);
@@ -307,17 +299,19 @@ fn local_item_child_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, ns: PathNS, n
                 } else {
                     None
                 }
+            } else if let Some(ident) = item.kind.ident()
+                && ident.name == name
+                && ns.matches(tcx.def_kind(item.owner_id).ns())
+            {
+                Some(item.owner_id.to_def_id())
             } else {
-                res(item.kind.ident()?, item_id.owner_id)
+                None
             }
         }),
-        ItemKind::Impl(r#impl) => r#impl
-            .items
-            .iter()
-            .find_map(|&ImplItemRef { ident, id, .. }| res(ident, id.owner_id)),
-        ItemKind::Trait(.., trait_item_refs) => trait_item_refs
-            .iter()
-            .find_map(|&TraitItemRef { ident, id, .. }| res(ident, id.owner_id)),
+        ItemKind::Impl(..) | ItemKind::Trait(..)
+            => tcx.associated_items(local_id).filter_by_name_unhygienic(name)
+                .find(|assoc_item| ns.matches(Some(assoc_item.namespace())))
+                .map(|assoc_item| assoc_item.def_id),
         _ => None,
     }
 }
diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs
index 7f2bf99daff..7d21336be1c 100644
--- a/src/tools/clippy/clippy_utils/src/source.rs
+++ b/src/tools/clippy/clippy_utils/src/source.rs
@@ -7,7 +7,7 @@ use std::sync::Arc;
 use rustc_ast::{LitKind, StrStyle};
 use rustc_errors::Applicability;
 use rustc_hir::{BlockCheckMode, Expr, ExprKind, UnsafeSource};
-use rustc_lexer::{LiteralKind, TokenKind, tokenize};
+use rustc_lexer::{FrontmatterAllowed, LiteralKind, TokenKind, tokenize};
 use rustc_lint::{EarlyContext, LateContext};
 use rustc_middle::ty::TyCtxt;
 use rustc_session::Session;
@@ -277,7 +277,7 @@ fn map_range(
 }
 
 fn ends_with_line_comment_or_broken(text: &str) -> bool {
-    let Some(last) = tokenize(text).last() else {
+    let Some(last) = tokenize(text, FrontmatterAllowed::No).last() else {
         return false;
     };
     match last.kind {
@@ -310,7 +310,8 @@ fn with_leading_whitespace_inner(lines: &[RelativeBytePos], src: &str, range: Ra
         && ends_with_line_comment_or_broken(&start[prev_start..])
         && let next_line = lines.partition_point(|&pos| pos.to_usize() < range.end)
         && let next_start = lines.get(next_line).map_or(src.len(), |&x| x.to_usize())
-        && tokenize(src.get(range.end..next_start)?).any(|t| !matches!(t.kind, TokenKind::Whitespace))
+        && tokenize(src.get(range.end..next_start)?, FrontmatterAllowed::No)
+            .any(|t| !matches!(t.kind, TokenKind::Whitespace))
     {
         Some(range.start)
     } else {
diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs
index bffbcf073ab..fe208c032f4 100644
--- a/src/tools/clippy/clippy_utils/src/ty/mod.rs
+++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs
@@ -889,7 +889,7 @@ impl AdtVariantInfo {
                     .enumerate()
                     .map(|(i, f)| (i, approx_ty_size(cx, f.ty(cx.tcx, subst))))
                     .collect::<Vec<_>>();
-                fields_size.sort_by(|(_, a_size), (_, b_size)| (a_size.cmp(b_size)));
+                fields_size.sort_by(|(_, a_size), (_, b_size)| a_size.cmp(b_size));
 
                 Self {
                     ind: i,
@@ -898,7 +898,7 @@ impl AdtVariantInfo {
                 }
             })
             .collect::<Vec<_>>();
-        variants_size.sort_by(|a, b| (b.size.cmp(&a.size)));
+        variants_size.sort_by(|a, b| b.size.cmp(&a.size));
         variants_size
     }
 }
diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs
index fc6e30a9804..ba5cbc73836 100644
--- a/src/tools/clippy/clippy_utils/src/visitors.rs
+++ b/src/tools/clippy/clippy_utils/src/visitors.rs
@@ -1,3 +1,5 @@
+use crate::msrvs::Msrv;
+use crate::qualify_min_const_fn::is_stable_const_fn;
 use crate::ty::needs_ordered_drop;
 use crate::{get_enclosing_block, path_to_local_id};
 use core::ops::ControlFlow;
@@ -343,17 +345,17 @@ pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) ->
                     .cx
                     .qpath_res(p, hir_id)
                     .opt_def_id()
-                    .is_some_and(|id| self.cx.tcx.is_const_fn(id)) => {},
+                    .is_some_and(|id| is_stable_const_fn(self.cx, id, Msrv::default())) => {},
                 ExprKind::MethodCall(..)
                     if self
                         .cx
                         .typeck_results()
                         .type_dependent_def_id(e.hir_id)
-                        .is_some_and(|id| self.cx.tcx.is_const_fn(id)) => {},
+                        .is_some_and(|id| is_stable_const_fn(self.cx, id, Msrv::default())) => {},
                 ExprKind::Binary(_, lhs, rhs)
                     if self.cx.typeck_results().expr_ty(lhs).peel_refs().is_primitive_ty()
                         && self.cx.typeck_results().expr_ty(rhs).peel_refs().is_primitive_ty() => {},
-                ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_ref() => (),
+                ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_raw_ptr() => (),
                 ExprKind::Unary(_, e) if self.cx.typeck_results().expr_ty(e).peel_refs().is_primitive_ty() => (),
                 ExprKind::Index(base, _, _)
                     if matches!(
@@ -388,7 +390,8 @@ pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) ->
                 | ExprKind::Repeat(..)
                 | ExprKind::Struct(..)
                 | ExprKind::Tup(_)
-                | ExprKind::Type(..) => (),
+                | ExprKind::Type(..)
+                | ExprKind::UnsafeBinderCast(..) => (),
 
                 _ => {
                     return ControlFlow::Break(());
@@ -676,10 +679,7 @@ pub fn for_each_unconsumed_temporary<'tcx, B>(
                     helper(typeck, true, else_expr, f)?;
                 }
             },
-            ExprKind::Type(e, _) => {
-                helper(typeck, consume, e, f)?;
-            },
-            ExprKind::UnsafeBinderCast(_, e, _) => {
+            ExprKind::Type(e, _) | ExprKind::UnsafeBinderCast(_, e, _) => {
                 helper(typeck, consume, e, f)?;
             },
 
diff --git a/src/tools/clippy/lintcheck/src/config.rs b/src/tools/clippy/lintcheck/src/config.rs
index 83c3d7aba02..3b2ebf0c28a 100644
--- a/src/tools/clippy/lintcheck/src/config.rs
+++ b/src/tools/clippy/lintcheck/src/config.rs
@@ -68,6 +68,9 @@ pub(crate) enum Commands {
         /// This will limit the number of warnings that will be printed for each lint
         #[clap(long)]
         truncate: bool,
+        /// Write the diff summary to a JSON file if there are any changes
+        #[clap(long, value_name = "PATH")]
+        write_summary: Option<PathBuf>,
     },
     /// Create a lintcheck crates TOML file containing the top N popular crates
     Popular {
diff --git a/src/tools/clippy/lintcheck/src/json.rs b/src/tools/clippy/lintcheck/src/json.rs
index 8ea0a41ed36..808997ff022 100644
--- a/src/tools/clippy/lintcheck/src/json.rs
+++ b/src/tools/clippy/lintcheck/src/json.rs
@@ -4,8 +4,8 @@
 //! loading warnings from JSON files, and generating human-readable diffs
 //! between different linting runs.
 
-use std::fs;
-use std::path::Path;
+use std::path::{Path, PathBuf};
+use std::{fmt, fs};
 
 use itertools::{EitherOrBoth, Itertools};
 use serde::{Deserialize, Serialize};
@@ -17,7 +17,6 @@ const DEFAULT_LIMIT_PER_LINT: usize = 300;
 /// Target for total warnings to display across all lints when truncating output.
 const TRUNCATION_TOTAL_TARGET: usize = 1000;
 
-/// Representation of a single Clippy warning for JSON serialization.
 #[derive(Debug, Deserialize, Serialize)]
 struct LintJson {
     /// The lint name e.g. `clippy::bytes_nth`
@@ -29,7 +28,6 @@ struct LintJson {
 }
 
 impl LintJson {
-    /// Returns a tuple of name and `file_line` for sorting and comparison.
     fn key(&self) -> impl Ord + '_ {
         (self.name.as_str(), self.file_line.as_str())
     }
@@ -40,6 +38,57 @@ impl LintJson {
     }
 }
 
+#[derive(Debug, Serialize)]
+struct SummaryRow {
+    name: String,
+    added: usize,
+    removed: usize,
+    changed: usize,
+}
+
+#[derive(Debug, Serialize)]
+struct Summary(Vec<SummaryRow>);
+
+impl fmt::Display for Summary {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        f.write_str(
+            "\
+| Lint | Added | Removed | Changed |
+| ---- | ----: | ------: | ------: |
+",
+        )?;
+
+        for SummaryRow {
+            name,
+            added,
+            changed,
+            removed,
+        } in &self.0
+        {
+            let html_id = to_html_id(name);
+            writeln!(f, "| [`{name}`](#{html_id}) | {added} | {changed} | {removed} |")?;
+        }
+
+        Ok(())
+    }
+}
+
+impl Summary {
+    fn new(lints: &[LintWarnings]) -> Self {
+        Summary(
+            lints
+                .iter()
+                .map(|lint| SummaryRow {
+                    name: lint.name.clone(),
+                    added: lint.added.len(),
+                    removed: lint.removed.len(),
+                    changed: lint.changed.len(),
+                })
+                .collect(),
+        )
+    }
+}
+
 /// Creates the log file output for [`crate::config::OutputFormat::Json`]
 pub(crate) fn output(clippy_warnings: Vec<ClippyWarning>) -> String {
     let mut lints: Vec<LintJson> = clippy_warnings
@@ -74,7 +123,7 @@ fn load_warnings(path: &Path) -> Vec<LintJson> {
 ///
 /// Compares warnings from `old_path` and `new_path`, then displays a summary table
 /// and detailed information about added, removed, and changed warnings.
-pub(crate) fn diff(old_path: &Path, new_path: &Path, truncate: bool) {
+pub(crate) fn diff(old_path: &Path, new_path: &Path, truncate: bool, write_summary: Option<PathBuf>) {
     let old_warnings = load_warnings(old_path);
     let new_warnings = load_warnings(new_path);
 
@@ -108,13 +157,16 @@ pub(crate) fn diff(old_path: &Path, new_path: &Path, truncate: bool) {
         }
     }
 
-    print_summary_table(&lint_warnings);
-    println!();
-
     if lint_warnings.is_empty() {
         return;
     }
 
+    let summary = Summary::new(&lint_warnings);
+    if let Some(path) = write_summary {
+        let json = serde_json::to_string(&summary).unwrap();
+        fs::write(path, json).unwrap();
+    }
+
     let truncate_after = if truncate {
         // Max 15 ensures that we at least have five messages per lint
         DEFAULT_LIMIT_PER_LINT
@@ -126,6 +178,7 @@ pub(crate) fn diff(old_path: &Path, new_path: &Path, truncate: bool) {
         usize::MAX
     };
 
+    println!("{summary}");
     for lint in lint_warnings {
         print_lint_warnings(&lint, truncate_after);
     }
@@ -140,13 +193,11 @@ struct LintWarnings {
     changed: Vec<(LintJson, LintJson)>,
 }
 
-/// Prints a formatted report for a single lint type with its warnings.
 fn print_lint_warnings(lint: &LintWarnings, truncate_after: usize) {
     let name = &lint.name;
     let html_id = to_html_id(name);
 
-    // The additional anchor is added for non GH viewers that don't prefix ID's
-    println!(r#"## `{name}` <a id="user-content-{html_id}"></a>"#);
+    println!(r#"<h2 id="{html_id}"><code>{name}</code></h2>"#);
     println!();
 
     print!(
@@ -162,22 +213,6 @@ fn print_lint_warnings(lint: &LintWarnings, truncate_after: usize) {
     print_changed_diff(&lint.changed, truncate_after / 3);
 }
 
-/// Prints a summary table of all lints with counts of added, removed, and changed warnings.
-fn print_summary_table(lints: &[LintWarnings]) {
-    println!("| Lint                                       | Added   | Removed | Changed |");
-    println!("| ------------------------------------------ | ------: | ------: | ------: |");
-
-    for lint in lints {
-        println!(
-            "| {:<62} | {:>7} | {:>7} | {:>7} |",
-            format!("[`{}`](#user-content-{})", lint.name, to_html_id(&lint.name)),
-            lint.added.len(),
-            lint.removed.len(),
-            lint.changed.len()
-        );
-    }
-}
-
 /// Prints a section of warnings with a header and formatted code blocks.
 fn print_warnings(title: &str, warnings: &[LintJson], truncate_after: usize) {
     if warnings.is_empty() {
@@ -248,17 +283,16 @@ fn truncate<T>(list: &[T], truncate_after: usize) -> &[T] {
     }
 }
 
-/// Prints a level 3 heading with an appropriate HTML ID for linking.
 fn print_h3(lint: &str, title: &str) {
     let html_id = to_html_id(lint);
-    // We have to use HTML here to be able to manually add an id.
-    println!(r#"### {title} <a id="user-content-{html_id}-{title}"></a>"#);
+    // We have to use HTML here to be able to manually add an id, GitHub doesn't add them automatically
+    println!(r#"<h3 id="{html_id}-{title}">{title}</h3>"#);
 }
 
-/// GitHub's markdown parsers doesn't like IDs with `::` and `_`. This simplifies
-/// the lint name for the HTML ID.
+/// Creates a custom ID allowed by GitHub, they must start with `user-content-` and cannot contain
+/// `::`/`_`
 fn to_html_id(lint_name: &str) -> String {
-    lint_name.replace("clippy::", "").replace('_', "-")
+    lint_name.replace("clippy::", "user-content-").replace('_', "-")
 }
 
 /// This generates the `x added` string for the start of the job summery.
@@ -270,9 +304,6 @@ fn count_string(lint: &str, label: &str, count: usize) -> String {
         format!("0 {label}")
     } else {
         let html_id = to_html_id(lint);
-        // GitHub's job summaries don't add HTML ids to headings. That's why we
-        // manually have to add them. GitHub prefixes these manual ids with
-        // `user-content-` and that's how we end up with these awesome links :D
-        format!("[{count} {label}](#user-content-{html_id}-{label})")
+        format!("[{count} {label}](#{html_id}-{label})")
     }
 }
diff --git a/src/tools/clippy/lintcheck/src/main.rs b/src/tools/clippy/lintcheck/src/main.rs
index eb390eecbcc..3a60cfa79f4 100644
--- a/src/tools/clippy/lintcheck/src/main.rs
+++ b/src/tools/clippy/lintcheck/src/main.rs
@@ -303,7 +303,12 @@ fn main() {
     let config = LintcheckConfig::new();
 
     match config.subcommand {
-        Some(Commands::Diff { old, new, truncate }) => json::diff(&old, &new, truncate),
+        Some(Commands::Diff {
+            old,
+            new,
+            truncate,
+            write_summary,
+        }) => json::diff(&old, &new, truncate, write_summary),
         Some(Commands::Popular { output, number }) => popular_crates::fetch(output, number).unwrap(),
         None => lintcheck(config),
     }
diff --git a/src/tools/clippy/rust-toolchain.toml b/src/tools/clippy/rust-toolchain.toml
index 124756a3600..f46e079db3f 100644
--- a/src/tools/clippy/rust-toolchain.toml
+++ b/src/tools/clippy/rust-toolchain.toml
@@ -1,6 +1,6 @@
 [toolchain]
 # begin autogenerated nightly
-channel = "nightly-2025-06-26"
+channel = "nightly-2025-07-10"
 # end autogenerated nightly
 components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
 profile = "minimal"
diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs
index cefe654fef6..57d623b2cfc 100644
--- a/src/tools/clippy/tests/compile-test.rs
+++ b/src/tools/clippy/tests/compile-test.rs
@@ -16,8 +16,10 @@ use test_utils::IS_RUSTC_TEST_SUITE;
 use ui_test::custom_flags::Flag;
 use ui_test::custom_flags::edition::Edition;
 use ui_test::custom_flags::rustfix::RustfixMode;
+use ui_test::dependencies::DependencyBuilder;
 use ui_test::spanned::Spanned;
-use ui_test::{Args, CommandBuilder, Config, Match, error_on_output_conflict, status_emitter};
+use ui_test::status_emitter::StatusEmitter;
+use ui_test::{Args, CommandBuilder, Config, Match, error_on_output_conflict};
 
 use std::collections::{BTreeMap, HashMap};
 use std::env::{self, set_var, var_os};
@@ -27,46 +29,26 @@ use std::path::{Path, PathBuf};
 use std::sync::mpsc::{Sender, channel};
 use std::{fs, iter, thread};
 
-// Test dependencies may need an `extern crate` here to ensure that they show up
-// in the depinfo file (otherwise cargo thinks they are unused)
-extern crate futures;
-extern crate if_chain;
-extern crate itertools;
-extern crate parking_lot;
-extern crate quote;
-extern crate syn;
-extern crate tokio;
-
 mod test_utils;
 
-/// All crates used in UI tests are listed here
-static TEST_DEPENDENCIES: &[&str] = &[
-    "clippy_config",
-    "clippy_lints",
-    "clippy_utils",
-    "futures",
-    "if_chain",
-    "itertools",
-    "parking_lot",
-    "quote",
-    "regex",
-    "serde_derive",
-    "serde",
-    "syn",
-    "tokio",
-];
-
-/// Produces a string with an `--extern` flag for all UI test crate
-/// dependencies.
+/// All crates used in internal UI tests are listed here.
+/// We directly re-use these crates from their normal clippy builds, so we don't have them
+/// in `clippy_test_devs`. That saves a lot of time but also means they don't work in a stage 1
+/// test in rustc bootstrap.
+static INTERNAL_TEST_DEPENDENCIES: &[&str] = &["clippy_config", "clippy_lints", "clippy_utils"];
+
+/// Produces a string with an `--extern` flag for all `INTERNAL_TEST_DEPENDENCIES`.
 ///
 /// The dependency files are located by parsing the depinfo file for this test
 /// module. This assumes the `-Z binary-dep-depinfo` flag is enabled. All test
 /// dependencies must be added to Cargo.toml at the project root. Test
 /// dependencies that are not *directly* used by this test module require an
 /// `extern crate` declaration.
-fn extern_flags() -> Vec<String> {
+fn internal_extern_flags() -> Vec<String> {
+    let current_exe_path = env::current_exe().unwrap();
+    let deps_path = current_exe_path.parent().unwrap();
     let current_exe_depinfo = {
-        let mut path = env::current_exe().unwrap();
+        let mut path = current_exe_path.clone();
         path.set_extension("d");
         fs::read_to_string(path).unwrap()
     };
@@ -88,7 +70,7 @@ fn extern_flags() -> Vec<String> {
             Some((name, path_str))
         };
         if let Some((name, path)) = parse_name_path()
-            && TEST_DEPENDENCIES.contains(&name)
+            && INTERNAL_TEST_DEPENDENCIES.contains(&name)
         {
             // A dependency may be listed twice if it is available in sysroot,
             // and the sysroot dependencies are listed first. As of the writing,
@@ -96,7 +78,7 @@ fn extern_flags() -> Vec<String> {
             crates.insert(name, path);
         }
     }
-    let not_found: Vec<&str> = TEST_DEPENDENCIES
+    let not_found: Vec<&str> = INTERNAL_TEST_DEPENDENCIES
         .iter()
         .copied()
         .filter(|n| !crates.contains_key(n))
@@ -111,6 +93,7 @@ fn extern_flags() -> Vec<String> {
     crates
         .into_iter()
         .map(|(name, path)| format!("--extern={name}={path}"))
+        .chain([format!("-Ldependency={}", deps_path.display())])
         .collect()
 }
 
@@ -119,7 +102,6 @@ const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal");
 
 struct TestContext {
     args: Args,
-    extern_flags: Vec<String>,
     diagnostic_collector: Option<DiagnosticCollector>,
     collector_thread: Option<thread::JoinHandle<()>>,
 }
@@ -134,7 +116,6 @@ impl TestContext {
             .unzip();
         Self {
             args,
-            extern_flags: extern_flags(),
             diagnostic_collector,
             collector_thread,
         }
@@ -144,8 +125,17 @@ impl TestContext {
         let target_dir = PathBuf::from(var_os("CARGO_TARGET_DIR").unwrap_or_else(|| "target".into()));
         let mut config = Config {
             output_conflict_handling: error_on_output_conflict,
+            // Pre-fill filters with TESTNAME; will be later extended with `self.args`.
             filter_files: env::var("TESTNAME")
-                .map(|filters| filters.split(',').map(str::to_string).collect())
+                .map(|filters| {
+                    filters
+                        .split(',')
+                        // Make sure that if TESTNAME is empty we produce the empty list here,
+                        // not a list containing an empty string.
+                        .filter(|s| !s.is_empty())
+                        .map(str::to_string)
+                        .collect()
+                })
                 .unwrap_or_default(),
             target: None,
             bless_command: Some(if IS_RUSTC_TEST_SUITE {
@@ -158,6 +148,15 @@ impl TestContext {
         };
         let defaults = config.comment_defaults.base();
         defaults.set_custom("edition", Edition("2024".into()));
+        defaults.set_custom(
+            "dependencies",
+            DependencyBuilder {
+                program: CommandBuilder::cargo(),
+                crate_manifest_path: Path::new("clippy_test_deps").join("Cargo.toml"),
+                build_std: None,
+                bless_lockfile: self.args.bless,
+            },
+        );
         defaults.exit_status = None.into();
         if mandatory_annotations {
             defaults.require_annotations = Some(Spanned::dummy(true)).into();
@@ -182,12 +181,10 @@ impl TestContext {
                 "-Zui-testing",
                 "-Zdeduplicate-diagnostics=no",
                 "-Dwarnings",
-                &format!("-Ldependency={}", deps_path.display()),
             ]
             .map(OsString::from),
         );
 
-        config.program.args.extend(self.extern_flags.iter().map(OsString::from));
         // Prevent rustc from creating `rustc-ice-*` files the console output is enough.
         config.program.envs.push(("RUSTC_ICE".into(), Some("0".into())));
 
@@ -217,7 +214,7 @@ fn run_ui(cx: &TestContext) {
         vec![config],
         ui_test::default_file_filter,
         ui_test::default_per_file_config,
-        status_emitter::Text::from(cx.args.format),
+        Box::<dyn StatusEmitter>::from(cx.args.format),
     )
     .unwrap();
 }
@@ -227,13 +224,17 @@ fn run_internal_tests(cx: &TestContext) {
         return;
     }
     let mut config = cx.base_config("ui-internal", true);
+    config
+        .program
+        .args
+        .extend(internal_extern_flags().iter().map(OsString::from));
     config.bless_command = Some("cargo uitest --features internal -- -- --bless".into());
 
     ui_test::run_tests_generic(
         vec![config],
         ui_test::default_file_filter,
         ui_test::default_per_file_config,
-        status_emitter::Text::from(cx.args.format),
+        Box::<dyn StatusEmitter>::from(cx.args.format),
     )
     .unwrap();
 }
@@ -257,7 +258,7 @@ fn run_ui_toml(cx: &TestContext) {
                 .envs
                 .push(("CLIPPY_CONF_DIR".into(), Some(path.parent().unwrap().into())));
         },
-        status_emitter::Text::from(cx.args.format),
+        Box::<dyn StatusEmitter>::from(cx.args.format),
     )
     .unwrap();
 }
@@ -304,7 +305,7 @@ fn run_ui_cargo(cx: &TestContext) {
                 .then(|| ui_test::default_any_file_filter(path, config) && !ignored_32bit(path))
         },
         |_config, _file_contents| {},
-        status_emitter::Text::from(cx.args.format),
+        Box::<dyn StatusEmitter>::from(cx.args.format),
     )
     .unwrap();
 }
diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default.stderr
index a3c35a31c33..87e4b0c5c7d 100644
--- a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default.stderr
+++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default.stderr
@@ -136,13 +136,13 @@ error: incorrect ordering of trait items (defined order: [Const, Type, Fn])
   --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:155:5
    |
 LL |     const A: bool;
-   |     ^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^
    |
 note: should be placed before `SomeType`
   --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:153:5
    |
 LL |     type SomeType;
-   |     ^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^
 
 error: incorrect ordering of items (must be alphabetically ordered)
   --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:171:11
@@ -172,13 +172,13 @@ error: incorrect ordering of impl items (defined order: [Const, Type, Fn])
   --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:189:5
    |
 LL |     const A: bool = false;
-   |     ^^^^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^
    |
 note: should be placed before `SomeType`
   --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:187:5
    |
 LL |     type SomeType = ();
-   |     ^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^
 
 error: aborting due to 15 previous errors
 
diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default_exp.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default_exp.stderr
index a3c35a31c33..87e4b0c5c7d 100644
--- a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default_exp.stderr
+++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default_exp.stderr
@@ -136,13 +136,13 @@ error: incorrect ordering of trait items (defined order: [Const, Type, Fn])
   --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:155:5
    |
 LL |     const A: bool;
-   |     ^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^
    |
 note: should be placed before `SomeType`
   --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:153:5
    |
 LL |     type SomeType;
-   |     ^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^
 
 error: incorrect ordering of items (must be alphabetically ordered)
   --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:171:11
@@ -172,13 +172,13 @@ error: incorrect ordering of impl items (defined order: [Const, Type, Fn])
   --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:189:5
    |
 LL |     const A: bool = false;
-   |     ^^^^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^
    |
 note: should be placed before `SomeType`
   --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:187:5
    |
 LL |     type SomeType = ();
-   |     ^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^
 
 error: aborting due to 15 previous errors
 
diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.ord_within.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.ord_within.stderr
index 3fdd706fc62..40505e2a1c4 100644
--- a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.ord_within.stderr
+++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.ord_within.stderr
@@ -211,13 +211,13 @@ error: incorrect ordering of trait items (defined order: [Const, Type, Fn])
   --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:155:5
    |
 LL |     const A: bool;
-   |     ^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^
    |
 note: should be placed before `SomeType`
   --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:153:5
    |
 LL |     type SomeType;
-   |     ^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^
 
 error: incorrect ordering of items (must be alphabetically ordered)
   --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:171:11
@@ -247,13 +247,13 @@ error: incorrect ordering of impl items (defined order: [Const, Type, Fn])
   --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:189:5
    |
 LL |     const A: bool = false;
-   |     ^^^^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^
    |
 note: should be placed before `SomeType`
   --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:187:5
    |
 LL |     type SomeType = ();
-   |     ^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^
 
 error: incorrect ordering of items (must be alphabetically ordered)
   --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:207:11
diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.var_1.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.var_1.stderr
index 730f12c38a0..d8db2243d41 100644
--- a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.var_1.stderr
+++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.var_1.stderr
@@ -28,25 +28,25 @@ error: incorrect ordering of impl items (defined order: [Fn, Type, Const])
   --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:119:5
    |
 LL |     type SomeType = ();
-   |     ^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^
    |
 note: should be placed before `A`
   --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:117:5
    |
 LL |     const A: bool = false;
-   |     ^^^^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^
 
 error: incorrect ordering of impl items (defined order: [Fn, Type, Const])
   --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:122:5
    |
 LL |     fn a() {}
-   |     ^^^^^^^^^
+   |     ^^^^^^
    |
 note: should be placed before `SomeType`
   --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:119:5
    |
 LL |     type SomeType = ();
-   |     ^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^
 
 error: incorrect ordering of items (must be alphabetically ordered)
   --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:149:8
@@ -76,13 +76,13 @@ error: incorrect ordering of trait items (defined order: [Fn, Type, Const])
   --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:163:5
    |
 LL |     type SomeType;
-   |     ^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^
    |
 note: should be placed before `A`
   --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:161:5
    |
 LL |     const A: bool;
-   |     ^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^
 
 error: incorrect ordering of trait items (defined order: [Fn, Type, Const])
   --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:166:5
@@ -94,7 +94,7 @@ note: should be placed before `SomeType`
   --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:163:5
    |
 LL |     type SomeType;
-   |     ^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^
 
 error: aborting due to 8 previous errors
 
diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.only_impl.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.only_impl.stderr
index 77596ba2394..7f6bddf8005 100644
--- a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.only_impl.stderr
+++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.only_impl.stderr
@@ -16,25 +16,25 @@ error: incorrect ordering of impl items (defined order: [Const, Type, Fn])
   --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.rs:46:5
    |
 LL |     type SomeType = i8;
-   |     ^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^
    |
 note: should be placed before `a`
   --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.rs:43:5
    |
 LL |     fn a() {}
-   |     ^^^^^^^^^
+   |     ^^^^^^
 
 error: incorrect ordering of impl items (defined order: [Const, Type, Fn])
   --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.rs:49:5
    |
 LL |     const A: bool = true;
-   |     ^^^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^
    |
 note: should be placed before `SomeType`
   --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.rs:46:5
    |
 LL |     type SomeType = i8;
-   |     ^^^^^^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^
 
 error: aborting due to 3 previous errors
 
diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.only_trait.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.only_trait.stderr
index 3d903330be8..a7cff238b78 100644
--- a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.only_trait.stderr
+++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.only_trait.stderr
@@ -28,13 +28,13 @@ error: incorrect ordering of trait items (defined order: [Const, Type, Fn])
   --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.rs:45:5
    |
 LL |     const A: bool;
-   |     ^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^
    |
 note: should be placed before `SomeType`
   --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.rs:43:5
    |
 LL |     type SomeType;
-   |     ^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^^
 
 error: aborting due to 3 previous errors
 
diff --git a/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr
index 20cdff5fcd1..cebfc48a884 100644
--- a/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr
+++ b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.disabled.stderr
@@ -450,5 +450,13 @@ help: consider removing the safety comment
 LL |     // SAFETY: unnecessary_safety_comment triggers here
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 52 previous errors
+error: unsafe block missing a safety comment
+  --> tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs:733:12
+   |
+LL |     return unsafe { h() };
+   |            ^^^^^^^^^^^^^^
+   |
+   = help: consider adding a safety comment on the preceding line
+
+error: aborting due to 53 previous errors
 
diff --git a/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs
index 91a02bc3d7c..a2d7c1b6c79 100644
--- a/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs
+++ b/src/tools/clippy/tests/ui-toml/undocumented_unsafe_blocks/undocumented_unsafe_blocks.rs
@@ -723,4 +723,15 @@ fn issue_13039() {
     _ = unsafe { foo() }
 }
 
+fn rfl_issue15034() -> i32 {
+    unsafe fn h() -> i32 {
+        1i32
+    }
+    // This shouldn't lint with accept-comment-above-attributes! Thus fixing a false positive!
+    // SAFETY: My safety comment!
+    #[allow(clippy::unnecessary_cast)]
+    return unsafe { h() };
+    //~[disabled]^ ERROR: unsafe block missing a safety comment
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/assign_ops.fixed b/src/tools/clippy/tests/ui/assign_ops.fixed
index 99beea850a2..eee61f949e7 100644
--- a/src/tools/clippy/tests/ui/assign_ops.fixed
+++ b/src/tools/clippy/tests/ui/assign_ops.fixed
@@ -84,8 +84,7 @@ mod issue14871 {
         const ONE: Self;
     }
 
-    #[const_trait]
-    pub trait NumberConstants {
+    pub const trait NumberConstants {
         fn constant(value: usize) -> Self;
     }
 
diff --git a/src/tools/clippy/tests/ui/assign_ops.rs b/src/tools/clippy/tests/ui/assign_ops.rs
index 900d5ad38e0..13ffcee0a3c 100644
--- a/src/tools/clippy/tests/ui/assign_ops.rs
+++ b/src/tools/clippy/tests/ui/assign_ops.rs
@@ -84,8 +84,7 @@ mod issue14871 {
         const ONE: Self;
     }
 
-    #[const_trait]
-    pub trait NumberConstants {
+    pub const trait NumberConstants {
         fn constant(value: usize) -> Self;
     }
 
diff --git a/src/tools/clippy/tests/ui/author/if.stdout b/src/tools/clippy/tests/ui/author/if.stdout
index da359866bff..dbff55634ea 100644
--- a/src/tools/clippy/tests/ui/author/if.stdout
+++ b/src/tools/clippy/tests/ui/author/if.stdout
@@ -1,8 +1,7 @@
 if let StmtKind::Let(local) = stmt.kind
     && let Some(init) = local.init
     && let ExprKind::If(cond, then, Some(else_expr)) = init.kind
-    && let ExprKind::DropTemps(expr) = cond.kind
-    && let ExprKind::Lit(ref lit) = expr.kind
+    && let ExprKind::Lit(ref lit) = cond.kind
     && let LitKind::Bool(true) = lit.node
     && let ExprKind::Block(block, None) = then.kind
     && block.stmts.len() == 1
diff --git a/src/tools/clippy/tests/ui/author/struct.stdout b/src/tools/clippy/tests/ui/author/struct.stdout
index 1e8fbafd30c..2dedab56dce 100644
--- a/src/tools/clippy/tests/ui/author/struct.stdout
+++ b/src/tools/clippy/tests/ui/author/struct.stdout
@@ -2,8 +2,7 @@ if let ExprKind::Struct(qpath, fields, None) = expr.kind
     && fields.len() == 1
     && fields[0].ident.as_str() == "field"
     && let ExprKind::If(cond, then, Some(else_expr)) = fields[0].expr.kind
-    && let ExprKind::DropTemps(expr1) = cond.kind
-    && let ExprKind::Lit(ref lit) = expr1.kind
+    && let ExprKind::Lit(ref lit) = cond.kind
     && let LitKind::Bool(true) = lit.node
     && let ExprKind::Block(block, None) = then.kind
     && block.stmts.is_empty()
diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr.fixed b/src/tools/clippy/tests/ui/borrow_as_ptr.fixed
index 3ba2eea59f0..3f6e5245b87 100644
--- a/src/tools/clippy/tests/ui/borrow_as_ptr.fixed
+++ b/src/tools/clippy/tests/ui/borrow_as_ptr.fixed
@@ -47,3 +47,9 @@ fn implicit_cast() {
     // Do not lint references to temporaries
     core::ptr::eq(&0i32, &1i32);
 }
+
+fn issue_15141() {
+    let a = String::new();
+    // Don't lint cast to dyn trait pointers
+    let b = &a as *const dyn std::any::Any;
+}
diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr.rs b/src/tools/clippy/tests/ui/borrow_as_ptr.rs
index 8cdd0512da5..20f4f40e001 100644
--- a/src/tools/clippy/tests/ui/borrow_as_ptr.rs
+++ b/src/tools/clippy/tests/ui/borrow_as_ptr.rs
@@ -47,3 +47,9 @@ fn implicit_cast() {
     // Do not lint references to temporaries
     core::ptr::eq(&0i32, &1i32);
 }
+
+fn issue_15141() {
+    let a = String::new();
+    // Don't lint cast to dyn trait pointers
+    let b = &a as *const dyn std::any::Any;
+}
diff --git a/src/tools/clippy/tests/ui/cast_size.32bit.stderr b/src/tools/clippy/tests/ui/cast_size.32bit.stderr
index cb1620e36a2..5811cb3607b 100644
--- a/src/tools/clippy/tests/ui/cast_size.32bit.stderr
+++ b/src/tools/clippy/tests/ui/cast_size.32bit.stderr
@@ -177,6 +177,14 @@ error: casting `usize` to `f64` causes a loss of precision on targets with 64-bi
 LL |     9_999_999_999_999_999usize as f64;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
+error: casting `usize` to `u16` may truncate the value
+  --> tests/ui/cast_size.rs:71:20
+   |
+LL |     const N: u16 = M as u16;
+   |                    ^^^^^^^^
+   |
+   = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
+
 error: literal out of range for `usize`
   --> tests/ui/cast_size.rs:63:5
    |
@@ -186,5 +194,5 @@ LL |     9_999_999_999_999_999usize as f64;
    = note: the literal `9_999_999_999_999_999usize` does not fit into the type `usize` whose range is `0..=4294967295`
    = note: `#[deny(overflowing_literals)]` on by default
 
-error: aborting due to 19 previous errors
+error: aborting due to 20 previous errors
 
diff --git a/src/tools/clippy/tests/ui/cast_size.64bit.stderr b/src/tools/clippy/tests/ui/cast_size.64bit.stderr
index b6000a52abb..ba1419583ae 100644
--- a/src/tools/clippy/tests/ui/cast_size.64bit.stderr
+++ b/src/tools/clippy/tests/ui/cast_size.64bit.stderr
@@ -177,5 +177,13 @@ error: casting `usize` to `f64` causes a loss of precision on targets with 64-bi
 LL |     9_999_999_999_999_999usize as f64;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 18 previous errors
+error: casting `usize` to `u16` may truncate the value
+  --> tests/ui/cast_size.rs:71:20
+   |
+LL |     const N: u16 = M as u16;
+   |                    ^^^^^^^^
+   |
+   = help: if this is intentional allow the lint with `#[allow(clippy::cast_possible_truncation)]` ...
+
+error: aborting due to 19 previous errors
 
diff --git a/src/tools/clippy/tests/ui/cast_size.rs b/src/tools/clippy/tests/ui/cast_size.rs
index e5bef2a99d5..ecc58669419 100644
--- a/src/tools/clippy/tests/ui/cast_size.rs
+++ b/src/tools/clippy/tests/ui/cast_size.rs
@@ -65,3 +65,9 @@ fn main() {
     //~[32bit]^^ ERROR: literal out of range for `usize`
     // 999_999_999_999_999_999_999_999_999_999u128 as f128;
 }
+
+fn issue15163() {
+    const M: usize = 100;
+    const N: u16 = M as u16;
+    //~^ cast_possible_truncation
+}
diff --git a/src/tools/clippy/tests/ui/coerce_container_to_any.fixed b/src/tools/clippy/tests/ui/coerce_container_to_any.fixed
index ae9d3ef9656..b5b3f15b4de 100644
--- a/src/tools/clippy/tests/ui/coerce_container_to_any.fixed
+++ b/src/tools/clippy/tests/ui/coerce_container_to_any.fixed
@@ -3,7 +3,7 @@
 use std::any::Any;
 
 fn main() {
-    let x: Box<dyn Any> = Box::new(());
+    let mut x: Box<dyn Any> = Box::new(());
     let ref_x = &x;
 
     f(&*x);
@@ -15,12 +15,23 @@ fn main() {
     let _: &dyn Any = &*x;
     //~^ coerce_container_to_any
 
+    let _: &dyn Any = &*x;
+    //~^ coerce_container_to_any
+
+    let _: &mut dyn Any = &mut *x;
+    //~^ coerce_container_to_any
+
     f(&42);
     f(&Box::new(()));
     f(&Box::new(Box::new(())));
+    let ref_x = &x;
     f(&**ref_x);
     f(&*x);
     let _: &dyn Any = &*x;
+
+    // https://github.com/rust-lang/rust-clippy/issues/15045
+    #[allow(clippy::needless_borrow)]
+    (&x).downcast_ref::<()>().unwrap();
 }
 
 fn f(_: &dyn Any) {}
diff --git a/src/tools/clippy/tests/ui/coerce_container_to_any.rs b/src/tools/clippy/tests/ui/coerce_container_to_any.rs
index 9948bd48e0d..4d6527bb552 100644
--- a/src/tools/clippy/tests/ui/coerce_container_to_any.rs
+++ b/src/tools/clippy/tests/ui/coerce_container_to_any.rs
@@ -3,7 +3,7 @@
 use std::any::Any;
 
 fn main() {
-    let x: Box<dyn Any> = Box::new(());
+    let mut x: Box<dyn Any> = Box::new(());
     let ref_x = &x;
 
     f(&x);
@@ -15,12 +15,23 @@ fn main() {
     let _: &dyn Any = &x;
     //~^ coerce_container_to_any
 
+    let _: &dyn Any = &mut x;
+    //~^ coerce_container_to_any
+
+    let _: &mut dyn Any = &mut x;
+    //~^ coerce_container_to_any
+
     f(&42);
     f(&Box::new(()));
     f(&Box::new(Box::new(())));
+    let ref_x = &x;
     f(&**ref_x);
     f(&*x);
     let _: &dyn Any = &*x;
+
+    // https://github.com/rust-lang/rust-clippy/issues/15045
+    #[allow(clippy::needless_borrow)]
+    (&x).downcast_ref::<()>().unwrap();
 }
 
 fn f(_: &dyn Any) {}
diff --git a/src/tools/clippy/tests/ui/coerce_container_to_any.stderr b/src/tools/clippy/tests/ui/coerce_container_to_any.stderr
index 00ab77e0ce0..26389c9186e 100644
--- a/src/tools/clippy/tests/ui/coerce_container_to_any.stderr
+++ b/src/tools/clippy/tests/ui/coerce_container_to_any.stderr
@@ -19,5 +19,17 @@ error: coercing `&std::boxed::Box<dyn std::any::Any>` to `&dyn Any`
 LL |     let _: &dyn Any = &x;
    |                       ^^ help: consider dereferencing: `&*x`
 
-error: aborting due to 3 previous errors
+error: coercing `&mut std::boxed::Box<dyn std::any::Any>` to `&dyn Any`
+  --> tests/ui/coerce_container_to_any.rs:18:23
+   |
+LL |     let _: &dyn Any = &mut x;
+   |                       ^^^^^^ help: consider dereferencing: `&*x`
+
+error: coercing `&mut std::boxed::Box<dyn std::any::Any>` to `&mut dyn Any`
+  --> tests/ui/coerce_container_to_any.rs:21:27
+   |
+LL |     let _: &mut dyn Any = &mut x;
+   |                           ^^^^^^ help: consider dereferencing: `&mut *x`
+
+error: aborting due to 5 previous errors
 
diff --git a/src/tools/clippy/tests/ui/disallowed_script_idents.rs b/src/tools/clippy/tests/ui/disallowed_script_idents.rs
index 08fd1d9669e..dae380045ae 100644
--- a/src/tools/clippy/tests/ui/disallowed_script_idents.rs
+++ b/src/tools/clippy/tests/ui/disallowed_script_idents.rs
@@ -15,3 +15,17 @@ fn main() {
     let カウンタ = 10;
     //~^ disallowed_script_idents
 }
+
+fn issue15116() {
+    const ÄÖÜ: u8 = 0;
+    const _ÄÖÜ: u8 = 0;
+    const Ä_ÖÜ: u8 = 0;
+    const ÄÖ_Ü: u8 = 0;
+    const ÄÖÜ_: u8 = 0;
+    let äöüß = 1;
+    let _äöüß = 1;
+    let ä_öüß = 1;
+    let äö_üß = 1;
+    let äöü_ß = 1;
+    let äöüß_ = 1;
+}
diff --git a/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.fixed b/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.fixed
index 065f4486e39..5c57c58fbc0 100644
--- a/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.fixed
+++ b/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.fixed
@@ -72,8 +72,6 @@ pub struct NotEmptyTight;
 
 /// ## Heading
 ///
-/// - [x][] - Done
-//~^ ERROR: link reference defined in list item
-/// - [ ][] - Not Done
-//~^ ERROR: link reference defined in list item
+/// - [x] - Done
+/// - [ ] - Not Done
 pub struct GithubCheckboxes;
diff --git a/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.rs b/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.rs
index c7eab50c8b3..06b6ba49e19 100644
--- a/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.rs
+++ b/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.rs
@@ -73,7 +73,5 @@ pub struct NotEmptyTight;
 /// ## Heading
 ///
 /// - [x] - Done
-//~^ ERROR: link reference defined in list item
 /// - [ ] - Not Done
-//~^ ERROR: link reference defined in list item
 pub struct GithubCheckboxes;
diff --git a/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.stderr b/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.stderr
index 5a815dabf4d..27314c7e968 100644
--- a/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.stderr
+++ b/src/tools/clippy/tests/ui/doc/doc_nested_refdef_list_item.stderr
@@ -144,29 +144,5 @@ help: for an intra-doc link, add `[]` between the label and the colon
 LL | /// - [link][]: def "title"
    |             ++
 
-error: link reference defined in list item
-  --> tests/ui/doc/doc_nested_refdef_list_item.rs:75:7
-   |
-LL | /// - [x] - Done
-   |       ^^^
-   |
-   = help: link definitions are not shown in rendered documentation
-help: for an intra-doc link, add `[]` between the label and the colon
-   |
-LL | /// - [x][] - Done
-   |          ++
-
-error: link reference defined in list item
-  --> tests/ui/doc/doc_nested_refdef_list_item.rs:77:7
-   |
-LL | /// - [ ] - Not Done
-   |       ^^^
-   |
-   = help: link definitions are not shown in rendered documentation
-help: for an intra-doc link, add `[]` between the label and the colon
-   |
-LL | /// - [ ][] - Not Done
-   |          ++
-
-error: aborting due to 14 previous errors
+error: aborting due to 12 previous errors
 
diff --git a/src/tools/clippy/tests/ui/empty_loop_intrinsic.rs b/src/tools/clippy/tests/ui/empty_loop_intrinsic.rs
new file mode 100644
index 00000000000..a550e560965
--- /dev/null
+++ b/src/tools/clippy/tests/ui/empty_loop_intrinsic.rs
@@ -0,0 +1,13 @@
+//@check-pass
+
+#![warn(clippy::empty_loop)]
+#![feature(intrinsics)]
+#![feature(rustc_attrs)]
+
+// From issue #15200
+#[rustc_intrinsic]
+#[rustc_nounwind]
+/// # Safety
+pub const unsafe fn simd_insert<T, U>(x: T, idx: u32, val: U) -> T;
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/exit1_compile_flag_test.rs b/src/tools/clippy/tests/ui/exit1_compile_flag_test.rs
new file mode 100644
index 00000000000..9f83ed32533
--- /dev/null
+++ b/src/tools/clippy/tests/ui/exit1_compile_flag_test.rs
@@ -0,0 +1,17 @@
+//@compile-flags: --test
+#![warn(clippy::exit)]
+
+fn not_main() {
+    if true {
+        std::process::exit(4);
+        //~^ exit
+    }
+}
+
+fn main() {
+    if true {
+        std::process::exit(2);
+    };
+    not_main();
+    std::process::exit(1);
+}
diff --git a/src/tools/clippy/tests/ui/exit1_compile_flag_test.stderr b/src/tools/clippy/tests/ui/exit1_compile_flag_test.stderr
new file mode 100644
index 00000000000..6e33c39f0d7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/exit1_compile_flag_test.stderr
@@ -0,0 +1,11 @@
+error: usage of `process::exit`
+  --> tests/ui/exit1_compile_flag_test.rs:6:9
+   |
+LL |         std::process::exit(4);
+   |         ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::exit` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::exit)]`
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/clippy/tests/ui/exit2_compile_flag_test.rs b/src/tools/clippy/tests/ui/exit2_compile_flag_test.rs
new file mode 100644
index 00000000000..0b994ebc56c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/exit2_compile_flag_test.rs
@@ -0,0 +1,15 @@
+//@compile-flags: --test
+#![warn(clippy::exit)]
+
+fn also_not_main() {
+    std::process::exit(3);
+    //~^ exit
+}
+
+fn main() {
+    if true {
+        std::process::exit(2);
+    };
+    also_not_main();
+    std::process::exit(1);
+}
diff --git a/src/tools/clippy/tests/ui/exit2_compile_flag_test.stderr b/src/tools/clippy/tests/ui/exit2_compile_flag_test.stderr
new file mode 100644
index 00000000000..51eb26e9c2a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/exit2_compile_flag_test.stderr
@@ -0,0 +1,11 @@
+error: usage of `process::exit`
+  --> tests/ui/exit2_compile_flag_test.rs:5:5
+   |
+LL |     std::process::exit(3);
+   |     ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::exit` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::exit)]`
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/clippy/tests/ui/exit3_compile_flag_test.rs b/src/tools/clippy/tests/ui/exit3_compile_flag_test.rs
new file mode 100644
index 00000000000..f8131ead2da
--- /dev/null
+++ b/src/tools/clippy/tests/ui/exit3_compile_flag_test.rs
@@ -0,0 +1,11 @@
+//@ check-pass
+//@compile-flags: --test
+
+#![warn(clippy::exit)]
+
+fn main() {
+    if true {
+        std::process::exit(2);
+    };
+    std::process::exit(1);
+}
diff --git a/src/tools/clippy/tests/ui/exit4.rs b/src/tools/clippy/tests/ui/exit4.rs
new file mode 100644
index 00000000000..821a26fd78b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/exit4.rs
@@ -0,0 +1,8 @@
+//@ check-pass
+//@compile-flags: --test
+
+#![warn(clippy::exit)]
+
+fn main() {
+    std::process::exit(0)
+}
diff --git a/src/tools/clippy/tests/ui/floating_point_mul_add.fixed b/src/tools/clippy/tests/ui/floating_point_mul_add.fixed
index 83aeddb2a1f..884bae00432 100644
--- a/src/tools/clippy/tests/ui/floating_point_mul_add.fixed
+++ b/src/tools/clippy/tests/ui/floating_point_mul_add.fixed
@@ -69,3 +69,47 @@ fn _issue11831() {
 
     let _ = a + b * c;
 }
+
+fn _issue14897() {
+    let x = 1.0;
+    let _ = x * 2.0 + 0.5; // should not suggest mul_add
+    let _ = 0.5 + x * 2.0; // should not suggest mul_add
+    let _ = 0.5 + x * 1.2; // should not suggest mul_add
+    let _ = 1.2 + x * 1.2; // should not suggest mul_add
+
+    let x = -1.0;
+    let _ = 0.5 + x * 1.2; // should not suggest mul_add
+
+    let x = { 4.0 };
+    let _ = 0.5 + x * 1.2; // should not suggest mul_add
+
+    let x = if 1 > 2 { 1.0 } else { 2.0 };
+    let _ = 0.5 + x * 1.2; // should not suggest mul_add
+
+    let x = 2.4 + 1.2;
+    let _ = 0.5 + x * 1.2; // should not suggest mul_add
+
+    let f = || 4.0;
+    let x = f();
+    let _ = 0.5 + f() * 1.2; // should not suggest mul_add
+    let _ = 0.5 + x * 1.2; // should not suggest mul_add
+
+    let x = 0.1;
+    let y = x;
+    let z = y;
+    let _ = 0.5 + z * 1.2; // should not suggest mul_add
+
+    let _ = 2.0f64.mul_add(x, 0.5);
+    //~^ suboptimal_flops
+    let _ = 2.0f64.mul_add(x, 0.5);
+    //~^ suboptimal_flops
+
+    let _ = 2.0f64.mul_add(4.0, x);
+    //~^ suboptimal_flops
+
+    let y: f64 = 1.0;
+    let _ = y.mul_add(2.0, 0.5);
+    //~^ suboptimal_flops
+    let _ = 1.0f64.mul_add(2.0, 0.5);
+    //~^ suboptimal_flops
+}
diff --git a/src/tools/clippy/tests/ui/floating_point_mul_add.rs b/src/tools/clippy/tests/ui/floating_point_mul_add.rs
index 039ee8d053f..9ceb2ec9606 100644
--- a/src/tools/clippy/tests/ui/floating_point_mul_add.rs
+++ b/src/tools/clippy/tests/ui/floating_point_mul_add.rs
@@ -69,3 +69,47 @@ fn _issue11831() {
 
     let _ = a + b * c;
 }
+
+fn _issue14897() {
+    let x = 1.0;
+    let _ = x * 2.0 + 0.5; // should not suggest mul_add
+    let _ = 0.5 + x * 2.0; // should not suggest mul_add
+    let _ = 0.5 + x * 1.2; // should not suggest mul_add
+    let _ = 1.2 + x * 1.2; // should not suggest mul_add
+
+    let x = -1.0;
+    let _ = 0.5 + x * 1.2; // should not suggest mul_add
+
+    let x = { 4.0 };
+    let _ = 0.5 + x * 1.2; // should not suggest mul_add
+
+    let x = if 1 > 2 { 1.0 } else { 2.0 };
+    let _ = 0.5 + x * 1.2; // should not suggest mul_add
+
+    let x = 2.4 + 1.2;
+    let _ = 0.5 + x * 1.2; // should not suggest mul_add
+
+    let f = || 4.0;
+    let x = f();
+    let _ = 0.5 + f() * 1.2; // should not suggest mul_add
+    let _ = 0.5 + x * 1.2; // should not suggest mul_add
+
+    let x = 0.1;
+    let y = x;
+    let z = y;
+    let _ = 0.5 + z * 1.2; // should not suggest mul_add
+
+    let _ = 0.5 + 2.0 * x;
+    //~^ suboptimal_flops
+    let _ = 2.0 * x + 0.5;
+    //~^ suboptimal_flops
+
+    let _ = x + 2.0 * 4.0;
+    //~^ suboptimal_flops
+
+    let y: f64 = 1.0;
+    let _ = y * 2.0 + 0.5;
+    //~^ suboptimal_flops
+    let _ = 1.0 * 2.0 + 0.5;
+    //~^ suboptimal_flops
+}
diff --git a/src/tools/clippy/tests/ui/floating_point_mul_add.stderr b/src/tools/clippy/tests/ui/floating_point_mul_add.stderr
index 6482127bcc0..dad65ddf2ec 100644
--- a/src/tools/clippy/tests/ui/floating_point_mul_add.stderr
+++ b/src/tools/clippy/tests/ui/floating_point_mul_add.stderr
@@ -79,5 +79,35 @@ error: multiply and add expressions can be calculated more efficiently and accur
 LL |     let _ = a - (b * u as f64);
    |             ^^^^^^^^^^^^^^^^^^ help: consider using: `b.mul_add(-(u as f64), a)`
 
-error: aborting due to 13 previous errors
+error: multiply and add expressions can be calculated more efficiently and accurately
+  --> tests/ui/floating_point_mul_add.rs:102:13
+   |
+LL |     let _ = 0.5 + 2.0 * x;
+   |             ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(x, 0.5)`
+
+error: multiply and add expressions can be calculated more efficiently and accurately
+  --> tests/ui/floating_point_mul_add.rs:104:13
+   |
+LL |     let _ = 2.0 * x + 0.5;
+   |             ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(x, 0.5)`
+
+error: multiply and add expressions can be calculated more efficiently and accurately
+  --> tests/ui/floating_point_mul_add.rs:107:13
+   |
+LL |     let _ = x + 2.0 * 4.0;
+   |             ^^^^^^^^^^^^^ help: consider using: `2.0f64.mul_add(4.0, x)`
+
+error: multiply and add expressions can be calculated more efficiently and accurately
+  --> tests/ui/floating_point_mul_add.rs:111:13
+   |
+LL |     let _ = y * 2.0 + 0.5;
+   |             ^^^^^^^^^^^^^ help: consider using: `y.mul_add(2.0, 0.5)`
+
+error: multiply and add expressions can be calculated more efficiently and accurately
+  --> tests/ui/floating_point_mul_add.rs:113:13
+   |
+LL |     let _ = 1.0 * 2.0 + 0.5;
+   |             ^^^^^^^^^^^^^^^ help: consider using: `1.0f64.mul_add(2.0, 0.5)`
+
+error: aborting due to 18 previous errors
 
diff --git a/src/tools/clippy/tests/ui/manual_is_variant_and.fixed b/src/tools/clippy/tests/ui/manual_is_variant_and.fixed
index 6425f32c09c..65a9cfa6e64 100644
--- a/src/tools/clippy/tests/ui/manual_is_variant_and.fixed
+++ b/src/tools/clippy/tests/ui/manual_is_variant_and.fixed
@@ -61,7 +61,7 @@ fn option_methods() {
 
     let _ = Some(2).is_some_and(|x| x % 2 == 0);
     //~^ manual_is_variant_and
-    let _ = Some(2).is_none_or(|x| x % 2 == 0);
+    let _ = Some(2).is_none_or(|x| x % 2 != 0);
     //~^ manual_is_variant_and
     let _ = Some(2).is_some_and(|x| x % 2 == 0);
     //~^ manual_is_variant_and
@@ -116,3 +116,113 @@ fn main() {
     option_methods();
     result_methods();
 }
+
+fn issue15202() {
+    let xs = [None, Some(b'_'), Some(b'1')];
+    for x in xs {
+        let a1 = x.is_none_or(|b| !b.is_ascii_digit());
+        //~^ manual_is_variant_and
+        let a2 = x.is_none_or(|b| !b.is_ascii_digit());
+        assert_eq!(a1, a2);
+    }
+
+    for x in xs {
+        let a1 = x.is_none_or(|b| b.is_ascii_digit());
+        //~^ manual_is_variant_and
+        let a2 = x.is_none_or(|b| b.is_ascii_digit());
+        assert_eq!(a1, a2);
+    }
+
+    for x in xs {
+        let a1 = x.is_some_and(|b| b.is_ascii_digit());
+        //~^ manual_is_variant_and
+        let a2 = x.is_some_and(|b| b.is_ascii_digit());
+        assert_eq!(a1, a2);
+    }
+
+    for x in xs {
+        let a1 = x.is_some_and(|b| !b.is_ascii_digit());
+        //~^ manual_is_variant_and
+        let a2 = x.is_some_and(|b| !b.is_ascii_digit());
+        assert_eq!(a1, a2);
+    }
+
+    let xs = [Err("foo"), Ok(b'_'), Ok(b'1')];
+    for x in xs {
+        let a1 = !x.is_ok_and(|b| b.is_ascii_digit());
+        //~^ manual_is_variant_and
+        let a2 = !x.is_ok_and(|b| b.is_ascii_digit());
+        assert_eq!(a1, a2);
+    }
+
+    for x in xs {
+        let a1 = !x.is_ok_and(|b| !b.is_ascii_digit());
+        //~^ manual_is_variant_and
+        let a2 = !x.is_ok_and(|b| !b.is_ascii_digit());
+        assert_eq!(a1, a2);
+    }
+
+    for x in xs {
+        let a1 = x.is_ok_and(|b| b.is_ascii_digit());
+        //~^ manual_is_variant_and
+        let a2 = x.is_ok_and(|b| b.is_ascii_digit());
+        assert_eq!(a1, a2);
+    }
+
+    for x in xs {
+        let a1 = x.is_ok_and(|b| !b.is_ascii_digit());
+        //~^ manual_is_variant_and
+        let a2 = x.is_ok_and(|b| !b.is_ascii_digit());
+        assert_eq!(a1, a2);
+    }
+}
+
+mod with_func {
+    fn iad(b: u8) -> bool {
+        b.is_ascii_digit()
+    }
+
+    fn check_option(b: Option<u8>) {
+        let a1 = b.is_some_and(iad);
+        //~^ manual_is_variant_and
+        let a2 = b.is_some_and(iad);
+        assert_eq!(a1, a2);
+
+        let a1 = b.is_some_and(|x| !iad(x));
+        //~^ manual_is_variant_and
+        let a2 = b.is_some_and(|x| !iad(x));
+        assert_eq!(a1, a2);
+
+        let a1 = b.is_none_or(|x| !iad(x));
+        //~^ manual_is_variant_and
+        let a2 = b.is_none_or(|x| !iad(x));
+        assert_eq!(a1, a2);
+
+        let a1 = b.is_none_or(iad);
+        //~^ manual_is_variant_and
+        let a2 = b.is_none_or(iad);
+        assert_eq!(a1, a2);
+    }
+
+    fn check_result(b: Result<u8, ()>) {
+        let a1 = b.is_ok_and(iad);
+        //~^ manual_is_variant_and
+        let a2 = b.is_ok_and(iad);
+        assert_eq!(a1, a2);
+
+        let a1 = b.is_ok_and(|x| !iad(x));
+        //~^ manual_is_variant_and
+        let a2 = b.is_ok_and(|x| !iad(x));
+        assert_eq!(a1, a2);
+
+        let a1 = !b.is_ok_and(iad);
+        //~^ manual_is_variant_and
+        let a2 = !b.is_ok_and(iad);
+        assert_eq!(a1, a2);
+
+        let a1 = !b.is_ok_and(|x| !iad(x));
+        //~^ manual_is_variant_and
+        let a2 = !b.is_ok_and(|x| !iad(x));
+        assert_eq!(a1, a2);
+    }
+}
diff --git a/src/tools/clippy/tests/ui/manual_is_variant_and.rs b/src/tools/clippy/tests/ui/manual_is_variant_and.rs
index e069e97a04d..85b45d654a7 100644
--- a/src/tools/clippy/tests/ui/manual_is_variant_and.rs
+++ b/src/tools/clippy/tests/ui/manual_is_variant_and.rs
@@ -125,3 +125,113 @@ fn main() {
     option_methods();
     result_methods();
 }
+
+fn issue15202() {
+    let xs = [None, Some(b'_'), Some(b'1')];
+    for x in xs {
+        let a1 = x.map(|b| b.is_ascii_digit()) != Some(true);
+        //~^ manual_is_variant_and
+        let a2 = x.is_none_or(|b| !b.is_ascii_digit());
+        assert_eq!(a1, a2);
+    }
+
+    for x in xs {
+        let a1 = x.map(|b| b.is_ascii_digit()) != Some(false);
+        //~^ manual_is_variant_and
+        let a2 = x.is_none_or(|b| b.is_ascii_digit());
+        assert_eq!(a1, a2);
+    }
+
+    for x in xs {
+        let a1 = x.map(|b| b.is_ascii_digit()) == Some(true);
+        //~^ manual_is_variant_and
+        let a2 = x.is_some_and(|b| b.is_ascii_digit());
+        assert_eq!(a1, a2);
+    }
+
+    for x in xs {
+        let a1 = x.map(|b| b.is_ascii_digit()) == Some(false);
+        //~^ manual_is_variant_and
+        let a2 = x.is_some_and(|b| !b.is_ascii_digit());
+        assert_eq!(a1, a2);
+    }
+
+    let xs = [Err("foo"), Ok(b'_'), Ok(b'1')];
+    for x in xs {
+        let a1 = x.map(|b| b.is_ascii_digit()) != Ok(true);
+        //~^ manual_is_variant_and
+        let a2 = !x.is_ok_and(|b| b.is_ascii_digit());
+        assert_eq!(a1, a2);
+    }
+
+    for x in xs {
+        let a1 = x.map(|b| b.is_ascii_digit()) != Ok(false);
+        //~^ manual_is_variant_and
+        let a2 = !x.is_ok_and(|b| !b.is_ascii_digit());
+        assert_eq!(a1, a2);
+    }
+
+    for x in xs {
+        let a1 = x.map(|b| b.is_ascii_digit()) == Ok(true);
+        //~^ manual_is_variant_and
+        let a2 = x.is_ok_and(|b| b.is_ascii_digit());
+        assert_eq!(a1, a2);
+    }
+
+    for x in xs {
+        let a1 = x.map(|b| b.is_ascii_digit()) == Ok(false);
+        //~^ manual_is_variant_and
+        let a2 = x.is_ok_and(|b| !b.is_ascii_digit());
+        assert_eq!(a1, a2);
+    }
+}
+
+mod with_func {
+    fn iad(b: u8) -> bool {
+        b.is_ascii_digit()
+    }
+
+    fn check_option(b: Option<u8>) {
+        let a1 = b.map(iad) == Some(true);
+        //~^ manual_is_variant_and
+        let a2 = b.is_some_and(iad);
+        assert_eq!(a1, a2);
+
+        let a1 = b.map(iad) == Some(false);
+        //~^ manual_is_variant_and
+        let a2 = b.is_some_and(|x| !iad(x));
+        assert_eq!(a1, a2);
+
+        let a1 = b.map(iad) != Some(true);
+        //~^ manual_is_variant_and
+        let a2 = b.is_none_or(|x| !iad(x));
+        assert_eq!(a1, a2);
+
+        let a1 = b.map(iad) != Some(false);
+        //~^ manual_is_variant_and
+        let a2 = b.is_none_or(iad);
+        assert_eq!(a1, a2);
+    }
+
+    fn check_result(b: Result<u8, ()>) {
+        let a1 = b.map(iad) == Ok(true);
+        //~^ manual_is_variant_and
+        let a2 = b.is_ok_and(iad);
+        assert_eq!(a1, a2);
+
+        let a1 = b.map(iad) == Ok(false);
+        //~^ manual_is_variant_and
+        let a2 = b.is_ok_and(|x| !iad(x));
+        assert_eq!(a1, a2);
+
+        let a1 = b.map(iad) != Ok(true);
+        //~^ manual_is_variant_and
+        let a2 = !b.is_ok_and(iad);
+        assert_eq!(a1, a2);
+
+        let a1 = b.map(iad) != Ok(false);
+        //~^ manual_is_variant_and
+        let a2 = !b.is_ok_and(|x| !iad(x));
+        assert_eq!(a1, a2);
+    }
+}
diff --git a/src/tools/clippy/tests/ui/manual_is_variant_and.stderr b/src/tools/clippy/tests/ui/manual_is_variant_and.stderr
index f770319a268..da36b5a07d2 100644
--- a/src/tools/clippy/tests/ui/manual_is_variant_and.stderr
+++ b/src/tools/clippy/tests/ui/manual_is_variant_and.stderr
@@ -54,7 +54,7 @@ error: called `.map() != Some()`
   --> tests/ui/manual_is_variant_and.rs:70:13
    |
 LL |     let _ = Some(2).map(|x| x % 2 == 0) != Some(true);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `Some(2).is_none_or(|x| x % 2 == 0)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `Some(2).is_none_or(|x| x % 2 != 0)`
 
 error: called `.map() == Some()`
   --> tests/ui/manual_is_variant_and.rs:72:13
@@ -126,5 +126,101 @@ error: called `map(<f>).unwrap_or_default()` on a `Result` value
 LL |     let _ = res2.map(char::is_alphanumeric).unwrap_or_default(); // should lint
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `is_ok_and(char::is_alphanumeric)`
 
-error: aborting due to 15 previous errors
+error: called `.map() != Some()`
+  --> tests/ui/manual_is_variant_and.rs:132:18
+   |
+LL |         let a1 = x.map(|b| b.is_ascii_digit()) != Some(true);
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.is_none_or(|b| !b.is_ascii_digit())`
+
+error: called `.map() != Some()`
+  --> tests/ui/manual_is_variant_and.rs:139:18
+   |
+LL |         let a1 = x.map(|b| b.is_ascii_digit()) != Some(false);
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.is_none_or(|b| b.is_ascii_digit())`
+
+error: called `.map() == Some()`
+  --> tests/ui/manual_is_variant_and.rs:146:18
+   |
+LL |         let a1 = x.map(|b| b.is_ascii_digit()) == Some(true);
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.is_some_and(|b| b.is_ascii_digit())`
+
+error: called `.map() == Some()`
+  --> tests/ui/manual_is_variant_and.rs:153:18
+   |
+LL |         let a1 = x.map(|b| b.is_ascii_digit()) == Some(false);
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.is_some_and(|b| !b.is_ascii_digit())`
+
+error: called `.map() != Ok()`
+  --> tests/ui/manual_is_variant_and.rs:161:18
+   |
+LL |         let a1 = x.map(|b| b.is_ascii_digit()) != Ok(true);
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!x.is_ok_and(|b| b.is_ascii_digit())`
+
+error: called `.map() != Ok()`
+  --> tests/ui/manual_is_variant_and.rs:168:18
+   |
+LL |         let a1 = x.map(|b| b.is_ascii_digit()) != Ok(false);
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!x.is_ok_and(|b| !b.is_ascii_digit())`
+
+error: called `.map() == Ok()`
+  --> tests/ui/manual_is_variant_and.rs:175:18
+   |
+LL |         let a1 = x.map(|b| b.is_ascii_digit()) == Ok(true);
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.is_ok_and(|b| b.is_ascii_digit())`
+
+error: called `.map() == Ok()`
+  --> tests/ui/manual_is_variant_and.rs:182:18
+   |
+LL |         let a1 = x.map(|b| b.is_ascii_digit()) == Ok(false);
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `x.is_ok_and(|b| !b.is_ascii_digit())`
+
+error: called `.map() == Some()`
+  --> tests/ui/manual_is_variant_and.rs:195:18
+   |
+LL |         let a1 = b.map(iad) == Some(true);
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.is_some_and(iad)`
+
+error: called `.map() == Some()`
+  --> tests/ui/manual_is_variant_and.rs:200:18
+   |
+LL |         let a1 = b.map(iad) == Some(false);
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.is_some_and(|x| !iad(x))`
+
+error: called `.map() != Some()`
+  --> tests/ui/manual_is_variant_and.rs:205:18
+   |
+LL |         let a1 = b.map(iad) != Some(true);
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.is_none_or(|x| !iad(x))`
+
+error: called `.map() != Some()`
+  --> tests/ui/manual_is_variant_and.rs:210:18
+   |
+LL |         let a1 = b.map(iad) != Some(false);
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.is_none_or(iad)`
+
+error: called `.map() == Ok()`
+  --> tests/ui/manual_is_variant_and.rs:217:18
+   |
+LL |         let a1 = b.map(iad) == Ok(true);
+   |                  ^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.is_ok_and(iad)`
+
+error: called `.map() == Ok()`
+  --> tests/ui/manual_is_variant_and.rs:222:18
+   |
+LL |         let a1 = b.map(iad) == Ok(false);
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^ help: use: `b.is_ok_and(|x| !iad(x))`
+
+error: called `.map() != Ok()`
+  --> tests/ui/manual_is_variant_and.rs:227:18
+   |
+LL |         let a1 = b.map(iad) != Ok(true);
+   |                  ^^^^^^^^^^^^^^^^^^^^^^ help: use: `!b.is_ok_and(iad)`
+
+error: called `.map() != Ok()`
+  --> tests/ui/manual_is_variant_and.rs:232:18
+   |
+LL |         let a1 = b.map(iad) != Ok(false);
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^ help: use: `!b.is_ok_and(|x| !iad(x))`
+
+error: aborting due to 31 previous errors
 
diff --git a/src/tools/clippy/tests/ui/manual_let_else_match.fixed b/src/tools/clippy/tests/ui/manual_let_else_match.fixed
index 588ba5edd8f..15f604aec29 100644
--- a/src/tools/clippy/tests/ui/manual_let_else_match.fixed
+++ b/src/tools/clippy/tests/ui/manual_let_else_match.fixed
@@ -137,3 +137,48 @@ fn not_fire() {
 fn issue11579() {
     let Some(msg) = Some("hi") else { unreachable!("can't happen") };
 }
+
+#[derive(Clone, Copy)]
+struct Issue9939<T> {
+    avalanche: T,
+}
+
+fn issue9939() {
+    let issue = Some(Issue9939 { avalanche: 1 });
+    let Some(Issue9939 { avalanche: tornado }) = issue else { unreachable!("can't happen") };
+    let issue = Some(Issue9939 { avalanche: true });
+    let Some(Issue9939 { avalanche: acid_rain }) = issue else { unreachable!("can't happen") };
+    assert_eq!(tornado, 1);
+    assert!(acid_rain);
+
+    // without shadowing
+    let _x @ Some(Issue9939 { avalanche: _y }) = issue else { unreachable!("can't happen") };
+
+    // with shadowing
+    let Some(Issue9939 { avalanche: _x }) = issue else { unreachable!("can't happen") };
+}
+
+#[derive(Clone, Copy)]
+struct Issue9939b<T, U> {
+    earthquake: T,
+    hurricane: U,
+}
+
+fn issue9939b() {
+    let issue = Some(Issue9939b {
+        earthquake: true,
+        hurricane: 1,
+    });
+    let issue @ Some(Issue9939b { earthquake: flood, hurricane: drought }) = issue else { unreachable!("can't happen") };
+    assert_eq!(drought, 1);
+    assert!(flood);
+    assert!(issue.is_some());
+
+    // without shadowing
+    let _x @ Some(Issue9939b { earthquake: erosion, hurricane: _y }) = issue else { unreachable!("can't happen") };
+    assert!(erosion);
+
+    // with shadowing
+    let Some(Issue9939b { earthquake: erosion, hurricane: _x }) = issue else { unreachable!("can't happen") };
+    assert!(erosion);
+}
diff --git a/src/tools/clippy/tests/ui/manual_let_else_match.rs b/src/tools/clippy/tests/ui/manual_let_else_match.rs
index 6416753bac1..44a044b142b 100644
--- a/src/tools/clippy/tests/ui/manual_let_else_match.rs
+++ b/src/tools/clippy/tests/ui/manual_let_else_match.rs
@@ -177,3 +177,76 @@ fn issue11579() {
         _ => unreachable!("can't happen"),
     };
 }
+
+#[derive(Clone, Copy)]
+struct Issue9939<T> {
+    avalanche: T,
+}
+
+fn issue9939() {
+    let issue = Some(Issue9939 { avalanche: 1 });
+    let tornado = match issue {
+        //~^ manual_let_else
+        Some(Issue9939 { avalanche }) => avalanche,
+        _ => unreachable!("can't happen"),
+    };
+    let issue = Some(Issue9939 { avalanche: true });
+    let acid_rain = match issue {
+        //~^ manual_let_else
+        Some(Issue9939 { avalanche: tornado }) => tornado,
+        _ => unreachable!("can't happen"),
+    };
+    assert_eq!(tornado, 1);
+    assert!(acid_rain);
+
+    // without shadowing
+    let _y = match issue {
+        //~^ manual_let_else
+        _x @ Some(Issue9939 { avalanche }) => avalanche,
+        None => unreachable!("can't happen"),
+    };
+
+    // with shadowing
+    let _x = match issue {
+        //~^ manual_let_else
+        _x @ Some(Issue9939 { avalanche }) => avalanche,
+        None => unreachable!("can't happen"),
+    };
+}
+
+#[derive(Clone, Copy)]
+struct Issue9939b<T, U> {
+    earthquake: T,
+    hurricane: U,
+}
+
+fn issue9939b() {
+    let issue = Some(Issue9939b {
+        earthquake: true,
+        hurricane: 1,
+    });
+    let (issue, drought, flood) = match issue {
+        //~^ manual_let_else
+        flood @ Some(Issue9939b { earthquake, hurricane }) => (flood, hurricane, earthquake),
+        None => unreachable!("can't happen"),
+    };
+    assert_eq!(drought, 1);
+    assert!(flood);
+    assert!(issue.is_some());
+
+    // without shadowing
+    let (_y, erosion) = match issue {
+        //~^ manual_let_else
+        _x @ Some(Issue9939b { earthquake, hurricane }) => (hurricane, earthquake),
+        None => unreachable!("can't happen"),
+    };
+    assert!(erosion);
+
+    // with shadowing
+    let (_x, erosion) = match issue {
+        //~^ manual_let_else
+        _x @ Some(Issue9939b { earthquake, hurricane }) => (hurricane, earthquake),
+        None => unreachable!("can't happen"),
+    };
+    assert!(erosion);
+}
diff --git a/src/tools/clippy/tests/ui/manual_let_else_match.stderr b/src/tools/clippy/tests/ui/manual_let_else_match.stderr
index 393562c629b..ed6117ebffb 100644
--- a/src/tools/clippy/tests/ui/manual_let_else_match.stderr
+++ b/src/tools/clippy/tests/ui/manual_let_else_match.stderr
@@ -101,5 +101,75 @@ LL | |         _ => unreachable!("can't happen"),
 LL | |     };
    | |______^ help: consider writing: `let Some(msg) = Some("hi") else { unreachable!("can't happen") };`
 
-error: aborting due to 10 previous errors
+error: this could be rewritten as `let...else`
+  --> tests/ui/manual_let_else_match.rs:188:5
+   |
+LL | /     let tornado = match issue {
+LL | |
+LL | |         Some(Issue9939 { avalanche }) => avalanche,
+LL | |         _ => unreachable!("can't happen"),
+LL | |     };
+   | |______^ help: consider writing: `let Some(Issue9939 { avalanche: tornado }) = issue else { unreachable!("can't happen") };`
+
+error: this could be rewritten as `let...else`
+  --> tests/ui/manual_let_else_match.rs:194:5
+   |
+LL | /     let acid_rain = match issue {
+LL | |
+LL | |         Some(Issue9939 { avalanche: tornado }) => tornado,
+LL | |         _ => unreachable!("can't happen"),
+LL | |     };
+   | |______^ help: consider writing: `let Some(Issue9939 { avalanche: acid_rain }) = issue else { unreachable!("can't happen") };`
+
+error: this could be rewritten as `let...else`
+  --> tests/ui/manual_let_else_match.rs:203:5
+   |
+LL | /     let _y = match issue {
+LL | |
+LL | |         _x @ Some(Issue9939 { avalanche }) => avalanche,
+LL | |         None => unreachable!("can't happen"),
+LL | |     };
+   | |______^ help: consider writing: `let _x @ Some(Issue9939 { avalanche: _y }) = issue else { unreachable!("can't happen") };`
+
+error: this could be rewritten as `let...else`
+  --> tests/ui/manual_let_else_match.rs:210:5
+   |
+LL | /     let _x = match issue {
+LL | |
+LL | |         _x @ Some(Issue9939 { avalanche }) => avalanche,
+LL | |         None => unreachable!("can't happen"),
+LL | |     };
+   | |______^ help: consider writing: `let Some(Issue9939 { avalanche: _x }) = issue else { unreachable!("can't happen") };`
+
+error: this could be rewritten as `let...else`
+  --> tests/ui/manual_let_else_match.rs:228:5
+   |
+LL | /     let (issue, drought, flood) = match issue {
+LL | |
+LL | |         flood @ Some(Issue9939b { earthquake, hurricane }) => (flood, hurricane, earthquake),
+LL | |         None => unreachable!("can't happen"),
+LL | |     };
+   | |______^ help: consider writing: `let issue @ Some(Issue9939b { earthquake: flood, hurricane: drought }) = issue else { unreachable!("can't happen") };`
+
+error: this could be rewritten as `let...else`
+  --> tests/ui/manual_let_else_match.rs:238:5
+   |
+LL | /     let (_y, erosion) = match issue {
+LL | |
+LL | |         _x @ Some(Issue9939b { earthquake, hurricane }) => (hurricane, earthquake),
+LL | |         None => unreachable!("can't happen"),
+LL | |     };
+   | |______^ help: consider writing: `let _x @ Some(Issue9939b { earthquake: erosion, hurricane: _y }) = issue else { unreachable!("can't happen") };`
+
+error: this could be rewritten as `let...else`
+  --> tests/ui/manual_let_else_match.rs:246:5
+   |
+LL | /     let (_x, erosion) = match issue {
+LL | |
+LL | |         _x @ Some(Issue9939b { earthquake, hurricane }) => (hurricane, earthquake),
+LL | |         None => unreachable!("can't happen"),
+LL | |     };
+   | |______^ help: consider writing: `let Some(Issue9939b { earthquake: erosion, hurricane: _x }) = issue else { unreachable!("can't happen") };`
+
+error: aborting due to 17 previous errors
 
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.fixed b/src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.fixed
index f1d5579a723..c113c1caaa6 100644
--- a/src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.fixed
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.fixed
@@ -3,8 +3,7 @@
 
 // Reduced test case from https://github.com/rust-lang/rust-clippy/issues/14658
 
-#[const_trait]
-trait ConstTrait {
+const trait ConstTrait {
     fn method(self);
 }
 
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.rs
index d495759526d..69248bc52d5 100644
--- a/src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.rs
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.rs
@@ -3,8 +3,7 @@
 
 // Reduced test case from https://github.com/rust-lang/rust-clippy/issues/14658
 
-#[const_trait]
-trait ConstTrait {
+const trait ConstTrait {
     fn method(self);
 }
 
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.stderr b/src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.stderr
index b994b88fac6..7ea009cfc9b 100644
--- a/src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.stderr
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/const_trait.stderr
@@ -1,5 +1,5 @@
 error: this could be a `const fn`
-  --> tests/ui/missing_const_for_fn/const_trait.rs:24:1
+  --> tests/ui/missing_const_for_fn/const_trait.rs:23:1
    |
 LL | / fn can_be_const() {
 LL | |     0u64.method();
diff --git a/src/tools/clippy/tests/ui/missing_panics_doc.rs b/src/tools/clippy/tests/ui/missing_panics_doc.rs
index ffdae8504f7..d016e099e30 100644
--- a/src/tools/clippy/tests/ui/missing_panics_doc.rs
+++ b/src/tools/clippy/tests/ui/missing_panics_doc.rs
@@ -250,3 +250,31 @@ pub fn issue_12760<const N: usize>() {
         }
     }
 }
+
+/// This needs documenting
+pub fn unwrap_expect_etc_in_const() {
+    let a = const { std::num::NonZeroUsize::new(1).unwrap() };
+    // This should still pass the lint even if it is guaranteed to panic at compile-time
+    let b = const { std::num::NonZeroUsize::new(0).unwrap() };
+}
+
+/// This needs documenting
+pub const fn unwrap_expect_etc_in_const_fn_fails() {
+    //~^ missing_panics_doc
+    let a = std::num::NonZeroUsize::new(1).unwrap();
+}
+
+/// This needs documenting
+pub const fn assert_in_const_fn_fails() {
+    //~^ missing_panics_doc
+    let x = 0;
+    if x == 0 {
+        panic!();
+    }
+}
+
+/// This needs documenting
+pub const fn in_const_fn<const N: usize>(n: usize) {
+    //~^ missing_panics_doc
+    assert!(N > n);
+}
diff --git a/src/tools/clippy/tests/ui/missing_panics_doc.stderr b/src/tools/clippy/tests/ui/missing_panics_doc.stderr
index 7f0acf8de9b..85a00914427 100644
--- a/src/tools/clippy/tests/ui/missing_panics_doc.stderr
+++ b/src/tools/clippy/tests/ui/missing_panics_doc.stderr
@@ -180,5 +180,41 @@ note: first possible panic found here
 LL |         *v.last().expect("passed an empty thing")
    |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 15 previous errors
+error: docs for function which may panic missing `# Panics` section
+  --> tests/ui/missing_panics_doc.rs:262:1
+   |
+LL | pub const fn unwrap_expect_etc_in_const_fn_fails() {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: first possible panic found here
+  --> tests/ui/missing_panics_doc.rs:264:13
+   |
+LL |     let a = std::num::NonZeroUsize::new(1).unwrap();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: docs for function which may panic missing `# Panics` section
+  --> tests/ui/missing_panics_doc.rs:268:1
+   |
+LL | pub const fn assert_in_const_fn_fails() {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: first possible panic found here
+  --> tests/ui/missing_panics_doc.rs:272:9
+   |
+LL |         panic!();
+   |         ^^^^^^^^
+
+error: docs for function which may panic missing `# Panics` section
+  --> tests/ui/missing_panics_doc.rs:277:1
+   |
+LL | pub const fn in_const_fn<const N: usize>(n: usize) {
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: first possible panic found here
+  --> tests/ui/missing_panics_doc.rs:279:5
+   |
+LL |     assert!(N > n);
+   |     ^^^^^^^^^^^^^^
+
+error: aborting due to 18 previous errors
 
diff --git a/src/tools/clippy/tests/ui/needless_bool_assign.fixed b/src/tools/clippy/tests/ui/needless_bool_assign.fixed
index e0c717ecda2..d6fab4c51b5 100644
--- a/src/tools/clippy/tests/ui/needless_bool_assign.fixed
+++ b/src/tools/clippy/tests/ui/needless_bool_assign.fixed
@@ -33,3 +33,12 @@ fn main() {
         b = true;
     }
 }
+
+fn issue15063(x: bool, y: bool) {
+    let mut z = false;
+
+    if x && y {
+        todo!()
+    } else { z = x || y; }
+    //~^^^^^ needless_bool_assign
+}
diff --git a/src/tools/clippy/tests/ui/needless_bool_assign.rs b/src/tools/clippy/tests/ui/needless_bool_assign.rs
index 3e4fecefa78..c504f61f4dd 100644
--- a/src/tools/clippy/tests/ui/needless_bool_assign.rs
+++ b/src/tools/clippy/tests/ui/needless_bool_assign.rs
@@ -45,3 +45,16 @@ fn main() {
         b = true;
     }
 }
+
+fn issue15063(x: bool, y: bool) {
+    let mut z = false;
+
+    if x && y {
+        todo!()
+    } else if x || y {
+        z = true;
+    } else {
+        z = false;
+    }
+    //~^^^^^ needless_bool_assign
+}
diff --git a/src/tools/clippy/tests/ui/needless_bool_assign.stderr b/src/tools/clippy/tests/ui/needless_bool_assign.stderr
index f33a4bc0c59..1d09b8b25a0 100644
--- a/src/tools/clippy/tests/ui/needless_bool_assign.stderr
+++ b/src/tools/clippy/tests/ui/needless_bool_assign.stderr
@@ -51,5 +51,16 @@ LL | |     }
    = note: `-D clippy::if-same-then-else` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::if_same_then_else)]`
 
-error: aborting due to 4 previous errors
+error: this if-then-else expression assigns a bool literal
+  --> tests/ui/needless_bool_assign.rs:54:12
+   |
+LL |       } else if x || y {
+   |  ____________^
+LL | |         z = true;
+LL | |     } else {
+LL | |         z = false;
+LL | |     }
+   | |_____^ help: you can reduce it to: `{ z = x || y; }`
+
+error: aborting due to 5 previous errors
 
diff --git a/src/tools/clippy/tests/ui/neg_multiply.fixed b/src/tools/clippy/tests/ui/neg_multiply.fixed
index ff6e08300e2..32d466e88fc 100644
--- a/src/tools/clippy/tests/ui/neg_multiply.fixed
+++ b/src/tools/clippy/tests/ui/neg_multiply.fixed
@@ -82,3 +82,15 @@ fn float() {
 
     -1.0 * -1.0; // should be ok
 }
+
+struct Y {
+    delta: f64,
+}
+
+fn nested() {
+    let a = Y { delta: 1.0 };
+    let b = Y { delta: 1.0 };
+    let _ = (-(a.delta - 0.5).abs()).total_cmp(&1.0);
+    //~^ neg_multiply
+    let _ = (-(a.delta - 0.5).abs()).total_cmp(&1.0);
+}
diff --git a/src/tools/clippy/tests/ui/neg_multiply.rs b/src/tools/clippy/tests/ui/neg_multiply.rs
index b0f4e85c78e..241a72c6d99 100644
--- a/src/tools/clippy/tests/ui/neg_multiply.rs
+++ b/src/tools/clippy/tests/ui/neg_multiply.rs
@@ -82,3 +82,15 @@ fn float() {
 
     -1.0 * -1.0; // should be ok
 }
+
+struct Y {
+    delta: f64,
+}
+
+fn nested() {
+    let a = Y { delta: 1.0 };
+    let b = Y { delta: 1.0 };
+    let _ = ((a.delta - 0.5).abs() * -1.0).total_cmp(&1.0);
+    //~^ neg_multiply
+    let _ = (-(a.delta - 0.5).abs()).total_cmp(&1.0);
+}
diff --git a/src/tools/clippy/tests/ui/neg_multiply.stderr b/src/tools/clippy/tests/ui/neg_multiply.stderr
index 2ef7e32ce05..f4fb6d3ce54 100644
--- a/src/tools/clippy/tests/ui/neg_multiply.stderr
+++ b/src/tools/clippy/tests/ui/neg_multiply.stderr
@@ -97,5 +97,11 @@ error: this multiplication by -1 can be written more succinctly
 LL |     (3.0_f32 as f64) * -1.0;
    |     ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `-(3.0_f32 as f64)`
 
-error: aborting due to 16 previous errors
+error: this multiplication by -1 can be written more succinctly
+  --> tests/ui/neg_multiply.rs:93:13
+   |
+LL |     let _ = ((a.delta - 0.5).abs() * -1.0).total_cmp(&1.0);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using: `(-(a.delta - 0.5).abs())`
+
+error: aborting due to 17 previous errors
 
diff --git a/src/tools/clippy/tests/ui/op_ref.fixed b/src/tools/clippy/tests/ui/op_ref.fixed
index f412190b9fd..4bf4b91888c 100644
--- a/src/tools/clippy/tests/ui/op_ref.fixed
+++ b/src/tools/clippy/tests/ui/op_ref.fixed
@@ -110,3 +110,37 @@ mod issue_2597 {
         &array[idx] < val
     }
 }
+
+#[allow(clippy::needless_if)]
+fn issue15063() {
+    use std::ops::BitAnd;
+
+    macro_rules! mac {
+        ($e:expr) => {
+            $e.clone()
+        };
+    }
+
+    let x = 1;
+    if x == mac!(1) {}
+    //~^ op_ref
+
+    #[derive(Copy, Clone)]
+    struct Y(i32);
+    impl BitAnd for Y {
+        type Output = Y;
+        fn bitand(self, rhs: Y) -> Y {
+            Y(self.0 & rhs.0)
+        }
+    }
+    impl<'a> BitAnd<&'a Y> for Y {
+        type Output = Y;
+        fn bitand(self, rhs: &'a Y) -> Y {
+            Y(self.0 & rhs.0)
+        }
+    }
+    let x = Y(1);
+    let y = Y(2);
+    let z = x & mac!(y);
+    //~^ op_ref
+}
diff --git a/src/tools/clippy/tests/ui/op_ref.rs b/src/tools/clippy/tests/ui/op_ref.rs
index a4bbd86c7e9..9a192661aaf 100644
--- a/src/tools/clippy/tests/ui/op_ref.rs
+++ b/src/tools/clippy/tests/ui/op_ref.rs
@@ -110,3 +110,37 @@ mod issue_2597 {
         &array[idx] < val
     }
 }
+
+#[allow(clippy::needless_if)]
+fn issue15063() {
+    use std::ops::BitAnd;
+
+    macro_rules! mac {
+        ($e:expr) => {
+            $e.clone()
+        };
+    }
+
+    let x = 1;
+    if &x == &mac!(1) {}
+    //~^ op_ref
+
+    #[derive(Copy, Clone)]
+    struct Y(i32);
+    impl BitAnd for Y {
+        type Output = Y;
+        fn bitand(self, rhs: Y) -> Y {
+            Y(self.0 & rhs.0)
+        }
+    }
+    impl<'a> BitAnd<&'a Y> for Y {
+        type Output = Y;
+        fn bitand(self, rhs: &'a Y) -> Y {
+            Y(self.0 & rhs.0)
+        }
+    }
+    let x = Y(1);
+    let y = Y(2);
+    let z = x & &mac!(y);
+    //~^ op_ref
+}
diff --git a/src/tools/clippy/tests/ui/op_ref.stderr b/src/tools/clippy/tests/ui/op_ref.stderr
index 51c2963a9ee..8a58b154c81 100644
--- a/src/tools/clippy/tests/ui/op_ref.stderr
+++ b/src/tools/clippy/tests/ui/op_ref.stderr
@@ -36,5 +36,25 @@ LL |         let _ = two + &three;
    |                       |
    |                       help: use the right value directly: `three`
 
-error: aborting due to 4 previous errors
+error: needlessly taken reference of both operands
+  --> tests/ui/op_ref.rs:125:8
+   |
+LL |     if &x == &mac!(1) {}
+   |        ^^^^^^^^^^^^^^
+   |
+help: use the values directly
+   |
+LL -     if &x == &mac!(1) {}
+LL +     if x == mac!(1) {}
+   |
+
+error: taken reference of right operand
+  --> tests/ui/op_ref.rs:144:13
+   |
+LL |     let z = x & &mac!(y);
+   |             ^^^^--------
+   |                 |
+   |                 help: use the right value directly: `mac!(y)`
+
+error: aborting due to 6 previous errors
 
diff --git a/src/tools/clippy/tests/ui/or_fun_call.fixed b/src/tools/clippy/tests/ui/or_fun_call.fixed
index 34f3e046841..bcd2602edb6 100644
--- a/src/tools/clippy/tests/ui/or_fun_call.fixed
+++ b/src/tools/clippy/tests/ui/or_fun_call.fixed
@@ -439,4 +439,24 @@ fn test_option_get_or_insert() {
     //~^ or_fun_call
 }
 
+fn test_option_and() {
+    // assume that this is slow call
+    fn g() -> Option<u8> {
+        Some(99)
+    }
+    let mut x = Some(42_u8);
+    let _ = x.and_then(|_| g());
+    //~^ or_fun_call
+}
+
+fn test_result_and() {
+    // assume that this is slow call
+    fn g() -> Result<u8, ()> {
+        Ok(99)
+    }
+    let mut x: Result<u8, ()> = Ok(42);
+    let _ = x.and_then(|_| g());
+    //~^ or_fun_call
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/or_fun_call.rs b/src/tools/clippy/tests/ui/or_fun_call.rs
index dc57bd6060a..8d1202ebf91 100644
--- a/src/tools/clippy/tests/ui/or_fun_call.rs
+++ b/src/tools/clippy/tests/ui/or_fun_call.rs
@@ -439,4 +439,24 @@ fn test_option_get_or_insert() {
     //~^ or_fun_call
 }
 
+fn test_option_and() {
+    // assume that this is slow call
+    fn g() -> Option<u8> {
+        Some(99)
+    }
+    let mut x = Some(42_u8);
+    let _ = x.and(g());
+    //~^ or_fun_call
+}
+
+fn test_result_and() {
+    // assume that this is slow call
+    fn g() -> Result<u8, ()> {
+        Ok(99)
+    }
+    let mut x: Result<u8, ()> = Ok(42);
+    let _ = x.and(g());
+    //~^ or_fun_call
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/or_fun_call.stderr b/src/tools/clippy/tests/ui/or_fun_call.stderr
index 0f159fe8bff..585ee2d0e19 100644
--- a/src/tools/clippy/tests/ui/or_fun_call.stderr
+++ b/src/tools/clippy/tests/ui/or_fun_call.stderr
@@ -264,5 +264,17 @@ error: function call inside of `get_or_insert`
 LL |     let _ = x.get_or_insert(g());
    |               ^^^^^^^^^^^^^^^^^^ help: try: `get_or_insert_with(g)`
 
-error: aborting due to 41 previous errors
+error: function call inside of `and`
+  --> tests/ui/or_fun_call.rs:448:15
+   |
+LL |     let _ = x.and(g());
+   |               ^^^^^^^^ help: try: `and_then(|_| g())`
+
+error: function call inside of `and`
+  --> tests/ui/or_fun_call.rs:458:15
+   |
+LL |     let _ = x.and(g());
+   |               ^^^^^^^^ help: try: `and_then(|_| g())`
+
+error: aborting due to 43 previous errors
 
diff --git a/src/tools/clippy/tests/ui/partialeq_ne_impl.stderr b/src/tools/clippy/tests/ui/partialeq_ne_impl.stderr
index dc01a375060..0f700654f7c 100644
--- a/src/tools/clippy/tests/ui/partialeq_ne_impl.stderr
+++ b/src/tools/clippy/tests/ui/partialeq_ne_impl.stderr
@@ -1,12 +1,8 @@
 error: re-implementing `PartialEq::ne` is unnecessary
   --> tests/ui/partialeq_ne_impl.rs:9:5
    |
-LL | /     fn ne(&self, _: &Foo) -> bool {
-LL | |
-LL | |
-LL | |         false
-LL | |     }
-   | |_____^
+LL |     fn ne(&self, _: &Foo) -> bool {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::partialeq-ne-impl` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::partialeq_ne_impl)]`
diff --git a/src/tools/clippy/tests/ui/ptr_arg.rs b/src/tools/clippy/tests/ui/ptr_arg.rs
index 65f3f05d6cb..578641e910d 100644
--- a/src/tools/clippy/tests/ui/ptr_arg.rs
+++ b/src/tools/clippy/tests/ui/ptr_arg.rs
@@ -312,7 +312,7 @@ mod issue_9218 {
 
     // Inferred to be `&'a str`, afaik.
     fn cow_good_ret_ty<'a>(input: &'a Cow<'a, str>) -> &str {
-        //~^ ERROR: lifetime flowing from input to output with different syntax
+        //~^ ERROR: eliding a lifetime that's named elsewhere is confusing
         todo!()
     }
 }
diff --git a/src/tools/clippy/tests/ui/ptr_arg.stderr b/src/tools/clippy/tests/ui/ptr_arg.stderr
index 600343754e1..fd9ceddfe11 100644
--- a/src/tools/clippy/tests/ui/ptr_arg.stderr
+++ b/src/tools/clippy/tests/ui/ptr_arg.stderr
@@ -231,18 +231,19 @@ error: writing `&String` instead of `&str` involves a new object where a slice w
 LL |     fn good(v1: &String, v2: &String) {
    |                              ^^^^^^^ help: change this to: `&str`
 
-error: lifetime flowing from input to output with different syntax can be confusing
+error: eliding a lifetime that's named elsewhere is confusing
   --> tests/ui/ptr_arg.rs:314:36
    |
 LL |     fn cow_good_ret_ty<'a>(input: &'a Cow<'a, str>) -> &str {
-   |                                    ^^     ^^           ---- the lifetime gets resolved as `'a`
+   |                                    ^^     ^^           ---- the same lifetime is elided here
    |                                    |      |
-   |                                    |      these lifetimes flow to the output
-   |                                    these lifetimes flow to the output
+   |                                    |      the lifetime is named here
+   |                                    the lifetime is named here
    |
+   = help: the same lifetime is referred to in inconsistent ways, making the signature confusing
    = note: `-D mismatched-lifetime-syntaxes` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(mismatched_lifetime_syntaxes)]`
-help: one option is to consistently use `'a`
+help: consistently use `'a`
    |
 LL |     fn cow_good_ret_ty<'a>(input: &'a Cow<'a, str>) -> &'a str {
    |                                                         ++
diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed
index 099c118e64e..9f6643e8d52 100644
--- a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed
+++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed
@@ -60,7 +60,7 @@ fn issue9956() {
     //~^ redundant_closure_call
 
     // immediately calling only one closure, so we can't remove the other ones
-    let a = (|| || 123);
+    let a = || || 123;
     //~^ redundant_closure_call
     dbg!(a()());
 
@@ -144,3 +144,15 @@ fn issue_12358() {
     // different.
     make_closure!(x)();
 }
+
+#[rustfmt::skip]
+fn issue_9583() {
+    Some(true) == Some(true);
+     //~^ redundant_closure_call
+    Some(true) == Some(true);
+    //~^ redundant_closure_call
+    Some(if 1 > 2 {1} else {2}) == Some(2);
+    //~^ redundant_closure_call
+    Some( 1 > 2 ) == Some(true);
+    //~^ redundant_closure_call
+}
diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs
index da5dd7ef263..34f22878678 100644
--- a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs
+++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs
@@ -144,3 +144,15 @@ fn issue_12358() {
     // different.
     make_closure!(x)();
 }
+
+#[rustfmt::skip]
+fn issue_9583() {
+    (|| { Some(true) })() == Some(true);
+     //~^ redundant_closure_call
+    (|| Some(true))() == Some(true);
+    //~^ redundant_closure_call
+    (|| { Some(if 1 > 2 {1} else {2}) })() == Some(2);
+    //~^ redundant_closure_call
+    (|| { Some( 1 > 2 ) })() == Some(true);
+    //~^ redundant_closure_call
+}
diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr
index 2c35aafbe31..a5591cf7813 100644
--- a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr
+++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.stderr
@@ -95,7 +95,7 @@ error: try not to call a closure in the expression where it is declared
   --> tests/ui/redundant_closure_call_fixable.rs:63:13
    |
 LL |     let a = (|| || || 123)();
-   |             ^^^^^^^^^^^^^^^^ help: try doing something like: `(|| || 123)`
+   |             ^^^^^^^^^^^^^^^^ help: try doing something like: `|| || 123`
 
 error: try not to call a closure in the expression where it is declared
   --> tests/ui/redundant_closure_call_fixable.rs:68:13
@@ -145,5 +145,29 @@ error: try not to call a closure in the expression where it is declared
 LL |     std::convert::identity((|| 13_i32 + 36_i32)()).leading_zeros();
    |                            ^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `13_i32 + 36_i32`
 
-error: aborting due to 17 previous errors
+error: try not to call a closure in the expression where it is declared
+  --> tests/ui/redundant_closure_call_fixable.rs:150:5
+   |
+LL |     (|| { Some(true) })() == Some(true);
+   |     ^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `Some(true)`
+
+error: try not to call a closure in the expression where it is declared
+  --> tests/ui/redundant_closure_call_fixable.rs:152:5
+   |
+LL |     (|| Some(true))() == Some(true);
+   |     ^^^^^^^^^^^^^^^^^ help: try doing something like: `Some(true)`
+
+error: try not to call a closure in the expression where it is declared
+  --> tests/ui/redundant_closure_call_fixable.rs:154:5
+   |
+LL |     (|| { Some(if 1 > 2 {1} else {2}) })() == Some(2);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `Some(if 1 > 2 {1} else {2})`
+
+error: try not to call a closure in the expression where it is declared
+  --> tests/ui/redundant_closure_call_fixable.rs:156:5
+   |
+LL |     (|| { Some( 1 > 2 ) })() == Some(true);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^ help: try doing something like: `Some( 1 > 2 )`
+
+error: aborting due to 21 previous errors
 
diff --git a/src/tools/clippy/tests/ui/return_and_then.fixed b/src/tools/clippy/tests/ui/return_and_then.fixed
index 8d9481d1595..8ee259b97f3 100644
--- a/src/tools/clippy/tests/ui/return_and_then.fixed
+++ b/src/tools/clippy/tests/ui/return_and_then.fixed
@@ -99,6 +99,92 @@ fn main() {
         };
         None
     }
+
+    #[expect(clippy::diverging_sub_expression)]
+    fn with_return_in_expression() -> Option<i32> {
+        _ = (
+            return {
+                let x = Some("")?;
+                if x.len() > 2 { Some(3) } else { None }
+            },
+            //~^ return_and_then
+            10,
+        );
+    }
+
+    fn inside_if(a: bool, i: Option<u32>) -> Option<u32> {
+        if a {
+            let i = i?;
+            if i > 3 { Some(i) } else { None }
+            //~^ return_and_then
+        } else {
+            Some(42)
+        }
+    }
+
+    fn inside_match(a: u32, i: Option<u32>) -> Option<u32> {
+        match a {
+            1 | 2 => {
+                let i = i?;
+                if i > 3 { Some(i) } else { None }
+            },
+            //~^ return_and_then
+            3 | 4 => Some(42),
+            _ => None,
+        }
+    }
+
+    fn inside_match_and_block_and_if(a: u32, i: Option<u32>) -> Option<u32> {
+        match a {
+            1 | 2 => {
+                let a = a * 3;
+                if a.is_multiple_of(2) {
+                    let i = i?;
+                    if i > 3 { Some(i) } else { None }
+                    //~^ return_and_then
+                } else {
+                    Some(10)
+                }
+            },
+            3 | 4 => Some(42),
+            _ => None,
+        }
+    }
+
+    #[expect(clippy::never_loop)]
+    fn with_break(i: Option<u32>) -> Option<u32> {
+        match i {
+            Some(1) => loop {
+                break ({
+                    let i = i?;
+                    if i > 3 { Some(i) } else { None }
+                });
+                //~^ return_and_then
+            },
+            Some(2) => 'foo: loop {
+                loop {
+                    break 'foo ({
+                        let i = i?;
+                        if i > 3 { Some(i) } else { None }
+                    });
+                    //~^ return_and_then
+                }
+            },
+            Some(3) => 'bar: {
+                break 'bar ({
+                    let i = i?;
+                    if i > 3 { Some(i) } else { None }
+                });
+                //~^ return_and_then
+            },
+            Some(4) => 'baz: loop {
+                _ = loop {
+                    break i.and_then(|i| if i > 3 { Some(i) } else { None });
+                };
+            },
+            _ => None,
+        }
+    }
 }
 
 fn gen_option(n: i32) -> Option<i32> {
@@ -124,3 +210,48 @@ mod issue14781 {
         Ok(())
     }
 }
+
+mod issue15111 {
+    #[derive(Debug)]
+    struct EvenOdd {
+        even: Option<u32>,
+        odd: Option<u32>,
+    }
+
+    impl EvenOdd {
+        fn new(i: Option<u32>) -> Self {
+            Self {
+                even: i.and_then(|i| if i.is_multiple_of(2) { Some(i) } else { None }),
+                odd: i.and_then(|i| if i.is_multiple_of(2) { None } else { Some(i) }),
+            }
+        }
+    }
+
+    fn with_if_let(i: Option<u32>) -> u32 {
+        if let Some(x) = i.and_then(|i| if i.is_multiple_of(2) { Some(i) } else { None }) {
+            x
+        } else {
+            std::hint::black_box(0)
+        }
+    }
+
+    fn main() {
+        let _ = EvenOdd::new(Some(2));
+    }
+}
+
+mod issue14927 {
+    use std::path::Path;
+    struct A {
+        pub func: fn(check: bool, a: &Path, b: Option<&Path>),
+    }
+    const MY_A: A = A {
+        func: |check, a, b| {
+            if check {
+                let _ = ();
+            } else if let Some(parent) = b.and_then(|p| p.parent()) {
+                let _ = ();
+            }
+        },
+    };
+}
diff --git a/src/tools/clippy/tests/ui/return_and_then.rs b/src/tools/clippy/tests/ui/return_and_then.rs
index beada921a91..dcb344f142b 100644
--- a/src/tools/clippy/tests/ui/return_and_then.rs
+++ b/src/tools/clippy/tests/ui/return_and_then.rs
@@ -90,6 +90,75 @@ fn main() {
         };
         None
     }
+
+    #[expect(clippy::diverging_sub_expression)]
+    fn with_return_in_expression() -> Option<i32> {
+        _ = (
+            return Some("").and_then(|x| if x.len() > 2 { Some(3) } else { None }),
+            //~^ return_and_then
+            10,
+        );
+    }
+
+    fn inside_if(a: bool, i: Option<u32>) -> Option<u32> {
+        if a {
+            i.and_then(|i| if i > 3 { Some(i) } else { None })
+            //~^ return_and_then
+        } else {
+            Some(42)
+        }
+    }
+
+    fn inside_match(a: u32, i: Option<u32>) -> Option<u32> {
+        match a {
+            1 | 2 => i.and_then(|i| if i > 3 { Some(i) } else { None }),
+            //~^ return_and_then
+            3 | 4 => Some(42),
+            _ => None,
+        }
+    }
+
+    fn inside_match_and_block_and_if(a: u32, i: Option<u32>) -> Option<u32> {
+        match a {
+            1 | 2 => {
+                let a = a * 3;
+                if a.is_multiple_of(2) {
+                    i.and_then(|i| if i > 3 { Some(i) } else { None })
+                    //~^ return_and_then
+                } else {
+                    Some(10)
+                }
+            },
+            3 | 4 => Some(42),
+            _ => None,
+        }
+    }
+
+    #[expect(clippy::never_loop)]
+    fn with_break(i: Option<u32>) -> Option<u32> {
+        match i {
+            Some(1) => loop {
+                break i.and_then(|i| if i > 3 { Some(i) } else { None });
+                //~^ return_and_then
+            },
+            Some(2) => 'foo: loop {
+                loop {
+                    break 'foo i.and_then(|i| if i > 3 { Some(i) } else { None });
+                    //~^ return_and_then
+                }
+            },
+            Some(3) => 'bar: {
+                break 'bar i.and_then(|i| if i > 3 { Some(i) } else { None });
+                //~^ return_and_then
+            },
+            Some(4) => 'baz: loop {
+                _ = loop {
+                    break i.and_then(|i| if i > 3 { Some(i) } else { None });
+                };
+            },
+            _ => None,
+        }
+    }
 }
 
 fn gen_option(n: i32) -> Option<i32> {
@@ -115,3 +184,48 @@ mod issue14781 {
         Ok(())
     }
 }
+
+mod issue15111 {
+    #[derive(Debug)]
+    struct EvenOdd {
+        even: Option<u32>,
+        odd: Option<u32>,
+    }
+
+    impl EvenOdd {
+        fn new(i: Option<u32>) -> Self {
+            Self {
+                even: i.and_then(|i| if i.is_multiple_of(2) { Some(i) } else { None }),
+                odd: i.and_then(|i| if i.is_multiple_of(2) { None } else { Some(i) }),
+            }
+        }
+    }
+
+    fn with_if_let(i: Option<u32>) -> u32 {
+        if let Some(x) = i.and_then(|i| if i.is_multiple_of(2) { Some(i) } else { None }) {
+            x
+        } else {
+            std::hint::black_box(0)
+        }
+    }
+
+    fn main() {
+        let _ = EvenOdd::new(Some(2));
+    }
+}
+
+mod issue14927 {
+    use std::path::Path;
+    struct A {
+        pub func: fn(check: bool, a: &Path, b: Option<&Path>),
+    }
+    const MY_A: A = A {
+        func: |check, a, b| {
+            if check {
+                let _ = ();
+            } else if let Some(parent) = b.and_then(|p| p.parent()) {
+                let _ = ();
+            }
+        },
+    };
+}
diff --git a/src/tools/clippy/tests/ui/return_and_then.stderr b/src/tools/clippy/tests/ui/return_and_then.stderr
index 5feca882860..33867ea818a 100644
--- a/src/tools/clippy/tests/ui/return_and_then.stderr
+++ b/src/tools/clippy/tests/ui/return_and_then.stderr
@@ -146,5 +146,99 @@ LL +                 if x.len() > 2 { Some(3) } else { None }
 LL ~             };
    |
 
-error: aborting due to 10 previous errors
+error: use the `?` operator instead of an `and_then` call
+  --> tests/ui/return_and_then.rs:97:20
+   |
+LL |             return Some("").and_then(|x| if x.len() > 2 { Some(3) } else { None }),
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL ~             return {
+LL +                 let x = Some("")?;
+LL +                 if x.len() > 2 { Some(3) } else { None }
+LL ~             },
+   |
+
+error: use the `?` operator instead of an `and_then` call
+  --> tests/ui/return_and_then.rs:105:13
+   |
+LL |             i.and_then(|i| if i > 3 { Some(i) } else { None })
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL ~             let i = i?;
+LL +             if i > 3 { Some(i) } else { None }
+   |
+
+error: use the `?` operator instead of an `and_then` call
+  --> tests/ui/return_and_then.rs:114:22
+   |
+LL |             1 | 2 => i.and_then(|i| if i > 3 { Some(i) } else { None }),
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL ~             1 | 2 => {
+LL +                 let i = i?;
+LL +                 if i > 3 { Some(i) } else { None }
+LL ~             },
+   |
+
+error: use the `?` operator instead of an `and_then` call
+  --> tests/ui/return_and_then.rs:126:21
+   |
+LL |                     i.and_then(|i| if i > 3 { Some(i) } else { None })
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL ~                     let i = i?;
+LL +                     if i > 3 { Some(i) } else { None }
+   |
+
+error: use the `?` operator instead of an `and_then` call
+  --> tests/ui/return_and_then.rs:141:23
+   |
+LL |                 break i.and_then(|i| if i > 3 { Some(i) } else { None });
+   |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL ~                 break ({
+LL +                     let i = i?;
+LL +                     if i > 3 { Some(i) } else { None }
+LL ~                 });
+   |
+
+error: use the `?` operator instead of an `and_then` call
+  --> tests/ui/return_and_then.rs:146:32
+   |
+LL |                     break 'foo i.and_then(|i| if i > 3 { Some(i) } else { None });
+   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL ~                     break 'foo ({
+LL +                         let i = i?;
+LL +                         if i > 3 { Some(i) } else { None }
+LL ~                     });
+   |
+
+error: use the `?` operator instead of an `and_then` call
+  --> tests/ui/return_and_then.rs:151:28
+   |
+LL |                 break 'bar i.and_then(|i| if i > 3 { Some(i) } else { None });
+   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: try
+   |
+LL ~                 break 'bar ({
+LL +                     let i = i?;
+LL +                     if i > 3 { Some(i) } else { None }
+LL ~                 });
+   |
+
+error: aborting due to 17 previous errors
 
diff --git a/src/tools/clippy/tests/ui/same_name_method.stderr b/src/tools/clippy/tests/ui/same_name_method.stderr
index b2624ac4d26..bf7456d80e2 100644
--- a/src/tools/clippy/tests/ui/same_name_method.stderr
+++ b/src/tools/clippy/tests/ui/same_name_method.stderr
@@ -2,13 +2,13 @@ error: method's name is the same as an existing method in a trait
   --> tests/ui/same_name_method.rs:20:13
    |
 LL |             fn foo() {}
-   |             ^^^^^^^^^^^
+   |             ^^^^^^^^
    |
 note: existing `foo` defined here
   --> tests/ui/same_name_method.rs:25:13
    |
 LL |             fn foo() {}
-   |             ^^^^^^^^^^^
+   |             ^^^^^^^^
    = note: `-D clippy::same-name-method` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::same_name_method)]`
 
@@ -16,7 +16,7 @@ error: method's name is the same as an existing method in a trait
   --> tests/ui/same_name_method.rs:35:13
    |
 LL |             fn clone() {}
-   |             ^^^^^^^^^^^^^
+   |             ^^^^^^^^^^
    |
 note: existing `clone` defined here
   --> tests/ui/same_name_method.rs:31:18
@@ -28,19 +28,19 @@ error: method's name is the same as an existing method in a trait
   --> tests/ui/same_name_method.rs:46:13
    |
 LL |             fn foo() {}
-   |             ^^^^^^^^^^^
+   |             ^^^^^^^^
    |
 note: existing `foo` defined here
   --> tests/ui/same_name_method.rs:51:13
    |
 LL |             fn foo() {}
-   |             ^^^^^^^^^^^
+   |             ^^^^^^^^
 
 error: method's name is the same as an existing method in a trait
   --> tests/ui/same_name_method.rs:61:13
    |
 LL |             fn foo() {}
-   |             ^^^^^^^^^^^
+   |             ^^^^^^^^
    |
 note: existing `foo` defined here
   --> tests/ui/same_name_method.rs:65:9
@@ -52,7 +52,7 @@ error: method's name is the same as an existing method in a trait
   --> tests/ui/same_name_method.rs:74:13
    |
 LL |             fn foo() {}
-   |             ^^^^^^^^^^^
+   |             ^^^^^^^^
    |
 note: existing `foo` defined here
   --> tests/ui/same_name_method.rs:79:9
@@ -64,7 +64,7 @@ error: method's name is the same as an existing method in a trait
   --> tests/ui/same_name_method.rs:74:13
    |
 LL |             fn foo() {}
-   |             ^^^^^^^^^^^
+   |             ^^^^^^^^
    |
 note: existing `foo` defined here
   --> tests/ui/same_name_method.rs:81:9
diff --git a/src/tools/clippy/tests/ui/serde.stderr b/src/tools/clippy/tests/ui/serde.stderr
index eb6b7c6b0c3..652248e3578 100644
--- a/src/tools/clippy/tests/ui/serde.stderr
+++ b/src/tools/clippy/tests/ui/serde.stderr
@@ -5,10 +5,7 @@ LL | /     fn visit_string<E>(self, _v: String) -> Result<Self::Value, E>
 LL | |
 LL | |     where
 LL | |         E: serde::de::Error,
-LL | |     {
-LL | |         unimplemented!()
-LL | |     }
-   | |_____^
+   | |____________________________^
    |
    = note: `-D clippy::serde-api-misuse` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::serde_api_misuse)]`
diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.fixed b/src/tools/clippy/tests/ui/std_instead_of_core.fixed
index 1820ade422f..603ab0accb0 100644
--- a/src/tools/clippy/tests/ui/std_instead_of_core.fixed
+++ b/src/tools/clippy/tests/ui/std_instead_of_core.fixed
@@ -8,7 +8,6 @@ extern crate alloc;
 #[macro_use]
 extern crate proc_macro_derive;
 
-#[warn(clippy::std_instead_of_core)]
 fn std_instead_of_core() {
     // Regular import
     use core::hash::Hasher;
@@ -90,9 +89,3 @@ fn msrv_1_76(_: std::net::IpAddr) {}
 #[clippy::msrv = "1.77"]
 fn msrv_1_77(_: core::net::IpAddr) {}
 //~^ std_instead_of_core
-
-#[warn(clippy::std_instead_of_core)]
-#[rustfmt::skip]
-fn issue14982() {
-    use std::{collections::HashMap, hash::Hash};
-}
diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.rs b/src/tools/clippy/tests/ui/std_instead_of_core.rs
index 32c49330981..b6d4abad9f8 100644
--- a/src/tools/clippy/tests/ui/std_instead_of_core.rs
+++ b/src/tools/clippy/tests/ui/std_instead_of_core.rs
@@ -8,7 +8,6 @@ extern crate alloc;
 #[macro_use]
 extern crate proc_macro_derive;
 
-#[warn(clippy::std_instead_of_core)]
 fn std_instead_of_core() {
     // Regular import
     use std::hash::Hasher;
@@ -90,9 +89,3 @@ fn msrv_1_76(_: std::net::IpAddr) {}
 #[clippy::msrv = "1.77"]
 fn msrv_1_77(_: std::net::IpAddr) {}
 //~^ std_instead_of_core
-
-#[warn(clippy::std_instead_of_core)]
-#[rustfmt::skip]
-fn issue14982() {
-    use std::{collections::HashMap, hash::Hash};
-}
diff --git a/src/tools/clippy/tests/ui/std_instead_of_core.stderr b/src/tools/clippy/tests/ui/std_instead_of_core.stderr
index 45d60d235ce..a5f8fbbe37c 100644
--- a/src/tools/clippy/tests/ui/std_instead_of_core.stderr
+++ b/src/tools/clippy/tests/ui/std_instead_of_core.stderr
@@ -1,5 +1,5 @@
 error: used import from `std` instead of `core`
-  --> tests/ui/std_instead_of_core.rs:14:9
+  --> tests/ui/std_instead_of_core.rs:13:9
    |
 LL |     use std::hash::Hasher;
    |         ^^^ help: consider importing the item from `core`: `core`
@@ -8,61 +8,61 @@ LL |     use std::hash::Hasher;
    = help: to override `-D warnings` add `#[allow(clippy::std_instead_of_core)]`
 
 error: used import from `std` instead of `core`
-  --> tests/ui/std_instead_of_core.rs:17:11
+  --> tests/ui/std_instead_of_core.rs:16:11
    |
 LL |     use ::std::hash::Hash;
    |           ^^^ help: consider importing the item from `core`: `core`
 
 error: used import from `std` instead of `core`
-  --> tests/ui/std_instead_of_core.rs:23:9
+  --> tests/ui/std_instead_of_core.rs:22:9
    |
 LL |     use std::fmt::{Debug, Result};
    |         ^^^ help: consider importing the item from `core`: `core`
 
 error: used import from `std` instead of `core`
-  --> tests/ui/std_instead_of_core.rs:28:9
+  --> tests/ui/std_instead_of_core.rs:27:9
    |
 LL |     use std::{
    |         ^^^ help: consider importing the item from `core`: `core`
 
 error: used import from `std` instead of `core`
-  --> tests/ui/std_instead_of_core.rs:35:15
+  --> tests/ui/std_instead_of_core.rs:34:15
    |
 LL |     let ptr = std::ptr::null::<u32>();
    |               ^^^ help: consider importing the item from `core`: `core`
 
 error: used import from `std` instead of `core`
-  --> tests/ui/std_instead_of_core.rs:37:21
+  --> tests/ui/std_instead_of_core.rs:36:21
    |
 LL |     let ptr_mut = ::std::ptr::null_mut::<usize>();
    |                     ^^^ help: consider importing the item from `core`: `core`
 
 error: used import from `std` instead of `core`
-  --> tests/ui/std_instead_of_core.rs:41:16
+  --> tests/ui/std_instead_of_core.rs:40:16
    |
 LL |     let cell = std::cell::Cell::new(8u32);
    |                ^^^ help: consider importing the item from `core`: `core`
 
 error: used import from `std` instead of `core`
-  --> tests/ui/std_instead_of_core.rs:43:27
+  --> tests/ui/std_instead_of_core.rs:42:27
    |
 LL |     let cell_absolute = ::std::cell::Cell::new(8u32);
    |                           ^^^ help: consider importing the item from `core`: `core`
 
 error: used import from `std` instead of `core`
-  --> tests/ui/std_instead_of_core.rs:48:9
+  --> tests/ui/std_instead_of_core.rs:47:9
    |
 LL |     use std::error::Error;
    |         ^^^ help: consider importing the item from `core`: `core`
 
 error: used import from `std` instead of `core`
-  --> tests/ui/std_instead_of_core.rs:52:9
+  --> tests/ui/std_instead_of_core.rs:51:9
    |
 LL |     use std::iter::Iterator;
    |         ^^^ help: consider importing the item from `core`: `core`
 
 error: used import from `std` instead of `alloc`
-  --> tests/ui/std_instead_of_core.rs:59:9
+  --> tests/ui/std_instead_of_core.rs:58:9
    |
 LL |     use std::vec;
    |         ^^^ help: consider importing the item from `alloc`: `alloc`
@@ -71,13 +71,13 @@ LL |     use std::vec;
    = help: to override `-D warnings` add `#[allow(clippy::std_instead_of_alloc)]`
 
 error: used import from `std` instead of `alloc`
-  --> tests/ui/std_instead_of_core.rs:61:9
+  --> tests/ui/std_instead_of_core.rs:60:9
    |
 LL |     use std::vec::Vec;
    |         ^^^ help: consider importing the item from `alloc`: `alloc`
 
 error: used import from `alloc` instead of `core`
-  --> tests/ui/std_instead_of_core.rs:67:9
+  --> tests/ui/std_instead_of_core.rs:66:9
    |
 LL |     use alloc::slice::from_ref;
    |         ^^^^^ help: consider importing the item from `core`: `core`
@@ -86,13 +86,13 @@ LL |     use alloc::slice::from_ref;
    = help: to override `-D warnings` add `#[allow(clippy::alloc_instead_of_core)]`
 
 error: used import from `std` instead of `core`
-  --> tests/ui/std_instead_of_core.rs:82:9
+  --> tests/ui/std_instead_of_core.rs:81:9
    |
 LL |         std::intrinsics::copy(a, b, 1);
    |         ^^^ help: consider importing the item from `core`: `core`
 
 error: used import from `std` instead of `core`
-  --> tests/ui/std_instead_of_core.rs:91:17
+  --> tests/ui/std_instead_of_core.rs:90:17
    |
 LL | fn msrv_1_77(_: std::net::IpAddr) {}
    |                 ^^^ help: consider importing the item from `core`: `core`
diff --git a/src/tools/clippy/tests/ui/std_instead_of_core_unfixable.rs b/src/tools/clippy/tests/ui/std_instead_of_core_unfixable.rs
new file mode 100644
index 00000000000..957f472a454
--- /dev/null
+++ b/src/tools/clippy/tests/ui/std_instead_of_core_unfixable.rs
@@ -0,0 +1,18 @@
+//@no-rustfix
+
+#![warn(clippy::std_instead_of_core)]
+#![warn(clippy::std_instead_of_alloc)]
+#![allow(unused_imports)]
+
+#[rustfmt::skip]
+fn issue14982() {
+    use std::{collections::HashMap, hash::Hash};
+    //~^ std_instead_of_core
+}
+
+#[rustfmt::skip]
+fn issue15143() {
+    use std::{error::Error, vec::Vec, fs::File};
+    //~^ std_instead_of_core
+    //~| std_instead_of_alloc
+}
diff --git a/src/tools/clippy/tests/ui/std_instead_of_core_unfixable.stderr b/src/tools/clippy/tests/ui/std_instead_of_core_unfixable.stderr
new file mode 100644
index 00000000000..0cdec56c992
--- /dev/null
+++ b/src/tools/clippy/tests/ui/std_instead_of_core_unfixable.stderr
@@ -0,0 +1,30 @@
+error: used import from `std` instead of `core`
+  --> tests/ui/std_instead_of_core_unfixable.rs:9:43
+   |
+LL |     use std::{collections::HashMap, hash::Hash};
+   |                                           ^^^^
+   |
+   = help: consider importing the item from `core`
+   = note: `-D clippy::std-instead-of-core` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::std_instead_of_core)]`
+
+error: used import from `std` instead of `core`
+  --> tests/ui/std_instead_of_core_unfixable.rs:15:22
+   |
+LL |     use std::{error::Error, vec::Vec, fs::File};
+   |                      ^^^^^
+   |
+   = help: consider importing the item from `core`
+
+error: used import from `std` instead of `alloc`
+  --> tests/ui/std_instead_of_core_unfixable.rs:15:34
+   |
+LL |     use std::{error::Error, vec::Vec, fs::File};
+   |                                  ^^^
+   |
+   = help: consider importing the item from `alloc`
+   = note: `-D clippy::std-instead-of-alloc` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::std_instead_of_alloc)]`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/swap_with_temporary.fixed b/src/tools/clippy/tests/ui/swap_with_temporary.fixed
index 4007d998ba0..4b4b0d4aebd 100644
--- a/src/tools/clippy/tests/ui/swap_with_temporary.fixed
+++ b/src/tools/clippy/tests/ui/swap_with_temporary.fixed
@@ -72,3 +72,49 @@ fn dont_lint_those(s: &mut S, v: &mut [String], w: Option<&mut String>) {
     swap(&mut s.t, v.get_mut(0).unwrap());
     swap(w.unwrap(), &mut s.t);
 }
+
+fn issue15166() {
+    use std::sync::Mutex;
+
+    struct A {
+        thing: Mutex<Vec<u8>>,
+    }
+
+    impl A {
+        fn a(&self) {
+            let mut new_vec = vec![42];
+            // Do not lint here, as neither `new_vec` nor the result of `.lock().unwrap()` are temporaries
+            swap(&mut new_vec, &mut self.thing.lock().unwrap());
+            for v in new_vec {
+                // Do something with v
+            }
+            // Here `vec![42]` is temporary though, and a proper dereference will have to be used in the fix
+            *self.thing.lock().unwrap() = vec![42];
+            //~^ ERROR: swapping with a temporary value is inefficient
+        }
+    }
+}
+
+fn multiple_deref() {
+    let mut v1 = &mut &mut &mut vec![42];
+    ***v1 = vec![];
+    //~^ ERROR: swapping with a temporary value is inefficient
+
+    struct Wrapper<T: ?Sized>(T);
+    impl<T: ?Sized> std::ops::Deref for Wrapper<T> {
+        type Target = T;
+        fn deref(&self) -> &Self::Target {
+            &self.0
+        }
+    }
+    impl<T: ?Sized> std::ops::DerefMut for Wrapper<T> {
+        fn deref_mut(&mut self) -> &mut Self::Target {
+            &mut self.0
+        }
+    }
+
+    use std::sync::Mutex;
+    let mut v1 = Mutex::new(Wrapper(Wrapper(vec![42])));
+    ***v1.lock().unwrap() = vec![];
+    //~^ ERROR: swapping with a temporary value is inefficient
+}
diff --git a/src/tools/clippy/tests/ui/swap_with_temporary.rs b/src/tools/clippy/tests/ui/swap_with_temporary.rs
index d403c086c0f..8e35e6144d9 100644
--- a/src/tools/clippy/tests/ui/swap_with_temporary.rs
+++ b/src/tools/clippy/tests/ui/swap_with_temporary.rs
@@ -72,3 +72,49 @@ fn dont_lint_those(s: &mut S, v: &mut [String], w: Option<&mut String>) {
     swap(&mut s.t, v.get_mut(0).unwrap());
     swap(w.unwrap(), &mut s.t);
 }
+
+fn issue15166() {
+    use std::sync::Mutex;
+
+    struct A {
+        thing: Mutex<Vec<u8>>,
+    }
+
+    impl A {
+        fn a(&self) {
+            let mut new_vec = vec![42];
+            // Do not lint here, as neither `new_vec` nor the result of `.lock().unwrap()` are temporaries
+            swap(&mut new_vec, &mut self.thing.lock().unwrap());
+            for v in new_vec {
+                // Do something with v
+            }
+            // Here `vec![42]` is temporary though, and a proper dereference will have to be used in the fix
+            swap(&mut vec![42], &mut self.thing.lock().unwrap());
+            //~^ ERROR: swapping with a temporary value is inefficient
+        }
+    }
+}
+
+fn multiple_deref() {
+    let mut v1 = &mut &mut &mut vec![42];
+    swap(&mut ***v1, &mut vec![]);
+    //~^ ERROR: swapping with a temporary value is inefficient
+
+    struct Wrapper<T: ?Sized>(T);
+    impl<T: ?Sized> std::ops::Deref for Wrapper<T> {
+        type Target = T;
+        fn deref(&self) -> &Self::Target {
+            &self.0
+        }
+    }
+    impl<T: ?Sized> std::ops::DerefMut for Wrapper<T> {
+        fn deref_mut(&mut self) -> &mut Self::Target {
+            &mut self.0
+        }
+    }
+
+    use std::sync::Mutex;
+    let mut v1 = Mutex::new(Wrapper(Wrapper(vec![42])));
+    swap(&mut vec![], &mut v1.lock().unwrap());
+    //~^ ERROR: swapping with a temporary value is inefficient
+}
diff --git a/src/tools/clippy/tests/ui/swap_with_temporary.stderr b/src/tools/clippy/tests/ui/swap_with_temporary.stderr
index 59355771a96..5ca4fccd37a 100644
--- a/src/tools/clippy/tests/ui/swap_with_temporary.stderr
+++ b/src/tools/clippy/tests/ui/swap_with_temporary.stderr
@@ -96,5 +96,41 @@ note: this expression returns a temporary value
 LL |     swap(mac!(refmut y), &mut func());
    |                               ^^^^^^
 
-error: aborting due to 8 previous errors
+error: swapping with a temporary value is inefficient
+  --> tests/ui/swap_with_temporary.rs:92:13
+   |
+LL |             swap(&mut vec![42], &mut self.thing.lock().unwrap());
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `*self.thing.lock().unwrap() = vec![42]`
+   |
+note: this expression returns a temporary value
+  --> tests/ui/swap_with_temporary.rs:92:23
+   |
+LL |             swap(&mut vec![42], &mut self.thing.lock().unwrap());
+   |                       ^^^^^^^^
+
+error: swapping with a temporary value is inefficient
+  --> tests/ui/swap_with_temporary.rs:100:5
+   |
+LL |     swap(&mut ***v1, &mut vec![]);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `***v1 = vec![]`
+   |
+note: this expression returns a temporary value
+  --> tests/ui/swap_with_temporary.rs:100:27
+   |
+LL |     swap(&mut ***v1, &mut vec![]);
+   |                           ^^^^^^
+
+error: swapping with a temporary value is inefficient
+  --> tests/ui/swap_with_temporary.rs:118:5
+   |
+LL |     swap(&mut vec![], &mut v1.lock().unwrap());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use assignment instead: `***v1.lock().unwrap() = vec![]`
+   |
+note: this expression returns a temporary value
+  --> tests/ui/swap_with_temporary.rs:118:15
+   |
+LL |     swap(&mut vec![], &mut v1.lock().unwrap());
+   |               ^^^^^^
+
+error: aborting due to 11 previous errors
 
diff --git a/src/tools/clippy/tests/ui/track-diagnostics-clippy.rs b/src/tools/clippy/tests/ui/track-diagnostics-clippy.rs
index 2e67fb65efc..3bae23f1984 100644
--- a/src/tools/clippy/tests/ui/track-diagnostics-clippy.rs
+++ b/src/tools/clippy/tests/ui/track-diagnostics-clippy.rs
@@ -4,6 +4,7 @@
 // Normalize the emitted location so this doesn't need
 // updating everytime someone adds or removes a line.
 //@normalize-stderr-test: ".rs:\d+:\d+" -> ".rs:LL:CC"
+//@normalize-stderr-test: "src/tools/clippy/" -> ""
 
 #![warn(clippy::let_and_return, clippy::unnecessary_cast)]
 
@@ -12,7 +13,7 @@ fn main() {
     let a = 3u32;
     let b = a as u32;
     //~^ unnecessary_cast
-    
+
     // Check the provenance of a lint sent through `TyCtxt::node_span_lint()`
     let c = {
         let d = 42;
diff --git a/src/tools/clippy/tests/ui/track-diagnostics-clippy.stderr b/src/tools/clippy/tests/ui/track-diagnostics-clippy.stderr
index 9d6538112bf..d5533877b45 100644
--- a/src/tools/clippy/tests/ui/track-diagnostics-clippy.stderr
+++ b/src/tools/clippy/tests/ui/track-diagnostics-clippy.stderr
@@ -4,7 +4,7 @@ error: casting to the same type is unnecessary (`u32` -> `u32`)
 LL |     let b = a as u32;
    |             ^^^^^^^^ help: try: `a`
    |
-   = note: -Ztrack-diagnostics: created at src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs:LL:CC
+   = note: -Ztrack-diagnostics: created at clippy_lints/src/casts/unnecessary_cast.rs:LL:CC
    = note: `-D clippy::unnecessary-cast` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::unnecessary_cast)]`
 
@@ -16,7 +16,7 @@ LL |         let d = 42;
 LL |         d
    |         ^
    |
-   = note: -Ztrack-diagnostics: created at src/tools/clippy/clippy_lints/src/returns.rs:LL:CC
+   = note: -Ztrack-diagnostics: created at clippy_lints/src/returns.rs:LL:CC
    = note: `-D clippy::let-and-return` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::let_and_return)]`
 help: return the expression directly
diff --git a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.fixed b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.fixed
index cf52ecf2f03..88ba5f810b4 100644
--- a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.fixed
+++ b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.fixed
@@ -167,8 +167,7 @@ where
 }
 
 // #13476
-#[const_trait]
-trait ConstTrait {}
+const trait ConstTrait {}
 const fn const_trait_bounds_good<T: ConstTrait + [const] ConstTrait>() {}
 
 const fn const_trait_bounds_bad<T: [const] ConstTrait>() {}
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 955562f08dc..19a4e70e294 100644
--- a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs
+++ b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.rs
@@ -167,8 +167,7 @@ where
 }
 
 // #13476
-#[const_trait]
-trait ConstTrait {}
+const trait ConstTrait {}
 const fn const_trait_bounds_good<T: ConstTrait + [const] ConstTrait>() {}
 
 const fn const_trait_bounds_bad<T: [const] ConstTrait + [const] ConstTrait>() {}
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 ab31721ef51..a56a683de97 100644
--- a/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr
+++ b/src/tools/clippy/tests/ui/trait_duplication_in_bounds.stderr
@@ -59,19 +59,19 @@ LL | fn bad_trait_object(arg0: &(dyn Any + Send + Send)) {
    |                                 ^^^^^^^^^^^^^^^^^ help: try: `Any + Send`
 
 error: these bounds contain repeated elements
-  --> tests/ui/trait_duplication_in_bounds.rs:174:36
+  --> tests/ui/trait_duplication_in_bounds.rs:173:36
    |
 LL | const fn const_trait_bounds_bad<T: [const] ConstTrait + [const] ConstTrait>() {}
    |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `[const] ConstTrait`
 
 error: these where clauses contain repeated elements
-  --> tests/ui/trait_duplication_in_bounds.rs:181:8
+  --> tests/ui/trait_duplication_in_bounds.rs:180:8
    |
 LL |     T: IntoIterator<Item = U::Owned> + IntoIterator<Item = U::Owned>,
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `IntoIterator<Item = U::Owned>`
 
 error: these where clauses contain repeated elements
-  --> tests/ui/trait_duplication_in_bounds.rs:203:8
+  --> tests/ui/trait_duplication_in_bounds.rs:202:8
    |
 LL |     T: AssocConstTrait<ASSOC = 0> + AssocConstTrait<ASSOC = 0>,
    |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `AssocConstTrait<ASSOC = 0>`
diff --git a/src/tools/clippy/tests/ui/unnecessary_map_or.fixed b/src/tools/clippy/tests/ui/unnecessary_map_or.fixed
index 3c724397284..3109c4af8e2 100644
--- a/src/tools/clippy/tests/ui/unnecessary_map_or.fixed
+++ b/src/tools/clippy/tests/ui/unnecessary_map_or.fixed
@@ -130,3 +130,13 @@ fn issue14201(a: Option<String>, b: Option<String>, s: &String) -> bool {
     //~^ unnecessary_map_or
     x && y
 }
+
+fn issue15180() {
+    let s = std::sync::Mutex::new(Some("foo"));
+    _ = s.lock().unwrap().is_some_and(|s| s == "foo");
+    //~^ unnecessary_map_or
+
+    let s = &&&&Some("foo");
+    _ = s.is_some_and(|s| s == "foo");
+    //~^ unnecessary_map_or
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_map_or.rs b/src/tools/clippy/tests/ui/unnecessary_map_or.rs
index e734a27bada..52a55f9fc9e 100644
--- a/src/tools/clippy/tests/ui/unnecessary_map_or.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_map_or.rs
@@ -134,3 +134,13 @@ fn issue14201(a: Option<String>, b: Option<String>, s: &String) -> bool {
     //~^ unnecessary_map_or
     x && y
 }
+
+fn issue15180() {
+    let s = std::sync::Mutex::new(Some("foo"));
+    _ = s.lock().unwrap().map_or(false, |s| s == "foo");
+    //~^ unnecessary_map_or
+
+    let s = &&&&Some("foo");
+    _ = s.map_or(false, |s| s == "foo");
+    //~^ unnecessary_map_or
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_map_or.stderr b/src/tools/clippy/tests/ui/unnecessary_map_or.stderr
index 0f9466a6a6b..99e17e8b34b 100644
--- a/src/tools/clippy/tests/ui/unnecessary_map_or.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_map_or.stderr
@@ -326,5 +326,29 @@ LL -     let y = b.map_or(true, |b| b == *s);
 LL +     let y = b.is_none_or(|b| b == *s);
    |
 
-error: aborting due to 26 previous errors
+error: this `map_or` can be simplified
+  --> tests/ui/unnecessary_map_or.rs:140:9
+   |
+LL |     _ = s.lock().unwrap().map_or(false, |s| s == "foo");
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use is_some_and instead
+   |
+LL -     _ = s.lock().unwrap().map_or(false, |s| s == "foo");
+LL +     _ = s.lock().unwrap().is_some_and(|s| s == "foo");
+   |
+
+error: this `map_or` can be simplified
+  --> tests/ui/unnecessary_map_or.rs:144:9
+   |
+LL |     _ = s.map_or(false, |s| s == "foo");
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use is_some_and instead
+   |
+LL -     _ = s.map_or(false, |s| s == "foo");
+LL +     _ = s.is_some_and(|s| s == "foo");
+   |
+
+error: aborting due to 28 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.fixed b/src/tools/clippy/tests/ui/unnecessary_operation.fixed
index 645b56fe95e..ac9fa4de20a 100644
--- a/src/tools/clippy/tests/ui/unnecessary_operation.fixed
+++ b/src/tools/clippy/tests/ui/unnecessary_operation.fixed
@@ -144,3 +144,16 @@ const fn foo() {
     assert!([42, 55].len() > get_usize());
     //~^ unnecessary_operation
 }
+
+fn issue15173() {
+    // No lint as `Box::new(None)` alone would be ambiguous
+    Box::new(None) as Box<Option<i32>>;
+}
+
+#[expect(clippy::redundant_closure_call)]
+fn issue15173_original<MsU>(handler: impl FnOnce() -> MsU + Clone + 'static) {
+    Box::new(move |value| {
+        (|_| handler.clone()())(value);
+        None
+    }) as Box<dyn Fn(i32) -> Option<i32>>;
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.rs b/src/tools/clippy/tests/ui/unnecessary_operation.rs
index 97e90269c5c..a3e6c6288ad 100644
--- a/src/tools/clippy/tests/ui/unnecessary_operation.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_operation.rs
@@ -150,3 +150,16 @@ const fn foo() {
     [42, 55][get_usize()];
     //~^ unnecessary_operation
 }
+
+fn issue15173() {
+    // No lint as `Box::new(None)` alone would be ambiguous
+    Box::new(None) as Box<Option<i32>>;
+}
+
+#[expect(clippy::redundant_closure_call)]
+fn issue15173_original<MsU>(handler: impl FnOnce() -> MsU + Clone + 'static) {
+    Box::new(move |value| {
+        (|_| handler.clone()())(value);
+        None
+    }) as Box<dyn Fn(i32) -> Option<i32>>;
+}
diff --git a/src/tools/clippy/tests/ui/zero_ptr.fixed b/src/tools/clippy/tests/ui/zero_ptr.fixed
index f2375d57f3a..f9d9d2db176 100644
--- a/src/tools/clippy/tests/ui/zero_ptr.fixed
+++ b/src/tools/clippy/tests/ui/zero_ptr.fixed
@@ -16,3 +16,11 @@ fn main() {
     let z = 0;
     let _ = z as *const usize; // this is currently not caught
 }
+
+const fn in_const_context() {
+    #[clippy::msrv = "1.23"]
+    let _: *const usize = 0 as *const _;
+    #[clippy::msrv = "1.24"]
+    let _: *const usize = std::ptr::null();
+    //~^ zero_ptr
+}
diff --git a/src/tools/clippy/tests/ui/zero_ptr.rs b/src/tools/clippy/tests/ui/zero_ptr.rs
index ee01e426a43..41455fee5b5 100644
--- a/src/tools/clippy/tests/ui/zero_ptr.rs
+++ b/src/tools/clippy/tests/ui/zero_ptr.rs
@@ -16,3 +16,11 @@ fn main() {
     let z = 0;
     let _ = z as *const usize; // this is currently not caught
 }
+
+const fn in_const_context() {
+    #[clippy::msrv = "1.23"]
+    let _: *const usize = 0 as *const _;
+    #[clippy::msrv = "1.24"]
+    let _: *const usize = 0 as *const _;
+    //~^ zero_ptr
+}
diff --git a/src/tools/clippy/tests/ui/zero_ptr.stderr b/src/tools/clippy/tests/ui/zero_ptr.stderr
index 8dc781f3625..81269de6c60 100644
--- a/src/tools/clippy/tests/ui/zero_ptr.stderr
+++ b/src/tools/clippy/tests/ui/zero_ptr.stderr
@@ -31,5 +31,11 @@ error: `0 as *mut _` detected
 LL |     foo(0 as *const _, 0 as *mut _);
    |                        ^^^^^^^^^^^ help: try: `std::ptr::null_mut()`
 
-error: aborting due to 5 previous errors
+error: `0 as *const _` detected
+  --> tests/ui/zero_ptr.rs:24:27
+   |
+LL |     let _: *const usize = 0 as *const _;
+   |                           ^^^^^^^^^^^^^ help: try: `std::ptr::null()`
+
+error: aborting due to 6 previous errors
 
diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml
index 4f370758c00..805baf2af6d 100644
--- a/src/tools/clippy/triagebot.toml
+++ b/src/tools/clippy/triagebot.toml
@@ -15,6 +15,8 @@ allow-unauthenticated = [
 
 [close]
 
+[transfer]
+
 [issue-links]
 
 [mentions."clippy_lints/src/doc"]
@@ -43,12 +45,15 @@ reviewed_label = "S-waiting-on-author"
 [autolabel."S-waiting-on-review"]
 new_pr = true
 
+[concern]
+# These labels are set when there are unresolved concerns, removed otherwise
+labels = ["S-waiting-on-concerns"]
+
 [assign]
 contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md"
 users_on_vacation = [
     "matthiaskrgr",
     "Manishearth",
-    "blyxyas",
 ]
 
 [assign.owners]
diff --git a/src/tools/clippy/util/gh-pages/index_template.html b/src/tools/clippy/util/gh-pages/index_template.html
index 865b9523c39..6f380ec8fee 100644
--- a/src/tools/clippy/util/gh-pages/index_template.html
+++ b/src/tools/clippy/util/gh-pages/index_template.html
@@ -149,49 +149,45 @@ Otherwise, have a great day =^.^=
                 <article class="panel panel-default" id="{{lint.id}}"> {# #}
                     <input id="label-{{lint.id}}" type="checkbox"> {# #}
                     <label for="label-{{lint.id}}"> {# #}
-                        <header class="panel-heading"> {# #}
-                            <h2 class="panel-title"> {# #}
-                                <div class="panel-title-name" id="lint-{{lint.id}}"> {# #}
-                                    <span>{{lint.id}}</span> {#+ #}
-                                    <a href="#{{lint.id}}" class="lint-anchor anchor label label-default">&para;</a> {#+ #}
-                                    <a href="" class="copy-to-clipboard anchor label label-default"> {# #}
-                                        &#128203; {# #}
-                                    </a> {# #}
-                                </div> {# #}
+                        <h2 class="lint-title"> {# #}
+                            <div class="panel-title-name" id="lint-{{lint.id}}"> {# #}
+                                {{lint.id +}}
+                                <a href="#{{lint.id}}" class="anchor label label-default">&para;</a> {#+ #}
+                                <a href="" class="copy-to-clipboard anchor label label-default"> {# #}
+                                    &#128203; {# #}
+                                </a> {# #}
+                            </div> {# #}
 
-                                <div class="panel-title-addons"> {# #}
-                                    <span class="label label-lint-group label-default label-group-{{lint.group}}">{{lint.group}}</span> {#+ #}
+                            <span class="label label-lint-group label-default label-group-{{lint.group}}">{{lint.group}}</span> {#+ #}
 
-                                    <span class="label label-lint-level label-lint-level-{{lint.level}}">{{lint.level}}</span> {#+ #}
+                            <span class="label label-lint-level label-lint-level-{{lint.level}}">{{lint.level}}</span> {#+ #}
 
-                                    <span class="label label-doc-folding"></span> {# #}
-                                </div> {# #}
-                            </h2> {# #}
-                        </header> {# #}
+                            <span class="label label-doc-folding"></span> {# #}
+                        </h2> {# #}
                     </label> {# #}
 
                     <div class="list-group lint-docs"> {# #}
                         <div class="list-group-item lint-doc-md">{{Self::markdown(lint.docs)}}</div> {# #}
                         <div class="lint-additional-info-container">
                             {# Applicability #}
-                            <div class="lint-additional-info-item"> {# #}
-                                <span> Applicability: </span> {# #}
+                            <div> {# #}
+                                Applicability: {#+ #}
                                 <span class="label label-default label-applicability">{{ lint.applicability_str() }}</span> {# #}
                                 <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint_defs/enum.Applicability.html#variants">(?)</a> {# #}
                             </div>
                             {# Clippy version #}
-                            <div class="lint-additional-info-item"> {# #}
-                                <span>{% if lint.group == "deprecated" %}Deprecated{% else %} Added{% endif +%} in: </span> {# #}
+                            <div> {# #}
+                                {% if lint.group == "deprecated" %}Deprecated{% else %} Added{% endif +%} in: {#+ #}
                                 <span class="label label-default label-version">{{lint.version}}</span> {# #}
                             </div>
                             {# Open related issues #}
-                            <div class="lint-additional-info-item"> {# #}
+                            <div> {# #}
                                 <a href="https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+{{lint.id}}">Related Issues</a> {# #}
                             </div>
 
                             {# Jump to source #}
                             {% if let Some(id_location) = lint.id_location %}
-                                <div class="lint-additional-info-item"> {# #}
+                                <div> {# #}
                                     <a href="https://github.com/rust-lang/rust-clippy/blob/master/{{id_location}}">View Source</a> {# #}
                                 </div>
                             {% endif %}
diff --git a/src/tools/clippy/util/gh-pages/script.js b/src/tools/clippy/util/gh-pages/script.js
index 285aa34e701..ee13f1c0cd8 100644
--- a/src/tools/clippy/util/gh-pages/script.js
+++ b/src/tools/clippy/util/gh-pages/script.js
@@ -554,10 +554,10 @@ function addListeners() {
             return;
         }
 
-        if (event.target.classList.contains("lint-anchor")) {
-            lintAnchor(event);
-        } else if (event.target.classList.contains("copy-to-clipboard")) {
+        if (event.target.classList.contains("copy-to-clipboard")) {
             copyToClipboard(event);
+        } else if (event.target.classList.contains("anchor")) {
+            lintAnchor(event);
         }
     });
 
diff --git a/src/tools/clippy/util/gh-pages/style.css b/src/tools/clippy/util/gh-pages/style.css
index 3cc7a919c23..022ea875200 100644
--- a/src/tools/clippy/util/gh-pages/style.css
+++ b/src/tools/clippy/util/gh-pages/style.css
@@ -50,11 +50,25 @@ div.panel div.panel-body  button.open {
 
 .panel-heading { cursor: pointer; }
 
-.panel-title { display: flex; flex-wrap: wrap;}
-.panel-title .label { display: inline-block; }
+.lint-title {
+    cursor: pointer;
+    margin-top: 0;
+    margin-bottom: 0;
+    font-size: 16px;
+    display: flex;
+    flex-wrap: wrap;
+    background: var(--theme-hover);
+    color: var(--fg);
+    border: 1px solid var(--theme-popup-border);
+    padding: 10px 15px;
+    border-top-left-radius: 3px;
+    border-top-right-radius: 3px;
+    gap: 4px;
+}
+
+.lint-title .label { display: inline-block; }
 
 .panel-title-name { flex: 1; min-width: 400px;}
-.panel-title-name span { vertical-align: bottom; }
 
 .panel .panel-title-name .anchor { display: none; }
 .panel:hover .panel-title-name .anchor { display: inline;}
@@ -147,7 +161,7 @@ div.panel div.panel-body  button.open {
         display: flex;
         flex-flow: column;
     }
-    .lint-additional-info-item + .lint-additional-info-item {
+    .lint-additional-info-container > div + div {
         border-top: 1px solid var(--theme-popup-border);
     }
 }
@@ -156,12 +170,12 @@ div.panel div.panel-body  button.open {
         display: flex;
         flex-flow: row;
     }
-    .lint-additional-info-item + .lint-additional-info-item {
+    .lint-additional-info-container > div + div {
         border-left: 1px solid var(--theme-popup-border);
     }
 }
 
-.lint-additional-info-item {
+.lint-additional-info-container > div {
     display: inline-flex;
     min-width: 200px;
     flex-grow: 1;
diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs
index 7122746fa87..33da1a25db1 100644
--- a/src/tools/compiletest/src/common.rs
+++ b/src/tools/compiletest/src/common.rs
@@ -1,63 +1,20 @@
 use std::collections::{BTreeSet, HashMap, HashSet};
+use std::iter;
 use std::process::Command;
-use std::str::FromStr;
 use std::sync::OnceLock;
-use std::{fmt, iter};
 
 use build_helper::git::GitConfig;
 use camino::{Utf8Path, Utf8PathBuf};
 use semver::Version;
 use serde::de::{Deserialize, Deserializer, Error as _};
 
-pub use self::Mode::*;
 use crate::executor::{ColorConfig, OutputFormat};
 use crate::fatal;
-use crate::util::{Utf8PathBufExt, add_dylib_path};
-
-macro_rules! string_enum {
-    ($(#[$meta:meta])* $vis:vis enum $name:ident { $($variant:ident => $repr:expr,)* }) => {
-        $(#[$meta])*
-        $vis enum $name {
-            $($variant,)*
-        }
-
-        impl $name {
-            $vis const VARIANTS: &'static [Self] = &[$(Self::$variant,)*];
-            $vis const STR_VARIANTS: &'static [&'static str] = &[$(Self::$variant.to_str(),)*];
-
-            $vis const fn to_str(&self) -> &'static str {
-                match self {
-                    $(Self::$variant => $repr,)*
-                }
-            }
-        }
-
-        impl fmt::Display for $name {
-            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-                fmt::Display::fmt(self.to_str(), f)
-            }
-        }
-
-        impl FromStr for $name {
-            type Err = String;
-
-            fn from_str(s: &str) -> Result<Self, Self::Err> {
-                match s {
-                    $($repr => Ok(Self::$variant),)*
-                    _ => Err(format!(concat!("unknown `", stringify!($name), "` variant: `{}`"), s)),
-                }
-            }
-        }
-    }
-}
-
-// Make the macro visible outside of this module, for tests.
-#[cfg(test)]
-pub(crate) use string_enum;
+use crate::util::{Utf8PathBufExt, add_dylib_path, string_enum};
 
 string_enum! {
     #[derive(Clone, Copy, PartialEq, Debug)]
-    pub enum Mode {
+    pub enum TestMode {
         Pretty => "pretty",
         DebugInfo => "debuginfo",
         Codegen => "codegen",
@@ -76,18 +33,12 @@ string_enum! {
     }
 }
 
-impl Default for Mode {
-    fn default() -> Self {
-        Mode::Ui
-    }
-}
-
-impl Mode {
+impl TestMode {
     pub fn aux_dir_disambiguator(self) -> &'static str {
         // Pretty-printing tests could run concurrently, and if they do,
         // they need to keep their output segregated.
         match self {
-            Pretty => ".pretty",
+            TestMode::Pretty => ".pretty",
             _ => "",
         }
     }
@@ -96,12 +47,38 @@ impl Mode {
         // Coverage tests use the same test files for multiple test modes,
         // so each mode should have a separate output directory.
         match self {
-            CoverageMap | CoverageRun => self.to_str(),
+            TestMode::CoverageMap | TestMode::CoverageRun => self.to_str(),
             _ => "",
         }
     }
 }
 
+// Note that coverage tests use the same test files for multiple test modes.
+string_enum! {
+    #[derive(Clone, Copy, PartialEq, Debug)]
+    pub enum TestSuite {
+        Assembly => "assembly",
+        Codegen => "codegen",
+        CodegenUnits => "codegen-units",
+        Coverage => "coverage",
+        CoverageRunRustdoc => "coverage-run-rustdoc",
+        Crashes => "crashes",
+        Debuginfo => "debuginfo",
+        Incremental => "incremental",
+        MirOpt => "mir-opt",
+        Pretty => "pretty",
+        RunMake => "run-make",
+        Rustdoc => "rustdoc",
+        RustdocGui => "rustdoc-gui",
+        RustdocJs => "rustdoc-js",
+        RustdocJsStd=> "rustdoc-js-std",
+        RustdocJson => "rustdoc-json",
+        RustdocUi => "rustdoc-ui",
+        Ui => "ui",
+        UiFullDeps => "ui-fulldeps",
+    }
+}
+
 string_enum! {
     #[derive(Clone, Copy, PartialEq, Debug, Hash)]
     pub enum PassMode {
@@ -193,9 +170,9 @@ pub enum Sanitizer {
 ///
 /// FIXME: audit these options to make sure we are not hashing less than necessary for build stamp
 /// (for changed test detection).
-#[derive(Debug, Default, Clone)]
+#[derive(Debug, Clone)]
 pub struct Config {
-    /// Some test [`Mode`]s support [snapshot testing], where a *reference snapshot* of outputs (of
+    /// Some [`TestMode`]s support [snapshot testing], where a *reference snapshot* of outputs (of
     /// `stdout`, `stderr`, or other form of artifacts) can be compared to the *actual output*.
     ///
     /// This option can be set to `true` to update the *reference snapshots* in-place, otherwise
@@ -317,23 +294,21 @@ pub struct Config {
     /// FIXME: reconsider this string; this is hashed for test build stamp.
     pub stage_id: String,
 
-    /// The test [`Mode`]. E.g. [`Mode::Ui`]. Each test mode can correspond to one or more test
+    /// The [`TestMode`]. E.g. [`TestMode::Ui`]. Each test mode can correspond to one or more test
     /// suites.
     ///
     /// FIXME: stop using stringly-typed test suites!
-    pub mode: Mode,
+    pub mode: TestMode,
 
     /// The test suite.
     ///
-    /// Example: `tests/ui/` is the "UI" test *suite*, which happens to also be of the [`Mode::Ui`]
-    /// test *mode*.
-    ///
-    /// Note that the same test directory (e.g. `tests/coverage/`) may correspond to multiple test
-    /// modes, e.g. `tests/coverage/` can be run under both [`Mode::CoverageRun`] and
-    /// [`Mode::CoverageMap`].
+    /// Example: `tests/ui/` is [`TestSuite::Ui`] test *suite*, which happens to also be of the
+    /// [`TestMode::Ui`] test *mode*.
     ///
-    /// FIXME: stop using stringly-typed test suites!
-    pub suite: String,
+    /// Note that the same test suite (e.g. `tests/coverage/`) may correspond to multiple test
+    /// modes, e.g. `tests/coverage/` can be run under both [`TestMode::CoverageRun`] and
+    /// [`TestMode::CoverageMap`].
+    pub suite: TestSuite,
 
     /// When specified, **only** the specified [`Debugger`] will be used to run against the
     /// `tests/debuginfo` test suite. When unspecified, `compiletest` will attempt to find all three
@@ -653,6 +628,108 @@ pub struct Config {
 }
 
 impl Config {
+    /// Incomplete config intended for `src/tools/rustdoc-gui-test` **only** as
+    /// `src/tools/rustdoc-gui-test` wants to reuse `compiletest`'s directive -> test property
+    /// handling for `//@ {compile,run}-flags`, do not use for any other purpose.
+    ///
+    /// FIXME(#143827): this setup feels very hacky. It so happens that `tests/rustdoc-gui/`
+    /// **only** uses `//@ {compile,run}-flags` for now and not any directives that actually rely on
+    /// info that is assumed available in a fully populated [`Config`].
+    pub fn incomplete_for_rustdoc_gui_test() -> Config {
+        // FIXME(#143827): spelling this out intentionally, because this is questionable.
+        //
+        // For instance, `//@ ignore-stage1` will not work at all.
+        Config {
+            mode: TestMode::Rustdoc,
+            // E.g. this has no sensible default tbh.
+            suite: TestSuite::Ui,
+
+            // Dummy values.
+            edition: Default::default(),
+            bless: Default::default(),
+            fail_fast: Default::default(),
+            compile_lib_path: Utf8PathBuf::default(),
+            run_lib_path: Utf8PathBuf::default(),
+            rustc_path: Utf8PathBuf::default(),
+            cargo_path: Default::default(),
+            stage0_rustc_path: Default::default(),
+            rustdoc_path: Default::default(),
+            coverage_dump_path: Default::default(),
+            python: Default::default(),
+            jsondocck_path: Default::default(),
+            jsondoclint_path: Default::default(),
+            llvm_filecheck: Default::default(),
+            llvm_bin_dir: Default::default(),
+            run_clang_based_tests_with: Default::default(),
+            src_root: Utf8PathBuf::default(),
+            src_test_suite_root: Utf8PathBuf::default(),
+            build_root: Utf8PathBuf::default(),
+            build_test_suite_root: Utf8PathBuf::default(),
+            sysroot_base: Utf8PathBuf::default(),
+            stage: Default::default(),
+            stage_id: String::default(),
+            debugger: Default::default(),
+            run_ignored: Default::default(),
+            with_rustc_debug_assertions: Default::default(),
+            with_std_debug_assertions: Default::default(),
+            filters: Default::default(),
+            skip: Default::default(),
+            filter_exact: Default::default(),
+            force_pass_mode: Default::default(),
+            run: Default::default(),
+            runner: Default::default(),
+            host_rustcflags: Default::default(),
+            target_rustcflags: Default::default(),
+            rust_randomized_layout: Default::default(),
+            optimize_tests: Default::default(),
+            target: Default::default(),
+            host: Default::default(),
+            cdb: Default::default(),
+            cdb_version: Default::default(),
+            gdb: Default::default(),
+            gdb_version: Default::default(),
+            lldb_version: Default::default(),
+            llvm_version: Default::default(),
+            system_llvm: Default::default(),
+            android_cross_path: Default::default(),
+            adb_path: Default::default(),
+            adb_test_dir: Default::default(),
+            adb_device_status: Default::default(),
+            lldb_python_dir: Default::default(),
+            verbose: Default::default(),
+            format: Default::default(),
+            color: Default::default(),
+            remote_test_client: Default::default(),
+            compare_mode: Default::default(),
+            rustfix_coverage: Default::default(),
+            has_html_tidy: Default::default(),
+            has_enzyme: Default::default(),
+            channel: Default::default(),
+            git_hash: Default::default(),
+            cc: Default::default(),
+            cxx: Default::default(),
+            cflags: Default::default(),
+            cxxflags: Default::default(),
+            ar: Default::default(),
+            target_linker: Default::default(),
+            host_linker: Default::default(),
+            llvm_components: Default::default(),
+            nodejs: Default::default(),
+            npm: Default::default(),
+            force_rerun: Default::default(),
+            only_modified: Default::default(),
+            target_cfgs: Default::default(),
+            builtin_cfg_names: Default::default(),
+            supported_crate_types: Default::default(),
+            nocapture: Default::default(),
+            nightly_branch: Default::default(),
+            git_merge_commit_email: Default::default(),
+            profiler_runtime: Default::default(),
+            diff_command: Default::default(),
+            minicore_path: Default::default(),
+        }
+    }
+
     /// FIXME: this run scheme is... confusing.
     pub fn run_enabled(&self) -> bool {
         self.run.unwrap_or_else(|| {
diff --git a/src/tools/compiletest/src/debuggers.rs b/src/tools/compiletest/src/debuggers.rs
index 0edc3d82d4f..8afe3289fa4 100644
--- a/src/tools/compiletest/src/debuggers.rs
+++ b/src/tools/compiletest/src/debuggers.rs
@@ -51,17 +51,6 @@ pub(crate) fn configure_gdb(config: &Config) -> Option<Arc<Config>> {
 pub(crate) fn configure_lldb(config: &Config) -> Option<Arc<Config>> {
     config.lldb_python_dir.as_ref()?;
 
-    // FIXME: this is super old
-    if let Some(350) = config.lldb_version {
-        println!(
-            "WARNING: The used version of LLDB (350) has a \
-             known issue that breaks debuginfo tests. See \
-             issue #32520 for more information. Skipping all \
-             LLDB-based tests!",
-        );
-        return None;
-    }
-
     Some(Arc::new(Config { debugger: Some(Debugger::Lldb), ..config.clone() }))
 }
 
diff --git a/src/tools/compiletest/src/directive-list.rs b/src/tools/compiletest/src/directive-list.rs
deleted file mode 100644
index adf2a7bffef..00000000000
--- a/src/tools/compiletest/src/directive-list.rs
+++ /dev/null
@@ -1,260 +0,0 @@
-/// This was originally generated by collecting directives from ui tests and then extracting their
-/// directive names. This is **not** an exhaustive list of all possible directives. Instead, this is
-/// a best-effort approximation for diagnostics. Add new directives to this list when needed.
-const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
-    // tidy-alphabetical-start
-    "add-core-stubs",
-    "assembly-output",
-    "aux-bin",
-    "aux-build",
-    "aux-codegen-backend",
-    "aux-crate",
-    "build-aux-docs",
-    "build-fail",
-    "build-pass",
-    "check-fail",
-    "check-pass",
-    "check-run-results",
-    "check-stdout",
-    "check-test-line-numbers-match",
-    "compile-flags",
-    "doc-flags",
-    "dont-check-compiler-stderr",
-    "dont-check-compiler-stdout",
-    "dont-check-failure-status",
-    "dont-require-annotations",
-    "edition",
-    "error-pattern",
-    "exact-llvm-major-version",
-    "exec-env",
-    "failure-status",
-    "filecheck-flags",
-    "forbid-output",
-    "force-host",
-    "ignore-16bit",
-    "ignore-32bit",
-    "ignore-64bit",
-    "ignore-aarch64",
-    "ignore-aarch64-pc-windows-msvc",
-    "ignore-aarch64-unknown-linux-gnu",
-    "ignore-aix",
-    "ignore-android",
-    "ignore-apple",
-    "ignore-arm",
-    "ignore-arm-unknown-linux-gnueabi",
-    "ignore-arm-unknown-linux-gnueabihf",
-    "ignore-arm-unknown-linux-musleabi",
-    "ignore-arm-unknown-linux-musleabihf",
-    "ignore-auxiliary",
-    "ignore-avr",
-    "ignore-beta",
-    "ignore-cdb",
-    "ignore-compare-mode-next-solver",
-    "ignore-compare-mode-polonius",
-    "ignore-coverage-map",
-    "ignore-coverage-run",
-    "ignore-cross-compile",
-    "ignore-eabi",
-    "ignore-elf",
-    "ignore-emscripten",
-    "ignore-endian-big",
-    "ignore-enzyme",
-    "ignore-freebsd",
-    "ignore-fuchsia",
-    "ignore-gdb",
-    "ignore-gdb-version",
-    "ignore-gnu",
-    "ignore-haiku",
-    "ignore-horizon",
-    "ignore-i686-pc-windows-gnu",
-    "ignore-i686-pc-windows-msvc",
-    "ignore-illumos",
-    "ignore-ios",
-    "ignore-linux",
-    "ignore-lldb",
-    "ignore-llvm-version",
-    "ignore-loongarch32",
-    "ignore-loongarch64",
-    "ignore-macabi",
-    "ignore-macos",
-    "ignore-msp430",
-    "ignore-msvc",
-    "ignore-musl",
-    "ignore-netbsd",
-    "ignore-nightly",
-    "ignore-none",
-    "ignore-nto",
-    "ignore-nvptx64",
-    "ignore-nvptx64-nvidia-cuda",
-    "ignore-openbsd",
-    "ignore-pass",
-    "ignore-powerpc",
-    "ignore-remote",
-    "ignore-riscv64",
-    "ignore-rustc-debug-assertions",
-    "ignore-rustc_abi-x86-sse2",
-    "ignore-s390x",
-    "ignore-sgx",
-    "ignore-sparc64",
-    "ignore-spirv",
-    "ignore-stable",
-    "ignore-stage1",
-    "ignore-stage2",
-    "ignore-std-debug-assertions",
-    "ignore-test",
-    "ignore-thumb",
-    "ignore-thumbv8m.base-none-eabi",
-    "ignore-thumbv8m.main-none-eabi",
-    "ignore-tvos",
-    "ignore-unix",
-    "ignore-unknown",
-    "ignore-uwp",
-    "ignore-visionos",
-    "ignore-vxworks",
-    "ignore-wasi",
-    "ignore-wasm",
-    "ignore-wasm32",
-    "ignore-wasm32-bare",
-    "ignore-wasm64",
-    "ignore-watchos",
-    "ignore-windows",
-    "ignore-windows-gnu",
-    "ignore-windows-msvc",
-    "ignore-x32",
-    "ignore-x86",
-    "ignore-x86_64",
-    "ignore-x86_64-apple-darwin",
-    "ignore-x86_64-pc-windows-gnu",
-    "ignore-x86_64-unknown-linux-gnu",
-    "incremental",
-    "known-bug",
-    "llvm-cov-flags",
-    "max-llvm-major-version",
-    "min-cdb-version",
-    "min-gdb-version",
-    "min-lldb-version",
-    "min-llvm-version",
-    "min-system-llvm-version",
-    "needs-asm-support",
-    "needs-crate-type",
-    "needs-deterministic-layouts",
-    "needs-dlltool",
-    "needs-dynamic-linking",
-    "needs-enzyme",
-    "needs-force-clang-based-tests",
-    "needs-git-hash",
-    "needs-llvm-components",
-    "needs-llvm-zstd",
-    "needs-profiler-runtime",
-    "needs-relocation-model-pic",
-    "needs-run-enabled",
-    "needs-rust-lld",
-    "needs-rustc-debug-assertions",
-    "needs-sanitizer-address",
-    "needs-sanitizer-cfi",
-    "needs-sanitizer-dataflow",
-    "needs-sanitizer-hwaddress",
-    "needs-sanitizer-kcfi",
-    "needs-sanitizer-leak",
-    "needs-sanitizer-memory",
-    "needs-sanitizer-memtag",
-    "needs-sanitizer-safestack",
-    "needs-sanitizer-shadow-call-stack",
-    "needs-sanitizer-support",
-    "needs-sanitizer-thread",
-    "needs-std-debug-assertions",
-    "needs-subprocess",
-    "needs-symlink",
-    "needs-target-has-atomic",
-    "needs-target-std",
-    "needs-threads",
-    "needs-unwind",
-    "needs-wasmtime",
-    "needs-xray",
-    "no-auto-check-cfg",
-    "no-prefer-dynamic",
-    "normalize-stderr",
-    "normalize-stderr-32bit",
-    "normalize-stderr-64bit",
-    "normalize-stdout",
-    "only-16bit",
-    "only-32bit",
-    "only-64bit",
-    "only-aarch64",
-    "only-aarch64-apple-darwin",
-    "only-aarch64-unknown-linux-gnu",
-    "only-apple",
-    "only-arm",
-    "only-avr",
-    "only-beta",
-    "only-bpf",
-    "only-cdb",
-    "only-dist",
-    "only-elf",
-    "only-emscripten",
-    "only-gnu",
-    "only-i686-pc-windows-gnu",
-    "only-i686-pc-windows-msvc",
-    "only-i686-unknown-linux-gnu",
-    "only-ios",
-    "only-linux",
-    "only-loongarch32",
-    "only-loongarch64",
-    "only-loongarch64-unknown-linux-gnu",
-    "only-macos",
-    "only-mips",
-    "only-mips64",
-    "only-msp430",
-    "only-msvc",
-    "only-nightly",
-    "only-nvptx64",
-    "only-powerpc",
-    "only-riscv64",
-    "only-rustc_abi-x86-sse2",
-    "only-s390x",
-    "only-sparc",
-    "only-sparc64",
-    "only-stable",
-    "only-thumb",
-    "only-tvos",
-    "only-unix",
-    "only-visionos",
-    "only-wasm32",
-    "only-wasm32-bare",
-    "only-wasm32-wasip1",
-    "only-watchos",
-    "only-windows",
-    "only-windows-gnu",
-    "only-windows-msvc",
-    "only-x86",
-    "only-x86_64",
-    "only-x86_64-apple-darwin",
-    "only-x86_64-fortanix-unknown-sgx",
-    "only-x86_64-pc-windows-gnu",
-    "only-x86_64-pc-windows-msvc",
-    "only-x86_64-unknown-linux-gnu",
-    "pp-exact",
-    "pretty-compare-only",
-    "pretty-mode",
-    "proc-macro",
-    "reference",
-    "regex-error-pattern",
-    "remap-src-base",
-    "revisions",
-    "run-fail",
-    "run-flags",
-    "run-pass",
-    "run-rustfix",
-    "rustc-env",
-    "rustfix-only-machine-applicable",
-    "should-fail",
-    "should-ice",
-    "stderr-per-bitwidth",
-    "test-mir-pass",
-    "unique-doc-out-dir",
-    "unset-exec-env",
-    "unset-rustc-env",
-    // Used by the tidy check `unknown_revision`.
-    "unused-revision-names",
-    // tidy-alphabetical-end
-];
diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs
index a6242cf0c22..93133ea0bfd 100644
--- a/src/tools/compiletest/src/directives.rs
+++ b/src/tools/compiletest/src/directives.rs
@@ -9,7 +9,7 @@ use camino::{Utf8Path, Utf8PathBuf};
 use semver::Version;
 use tracing::*;
 
-use crate::common::{Config, Debugger, FailMode, Mode, PassMode};
+use crate::common::{Config, Debugger, FailMode, PassMode, TestMode};
 use crate::debuggers::{extract_cdb_version, extract_gdb_version};
 use crate::directives::auxiliary::{AuxProps, parse_and_update_aux};
 use crate::directives::needs::CachedNeedsConditions;
@@ -56,7 +56,6 @@ impl EarlyProps {
         let mut poisoned = false;
         iter_directives(
             config.mode,
-            &config.suite,
             &mut poisoned,
             testfile,
             rdr,
@@ -328,7 +327,7 @@ impl TestProps {
         props.exec_env.push(("RUSTC".to_string(), config.rustc_path.to_string()));
 
         match (props.pass_mode, props.fail_mode) {
-            (None, None) if config.mode == Mode::Ui => props.fail_mode = Some(FailMode::Check),
+            (None, None) if config.mode == TestMode::Ui => props.fail_mode = Some(FailMode::Check),
             (Some(_), Some(_)) => panic!("cannot use a *-fail and *-pass mode together"),
             _ => {}
         }
@@ -349,7 +348,6 @@ impl TestProps {
 
             iter_directives(
                 config.mode,
-                &config.suite,
                 &mut poisoned,
                 testfile,
                 file,
@@ -609,11 +607,11 @@ impl TestProps {
             self.failure_status = Some(101);
         }
 
-        if config.mode == Mode::Incremental {
+        if config.mode == TestMode::Incremental {
             self.incremental = true;
         }
 
-        if config.mode == Mode::Crashes {
+        if config.mode == TestMode::Crashes {
             // we don't want to pollute anything with backtrace-files
             // also turn off backtraces in order to save some execution
             // time on the tests; we only need to know IF it crashes
@@ -641,11 +639,11 @@ impl TestProps {
     fn update_fail_mode(&mut self, ln: &str, config: &Config) {
         let check_ui = |mode: &str| {
             // Mode::Crashes may need build-fail in order to trigger llvm errors or stack overflows
-            if config.mode != Mode::Ui && config.mode != Mode::Crashes {
+            if config.mode != TestMode::Ui && config.mode != TestMode::Crashes {
                 panic!("`{}-fail` directive is only supported in UI tests", mode);
             }
         };
-        if config.mode == Mode::Ui && config.parse_name_directive(ln, "compile-fail") {
+        if config.mode == TestMode::Ui && config.parse_name_directive(ln, "compile-fail") {
             panic!("`compile-fail` directive is useless in UI tests");
         }
         let fail_mode = if config.parse_name_directive(ln, "check-fail") {
@@ -669,10 +667,10 @@ impl TestProps {
 
     fn update_pass_mode(&mut self, ln: &str, revision: Option<&str>, config: &Config) {
         let check_no_run = |s| match (config.mode, s) {
-            (Mode::Ui, _) => (),
-            (Mode::Crashes, _) => (),
-            (Mode::Codegen, "build-pass") => (),
-            (Mode::Incremental, _) => {
+            (TestMode::Ui, _) => (),
+            (TestMode::Crashes, _) => (),
+            (TestMode::Codegen, "build-pass") => (),
+            (TestMode::Incremental, _) => {
                 if revision.is_some() && !self.revisions.iter().all(|r| r.starts_with("cfail")) {
                     panic!("`{s}` directive is only supported in `cfail` incremental tests")
                 }
@@ -715,7 +713,7 @@ impl TestProps {
     pub fn update_add_core_stubs(&mut self, ln: &str, config: &Config) {
         let add_core_stubs = config.parse_name_directive(ln, directives::ADD_CORE_STUBS);
         if add_core_stubs {
-            if !matches!(config.mode, Mode::Ui | Mode::Codegen | Mode::Assembly) {
+            if !matches!(config.mode, TestMode::Ui | TestMode::Codegen | TestMode::Assembly) {
                 panic!(
                     "`add-core-stubs` is currently only supported for ui, codegen and assembly test modes"
                 );
@@ -765,11 +763,267 @@ fn line_directive<'line>(
     Some(DirectiveLine { line_number, revision, raw_directive })
 }
 
-// To prevent duplicating the list of directives between `compiletest`,`htmldocck` and `jsondocck`,
-// we put it into a common file which is included in rust code and parsed here.
-// FIXME: This setup is temporary until we figure out how to improve this situation.
-//        See <https://github.com/rust-lang/rust/issues/125813#issuecomment-2141953780>.
-include!("directive-list.rs");
+/// This was originally generated by collecting directives from ui tests and then extracting their
+/// directive names. This is **not** an exhaustive list of all possible directives. Instead, this is
+/// a best-effort approximation for diagnostics. Add new directives to this list when needed.
+const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
+    // tidy-alphabetical-start
+    "add-core-stubs",
+    "assembly-output",
+    "aux-bin",
+    "aux-build",
+    "aux-codegen-backend",
+    "aux-crate",
+    "build-aux-docs",
+    "build-fail",
+    "build-pass",
+    "check-fail",
+    "check-pass",
+    "check-run-results",
+    "check-stdout",
+    "check-test-line-numbers-match",
+    "compile-flags",
+    "doc-flags",
+    "dont-check-compiler-stderr",
+    "dont-check-compiler-stdout",
+    "dont-check-failure-status",
+    "dont-require-annotations",
+    "edition",
+    "error-pattern",
+    "exact-llvm-major-version",
+    "exec-env",
+    "failure-status",
+    "filecheck-flags",
+    "forbid-output",
+    "force-host",
+    "ignore-16bit",
+    "ignore-32bit",
+    "ignore-64bit",
+    "ignore-aarch64",
+    "ignore-aarch64-pc-windows-msvc",
+    "ignore-aarch64-unknown-linux-gnu",
+    "ignore-aix",
+    "ignore-android",
+    "ignore-apple",
+    "ignore-arm",
+    "ignore-arm-unknown-linux-gnueabi",
+    "ignore-arm-unknown-linux-gnueabihf",
+    "ignore-arm-unknown-linux-musleabi",
+    "ignore-arm-unknown-linux-musleabihf",
+    "ignore-auxiliary",
+    "ignore-avr",
+    "ignore-beta",
+    "ignore-cdb",
+    "ignore-compare-mode-next-solver",
+    "ignore-compare-mode-polonius",
+    "ignore-coverage-map",
+    "ignore-coverage-run",
+    "ignore-cross-compile",
+    "ignore-eabi",
+    "ignore-elf",
+    "ignore-emscripten",
+    "ignore-endian-big",
+    "ignore-enzyme",
+    "ignore-freebsd",
+    "ignore-fuchsia",
+    "ignore-gdb",
+    "ignore-gdb-version",
+    "ignore-gnu",
+    "ignore-haiku",
+    "ignore-horizon",
+    "ignore-i686-pc-windows-gnu",
+    "ignore-i686-pc-windows-msvc",
+    "ignore-illumos",
+    "ignore-ios",
+    "ignore-linux",
+    "ignore-lldb",
+    "ignore-llvm-version",
+    "ignore-loongarch32",
+    "ignore-loongarch64",
+    "ignore-macabi",
+    "ignore-macos",
+    "ignore-msp430",
+    "ignore-msvc",
+    "ignore-musl",
+    "ignore-netbsd",
+    "ignore-nightly",
+    "ignore-none",
+    "ignore-nto",
+    "ignore-nvptx64",
+    "ignore-nvptx64-nvidia-cuda",
+    "ignore-openbsd",
+    "ignore-pass",
+    "ignore-powerpc",
+    "ignore-remote",
+    "ignore-riscv64",
+    "ignore-rustc-debug-assertions",
+    "ignore-rustc_abi-x86-sse2",
+    "ignore-s390x",
+    "ignore-sgx",
+    "ignore-sparc64",
+    "ignore-spirv",
+    "ignore-stable",
+    "ignore-stage1",
+    "ignore-stage2",
+    "ignore-std-debug-assertions",
+    "ignore-test",
+    "ignore-thumb",
+    "ignore-thumbv8m.base-none-eabi",
+    "ignore-thumbv8m.main-none-eabi",
+    "ignore-tvos",
+    "ignore-unix",
+    "ignore-unknown",
+    "ignore-uwp",
+    "ignore-visionos",
+    "ignore-vxworks",
+    "ignore-wasi",
+    "ignore-wasm",
+    "ignore-wasm32",
+    "ignore-wasm32-bare",
+    "ignore-wasm64",
+    "ignore-watchos",
+    "ignore-windows",
+    "ignore-windows-gnu",
+    "ignore-windows-msvc",
+    "ignore-x32",
+    "ignore-x86",
+    "ignore-x86_64",
+    "ignore-x86_64-apple-darwin",
+    "ignore-x86_64-pc-windows-gnu",
+    "ignore-x86_64-unknown-linux-gnu",
+    "incremental",
+    "known-bug",
+    "llvm-cov-flags",
+    "max-llvm-major-version",
+    "min-cdb-version",
+    "min-gdb-version",
+    "min-lldb-version",
+    "min-llvm-version",
+    "min-system-llvm-version",
+    "needs-asm-support",
+    "needs-crate-type",
+    "needs-deterministic-layouts",
+    "needs-dlltool",
+    "needs-dynamic-linking",
+    "needs-enzyme",
+    "needs-force-clang-based-tests",
+    "needs-git-hash",
+    "needs-llvm-components",
+    "needs-llvm-zstd",
+    "needs-profiler-runtime",
+    "needs-relocation-model-pic",
+    "needs-run-enabled",
+    "needs-rust-lld",
+    "needs-rustc-debug-assertions",
+    "needs-sanitizer-address",
+    "needs-sanitizer-cfi",
+    "needs-sanitizer-dataflow",
+    "needs-sanitizer-hwaddress",
+    "needs-sanitizer-kcfi",
+    "needs-sanitizer-leak",
+    "needs-sanitizer-memory",
+    "needs-sanitizer-memtag",
+    "needs-sanitizer-safestack",
+    "needs-sanitizer-shadow-call-stack",
+    "needs-sanitizer-support",
+    "needs-sanitizer-thread",
+    "needs-std-debug-assertions",
+    "needs-subprocess",
+    "needs-symlink",
+    "needs-target-has-atomic",
+    "needs-target-std",
+    "needs-threads",
+    "needs-unwind",
+    "needs-wasmtime",
+    "needs-xray",
+    "no-auto-check-cfg",
+    "no-prefer-dynamic",
+    "normalize-stderr",
+    "normalize-stderr-32bit",
+    "normalize-stderr-64bit",
+    "normalize-stdout",
+    "only-16bit",
+    "only-32bit",
+    "only-64bit",
+    "only-aarch64",
+    "only-aarch64-apple-darwin",
+    "only-aarch64-unknown-linux-gnu",
+    "only-apple",
+    "only-arm",
+    "only-avr",
+    "only-beta",
+    "only-bpf",
+    "only-cdb",
+    "only-dist",
+    "only-elf",
+    "only-emscripten",
+    "only-gnu",
+    "only-i686-pc-windows-gnu",
+    "only-i686-pc-windows-msvc",
+    "only-i686-unknown-linux-gnu",
+    "only-ios",
+    "only-linux",
+    "only-loongarch32",
+    "only-loongarch64",
+    "only-loongarch64-unknown-linux-gnu",
+    "only-macos",
+    "only-mips",
+    "only-mips64",
+    "only-msp430",
+    "only-msvc",
+    "only-musl",
+    "only-nightly",
+    "only-nvptx64",
+    "only-powerpc",
+    "only-riscv64",
+    "only-rustc_abi-x86-sse2",
+    "only-s390x",
+    "only-sparc",
+    "only-sparc64",
+    "only-stable",
+    "only-thumb",
+    "only-tvos",
+    "only-unix",
+    "only-visionos",
+    "only-wasm32",
+    "only-wasm32-bare",
+    "only-wasm32-wasip1",
+    "only-watchos",
+    "only-windows",
+    "only-windows-gnu",
+    "only-windows-msvc",
+    "only-x86",
+    "only-x86_64",
+    "only-x86_64-apple-darwin",
+    "only-x86_64-fortanix-unknown-sgx",
+    "only-x86_64-pc-windows-gnu",
+    "only-x86_64-pc-windows-msvc",
+    "only-x86_64-unknown-linux-gnu",
+    "pp-exact",
+    "pretty-compare-only",
+    "pretty-mode",
+    "proc-macro",
+    "reference",
+    "regex-error-pattern",
+    "remap-src-base",
+    "revisions",
+    "run-fail",
+    "run-flags",
+    "run-pass",
+    "run-rustfix",
+    "rustc-env",
+    "rustfix-only-machine-applicable",
+    "should-fail",
+    "should-ice",
+    "stderr-per-bitwidth",
+    "test-mir-pass",
+    "unique-doc-out-dir",
+    "unset-exec-env",
+    "unset-rustc-env",
+    // Used by the tidy check `unknown_revision`.
+    "unused-revision-names",
+    // tidy-alphabetical-end
+];
 
 const KNOWN_HTMLDOCCK_DIRECTIVE_NAMES: &[&str] = &[
     "count",
@@ -833,43 +1087,33 @@ pub(crate) struct CheckDirectiveResult<'ln> {
 
 pub(crate) fn check_directive<'a>(
     directive_ln: &'a str,
-    mode: Mode,
-    original_line: &str,
+    mode: TestMode,
 ) -> CheckDirectiveResult<'a> {
     let (directive_name, post) = directive_ln.split_once([':', ' ']).unwrap_or((directive_ln, ""));
 
+    let is_known_directive = KNOWN_DIRECTIVE_NAMES.contains(&directive_name)
+        || match mode {
+            TestMode::Rustdoc => KNOWN_HTMLDOCCK_DIRECTIVE_NAMES.contains(&directive_name),
+            TestMode::RustdocJson => KNOWN_JSONDOCCK_DIRECTIVE_NAMES.contains(&directive_name),
+            _ => false,
+        };
+
     let trailing = post.trim().split_once(' ').map(|(pre, _)| pre).unwrap_or(post);
-    let is_known = |s: &str| {
-        KNOWN_DIRECTIVE_NAMES.contains(&s)
-            || match mode {
-                Mode::Rustdoc | Mode::RustdocJson => {
-                    original_line.starts_with("//@")
-                        && match mode {
-                            Mode::Rustdoc => KNOWN_HTMLDOCCK_DIRECTIVE_NAMES,
-                            Mode::RustdocJson => KNOWN_JSONDOCCK_DIRECTIVE_NAMES,
-                            _ => unreachable!(),
-                        }
-                        .contains(&s)
-                }
-                _ => false,
-            }
-    };
     let trailing_directive = {
         // 1. is the directive name followed by a space? (to exclude `:`)
-        matches!(directive_ln.get(directive_name.len()..), Some(s) if s.starts_with(' '))
+        directive_ln.get(directive_name.len()..).is_some_and(|s| s.starts_with(' '))
             // 2. is what is after that directive also a directive (ex: "only-x86 only-arm")
-            && is_known(trailing)
+            && KNOWN_DIRECTIVE_NAMES.contains(&trailing)
     }
     .then_some(trailing);
 
-    CheckDirectiveResult { is_known_directive: is_known(&directive_name), trailing_directive }
+    CheckDirectiveResult { is_known_directive, trailing_directive }
 }
 
 const COMPILETEST_DIRECTIVE_PREFIX: &str = "//@";
 
 fn iter_directives(
-    mode: Mode,
-    _suite: &str,
+    mode: TestMode,
     poisoned: &mut bool,
     testfile: &Utf8Path,
     rdr: impl Read,
@@ -883,7 +1127,7 @@ fn iter_directives(
     // specify them manually in every test file.
     //
     // FIXME(jieyouxu): I feel like there's a better way to do this, leaving for later.
-    if mode == Mode::CoverageRun {
+    if mode == TestMode::CoverageRun {
         let extra_directives: &[&str] = &[
             "needs-profiler-runtime",
             // FIXME(pietroalbini): this test currently does not work on cross-compiled targets
@@ -914,9 +1158,9 @@ fn iter_directives(
         };
 
         // Perform unknown directive check on Rust files.
-        if testfile.extension().map(|e| e == "rs").unwrap_or(false) {
+        if testfile.extension() == Some("rs") {
             let CheckDirectiveResult { is_known_directive, trailing_directive } =
-                check_directive(directive_line.raw_directive, mode, ln);
+                check_directive(directive_line.raw_directive, mode);
 
             if !is_known_directive {
                 *poisoned = true;
@@ -936,7 +1180,7 @@ fn iter_directives(
                     "{testfile}:{line_number}: detected trailing compiletest test directive `{}`",
                     trailing_directive,
                 );
-                help!("put the trailing directive in it's own line: `//@ {}`", trailing_directive);
+                help!("put the trailing directive in its own line: `//@ {}`", trailing_directive);
 
                 return;
             }
@@ -964,7 +1208,7 @@ impl Config {
             ["CHECK", "COM", "NEXT", "SAME", "EMPTY", "NOT", "COUNT", "DAG", "LABEL"];
 
         if let Some(raw) = self.parse_name_value_directive(line, "revisions") {
-            if self.mode == Mode::RunMake {
+            if self.mode == TestMode::RunMake {
                 panic!("`run-make` tests do not support revisions: {}", testfile);
             }
 
@@ -981,7 +1225,7 @@ impl Config {
                     );
                 }
 
-                if matches!(self.mode, Mode::Assembly | Mode::Codegen | Mode::MirOpt)
+                if matches!(self.mode, TestMode::Assembly | TestMode::Codegen | TestMode::MirOpt)
                     && FILECHECK_FORBIDDEN_REVISION_NAMES.contains(&revision)
                 {
                     panic!(
@@ -1388,7 +1632,6 @@ pub(crate) fn make_test_description<R: Read>(
     // Scan through the test file to handle `ignore-*`, `only-*`, and `needs-*` directives.
     iter_directives(
         config.mode,
-        &config.suite,
         &mut local_poisoned,
         path,
         src,
@@ -1443,7 +1686,7 @@ pub(crate) fn make_test_description<R: Read>(
     // since we run the pretty printer across all tests by default.
     // If desired, we could add a `should-fail-pretty` annotation.
     let should_panic = match config.mode {
-        crate::common::Pretty => ShouldPanic::No,
+        TestMode::Pretty => ShouldPanic::No,
         _ if should_fail => ShouldPanic::Yes,
         _ => ShouldPanic::No,
     };
diff --git a/src/tools/compiletest/src/directives/tests.rs b/src/tools/compiletest/src/directives/tests.rs
index d4570f82677..5682cc57b6f 100644
--- a/src/tools/compiletest/src/directives/tests.rs
+++ b/src/tools/compiletest/src/directives/tests.rs
@@ -7,7 +7,7 @@ use super::{
     DirectivesCache, EarlyProps, extract_llvm_version, extract_version_range, iter_directives,
     parse_normalize_rule,
 };
-use crate::common::{Config, Debugger, Mode};
+use crate::common::{Config, Debugger, TestMode};
 use crate::executor::{CollectedTestDesc, ShouldPanic};
 
 fn make_test_description<R: Read>(
@@ -785,7 +785,7 @@ fn threads_support() {
 
 fn run_path(poisoned: &mut bool, path: &Utf8Path, buf: &[u8]) {
     let rdr = std::io::Cursor::new(&buf);
-    iter_directives(Mode::Ui, "ui", poisoned, path, rdr, &mut |_| {});
+    iter_directives(TestMode::Ui, poisoned, path, rdr, &mut |_| {});
 }
 
 #[test]
diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs
index 9819079e284..f3b3605a120 100644
--- a/src/tools/compiletest/src/lib.rs
+++ b/src/tools/compiletest/src/lib.rs
@@ -39,8 +39,8 @@ use walkdir::WalkDir;
 
 use self::directives::{EarlyProps, make_test_description};
 use crate::common::{
-    CompareMode, Config, Debugger, Mode, PassMode, TestPaths, UI_EXTENSIONS, expected_output_path,
-    output_base_dir, output_relative_path,
+    CompareMode, Config, Debugger, PassMode, TestMode, TestPaths, UI_EXTENSIONS,
+    expected_output_path, output_base_dir, output_relative_path,
 };
 use crate::directives::DirectivesCache;
 use crate::executor::{CollectedTest, ColorConfig, OutputFormat};
@@ -268,7 +268,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
     let with_rustc_debug_assertions = matches.opt_present("with-rustc-debug-assertions");
     let with_std_debug_assertions = matches.opt_present("with-std-debug-assertions");
     let mode = matches.opt_str("mode").unwrap().parse().expect("invalid mode");
-    let has_html_tidy = if mode == Mode::Rustdoc {
+    let has_html_tidy = if mode == TestMode::Rustdoc {
         Command::new("tidy")
             .arg("--version")
             .stdout(Stdio::null())
@@ -279,7 +279,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
         false
     };
     let has_enzyme = matches.opt_present("has-enzyme");
-    let filters = if mode == Mode::RunMake {
+    let filters = if mode == TestMode::RunMake {
         matches
             .free
             .iter()
@@ -360,7 +360,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
         stage_id: matches.opt_str("stage-id").unwrap(),
 
         mode,
-        suite: matches.opt_str("suite").unwrap(),
+        suite: matches.opt_str("suite").unwrap().parse().expect("invalid suite"),
         debugger: matches.opt_str("debugger").map(|debugger| {
             debugger
                 .parse::<Debugger>()
@@ -545,7 +545,7 @@ pub fn run_tests(config: Arc<Config>) {
     unsafe { env::set_var("TARGET", &config.target) };
 
     let mut configs = Vec::new();
-    if let Mode::DebugInfo = config.mode {
+    if let TestMode::DebugInfo = config.mode {
         // Debugging emscripten code doesn't make sense today
         if !config.target.contains("emscripten") {
             match config.debugger {
@@ -783,7 +783,7 @@ fn collect_tests_from_dir(
     }
 
     // For run-make tests, a "test file" is actually a directory that contains an `rmake.rs`.
-    if cx.config.mode == Mode::RunMake {
+    if cx.config.mode == TestMode::RunMake {
         let mut collector = TestCollector::new();
         if dir.join("rmake.rs").exists() {
             let paths = TestPaths {
@@ -869,7 +869,7 @@ fn make_test(cx: &TestCollectorCx, collector: &mut TestCollector, testpaths: &Te
     // For run-make tests, each "test file" is actually a _directory_ containing an `rmake.rs`. But
     // for the purposes of directive parsing, we want to look at that recipe file, not the directory
     // itself.
-    let test_path = if cx.config.mode == Mode::RunMake {
+    let test_path = if cx.config.mode == TestMode::RunMake {
         testpaths.file.join("rmake.rs")
     } else {
         testpaths.file.clone()
@@ -884,7 +884,7 @@ fn make_test(cx: &TestCollectorCx, collector: &mut TestCollector, testpaths: &Te
     // - Incremental tests inherently can't run their revisions in parallel, so
     //   we treat them like non-revisioned tests here. Incremental revisions are
     //   handled internally by `runtest::run` instead.
-    let revisions = if early_props.revisions.is_empty() || cx.config.mode == Mode::Incremental {
+    let revisions = if early_props.revisions.is_empty() || cx.config.mode == TestMode::Incremental {
         vec![None]
     } else {
         early_props.revisions.iter().map(|r| Some(r.as_str())).collect()
@@ -1116,11 +1116,11 @@ fn check_for_overlapping_test_paths(found_path_stems: &HashSet<Utf8PathBuf>) {
 }
 
 pub fn early_config_check(config: &Config) {
-    if !config.has_html_tidy && config.mode == Mode::Rustdoc {
+    if !config.has_html_tidy && config.mode == TestMode::Rustdoc {
         warning!("`tidy` (html-tidy.org) is not installed; diffs will not be generated");
     }
 
-    if !config.profiler_runtime && config.mode == Mode::CoverageRun {
+    if !config.profiler_runtime && config.mode == TestMode::CoverageRun {
         let actioned = if config.bless { "blessed" } else { "checked" };
         warning!("profiler runtime is not available, so `.coverage` files won't be {actioned}");
         help!("try setting `profiler = true` in the `[build]` section of `bootstrap.toml`");
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 3e879e0e4bb..cb8f593c9df 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -16,11 +16,10 @@ use regex::{Captures, Regex};
 use tracing::*;
 
 use crate::common::{
-    Assembly, Codegen, CodegenUnits, CompareMode, Config, CoverageMap, CoverageRun, Crashes,
-    DebugInfo, Debugger, FailMode, Incremental, MirOpt, PassMode, Pretty, RunMake, Rustdoc,
-    RustdocJs, RustdocJson, TestPaths, UI_EXTENSIONS, UI_FIXED, UI_RUN_STDERR, UI_RUN_STDOUT,
-    UI_STDERR, UI_STDOUT, UI_SVG, UI_WINDOWS_SVG, Ui, expected_output_path, incremental_dir,
-    output_base_dir, output_base_name, output_testname_unique,
+    CompareMode, Config, Debugger, FailMode, PassMode, TestMode, TestPaths, TestSuite,
+    UI_EXTENSIONS, UI_FIXED, UI_RUN_STDERR, UI_RUN_STDOUT, UI_STDERR, UI_STDOUT, UI_SVG,
+    UI_WINDOWS_SVG, expected_output_path, incremental_dir, output_base_dir, output_base_name,
+    output_testname_unique,
 };
 use crate::compute_diff::{DiffLine, make_diff, write_diff, write_filtered_diff};
 use crate::directives::TestProps;
@@ -154,7 +153,7 @@ pub fn run(config: Arc<Config>, testpaths: &TestPaths, revision: Option<&str>) {
         cx.init_incremental_test();
     }
 
-    if config.mode == Incremental {
+    if config.mode == TestMode::Incremental {
         // Incremental tests are special because they cannot be run in
         // parallel.
         assert!(!props.revisions.is_empty(), "Incremental tests require revisions.");
@@ -203,7 +202,7 @@ pub fn compute_stamp_hash(config: &Config) -> String {
         None => {}
     }
 
-    if let Ui = config.mode {
+    if config.mode == TestMode::Ui {
         config.force_pass_mode.hash(&mut hash);
     }
 
@@ -251,25 +250,28 @@ impl<'test> TestCx<'test> {
     /// Code executed for each revision in turn (or, if there are no
     /// revisions, exactly once, with revision == None).
     fn run_revision(&self) {
-        if self.props.should_ice && self.config.mode != Incremental && self.config.mode != Crashes {
+        if self.props.should_ice
+            && self.config.mode != TestMode::Incremental
+            && self.config.mode != TestMode::Crashes
+        {
             self.fatal("cannot use should-ice in a test that is not cfail");
         }
         match self.config.mode {
-            Pretty => self.run_pretty_test(),
-            DebugInfo => self.run_debuginfo_test(),
-            Codegen => self.run_codegen_test(),
-            Rustdoc => self.run_rustdoc_test(),
-            RustdocJson => self.run_rustdoc_json_test(),
-            CodegenUnits => self.run_codegen_units_test(),
-            Incremental => self.run_incremental_test(),
-            RunMake => self.run_rmake_test(),
-            Ui => self.run_ui_test(),
-            MirOpt => self.run_mir_opt_test(),
-            Assembly => self.run_assembly_test(),
-            RustdocJs => self.run_rustdoc_js_test(),
-            CoverageMap => self.run_coverage_map_test(), // see self::coverage
-            CoverageRun => self.run_coverage_run_test(), // see self::coverage
-            Crashes => self.run_crash_test(),
+            TestMode::Pretty => self.run_pretty_test(),
+            TestMode::DebugInfo => self.run_debuginfo_test(),
+            TestMode::Codegen => self.run_codegen_test(),
+            TestMode::Rustdoc => self.run_rustdoc_test(),
+            TestMode::RustdocJson => self.run_rustdoc_json_test(),
+            TestMode::CodegenUnits => self.run_codegen_units_test(),
+            TestMode::Incremental => self.run_incremental_test(),
+            TestMode::RunMake => self.run_rmake_test(),
+            TestMode::Ui => self.run_ui_test(),
+            TestMode::MirOpt => self.run_mir_opt_test(),
+            TestMode::Assembly => self.run_assembly_test(),
+            TestMode::RustdocJs => self.run_rustdoc_js_test(),
+            TestMode::CoverageMap => self.run_coverage_map_test(), // see self::coverage
+            TestMode::CoverageRun => self.run_coverage_run_test(), // see self::coverage
+            TestMode::Crashes => self.run_crash_test(),
         }
     }
 
@@ -279,9 +281,13 @@ impl<'test> TestCx<'test> {
 
     fn should_run(&self, pm: Option<PassMode>) -> WillExecute {
         let test_should_run = match self.config.mode {
-            Ui if pm == Some(PassMode::Run) || self.props.fail_mode == Some(FailMode::Run) => true,
-            MirOpt if pm == Some(PassMode::Run) => true,
-            Ui | MirOpt => false,
+            TestMode::Ui
+                if pm == Some(PassMode::Run) || self.props.fail_mode == Some(FailMode::Run) =>
+            {
+                true
+            }
+            TestMode::MirOpt if pm == Some(PassMode::Run) => true,
+            TestMode::Ui | TestMode::MirOpt => false,
             mode => panic!("unimplemented for mode {:?}", mode),
         };
         if test_should_run { self.run_if_enabled() } else { WillExecute::No }
@@ -293,17 +299,17 @@ impl<'test> TestCx<'test> {
 
     fn should_run_successfully(&self, pm: Option<PassMode>) -> bool {
         match self.config.mode {
-            Ui | MirOpt => pm == Some(PassMode::Run),
+            TestMode::Ui | TestMode::MirOpt => pm == Some(PassMode::Run),
             mode => panic!("unimplemented for mode {:?}", mode),
         }
     }
 
     fn should_compile_successfully(&self, pm: Option<PassMode>) -> bool {
         match self.config.mode {
-            RustdocJs => true,
-            Ui => pm.is_some() || self.props.fail_mode > Some(FailMode::Build),
-            Crashes => false,
-            Incremental => {
+            TestMode::RustdocJs => true,
+            TestMode::Ui => pm.is_some() || self.props.fail_mode > Some(FailMode::Build),
+            TestMode::Crashes => false,
+            TestMode::Incremental => {
                 let revision =
                     self.revision.expect("incremental tests require a list of revisions");
                 if revision.starts_with("cpass")
@@ -349,7 +355,7 @@ impl<'test> TestCx<'test> {
             if proc_res.status.success() {
                 {
                     self.error(&format!("{} test did not emit an error", self.config.mode));
-                    if self.config.mode == crate::common::Mode::Ui {
+                    if self.config.mode == crate::common::TestMode::Ui {
                         println!("note: by default, ui tests are expected not to compile");
                     }
                     proc_res.fatal(None, || ());
@@ -892,7 +898,9 @@ impl<'test> TestCx<'test> {
 
     fn should_emit_metadata(&self, pm: Option<PassMode>) -> Emit {
         match (pm, self.props.fail_mode, self.config.mode) {
-            (Some(PassMode::Check), ..) | (_, Some(FailMode::Check), Ui) => Emit::Metadata,
+            (Some(PassMode::Check), ..) | (_, Some(FailMode::Check), TestMode::Ui) => {
+                Emit::Metadata
+            }
             _ => Emit::None,
         }
     }
@@ -926,7 +934,7 @@ impl<'test> TestCx<'test> {
         };
 
         let allow_unused = match self.config.mode {
-            Ui => {
+            TestMode::Ui => {
                 // UI tests tend to have tons of unused code as
                 // it's just testing various pieces of the compile, but we don't
                 // want to actually assert warnings about all this code. Instead
@@ -1021,7 +1029,7 @@ impl<'test> TestCx<'test> {
             .args(&self.props.compile_flags)
             .args(&self.props.doc_flags);
 
-        if self.config.mode == RustdocJson {
+        if self.config.mode == TestMode::RustdocJson {
             rustdoc.arg("--output-format").arg("json").arg("-Zunstable-options");
         }
 
@@ -1372,7 +1380,7 @@ impl<'test> TestCx<'test> {
             || self.is_vxworks_pure_static()
             || self.config.target.contains("bpf")
             || !self.config.target_cfg().dynamic_linking
-            || matches!(self.config.mode, CoverageMap | CoverageRun)
+            || matches!(self.config.mode, TestMode::CoverageMap | TestMode::CoverageRun)
         {
             // We primarily compile all auxiliary libraries as dynamic libraries
             // to avoid code size bloat and large binaries as much as possible
@@ -1486,7 +1494,10 @@ impl<'test> TestCx<'test> {
     }
 
     fn is_rustdoc(&self) -> bool {
-        matches!(self.config.suite.as_str(), "rustdoc-ui" | "rustdoc-js" | "rustdoc-json")
+        matches!(
+            self.config.suite,
+            TestSuite::RustdocUi | TestSuite::RustdocJs | TestSuite::RustdocJson
+        )
     }
 
     fn make_compile_args(
@@ -1562,14 +1573,14 @@ impl<'test> TestCx<'test> {
                 rustc.args(&["-Z", "incremental-verify-ich"]);
             }
 
-            if self.config.mode == CodegenUnits {
+            if self.config.mode == TestMode::CodegenUnits {
                 rustc.args(&["-Z", "human_readable_cgu_names"]);
             }
         }
 
         if self.config.optimize_tests && !is_rustdoc {
             match self.config.mode {
-                Ui => {
+                TestMode::Ui => {
                     // If optimize-tests is true we still only want to optimize tests that actually get
                     // executed and that don't specify their own optimization levels.
                     // Note: aux libs don't have a pass-mode, so they won't get optimized
@@ -1585,8 +1596,8 @@ impl<'test> TestCx<'test> {
                         rustc.arg("-O");
                     }
                 }
-                DebugInfo => { /* debuginfo tests must be unoptimized */ }
-                CoverageMap | CoverageRun => {
+                TestMode::DebugInfo => { /* debuginfo tests must be unoptimized */ }
+                TestMode::CoverageMap | TestMode::CoverageRun => {
                     // Coverage mappings and coverage reports are affected by
                     // optimization level, so they ignore the optimize-tests
                     // setting and set an optimization level in their mode's
@@ -1607,7 +1618,7 @@ impl<'test> TestCx<'test> {
         };
 
         match self.config.mode {
-            Incremental => {
+            TestMode::Incremental => {
                 // If we are extracting and matching errors in the new
                 // fashion, then you want JSON mode. Old-skool error
                 // patterns still match the raw compiler output.
@@ -1620,7 +1631,7 @@ impl<'test> TestCx<'test> {
                 rustc.arg("-Zui-testing");
                 rustc.arg("-Zdeduplicate-diagnostics=no");
             }
-            Ui => {
+            TestMode::Ui => {
                 if !self.props.compile_flags.iter().any(|s| s.starts_with("--error-format")) {
                     rustc.args(&["--error-format", "json"]);
                     rustc.args(&["--json", "future-incompat"]);
@@ -1633,7 +1644,7 @@ impl<'test> TestCx<'test> {
                 // FIXME: use this for other modes too, for perf?
                 rustc.arg("-Cstrip=debuginfo");
             }
-            MirOpt => {
+            TestMode::MirOpt => {
                 // We check passes under test to minimize the mir-opt test dump
                 // if files_for_miropt_test parses the passes, we dump only those passes
                 // otherwise we conservatively pass -Zdump-mir=all
@@ -1663,7 +1674,7 @@ impl<'test> TestCx<'test> {
 
                 set_mir_dump_dir(&mut rustc);
             }
-            CoverageMap => {
+            TestMode::CoverageMap => {
                 rustc.arg("-Cinstrument-coverage");
                 // These tests only compile to LLVM IR, so they don't need the
                 // profiler runtime to be present.
@@ -1673,23 +1684,28 @@ impl<'test> TestCx<'test> {
                 // by `compile-flags`.
                 rustc.arg("-Copt-level=2");
             }
-            CoverageRun => {
+            TestMode::CoverageRun => {
                 rustc.arg("-Cinstrument-coverage");
                 // Coverage reports are sometimes sensitive to optimizations,
                 // and the current snapshots assume `opt-level=2` unless
                 // overridden by `compile-flags`.
                 rustc.arg("-Copt-level=2");
             }
-            Assembly | Codegen => {
+            TestMode::Assembly | TestMode::Codegen => {
                 rustc.arg("-Cdebug-assertions=no");
             }
-            Crashes => {
+            TestMode::Crashes => {
                 set_mir_dump_dir(&mut rustc);
             }
-            CodegenUnits => {
+            TestMode::CodegenUnits => {
                 rustc.arg("-Zprint-mono-items");
             }
-            Pretty | DebugInfo | Rustdoc | RustdocJson | RunMake | RustdocJs => {
+            TestMode::Pretty
+            | TestMode::DebugInfo
+            | TestMode::Rustdoc
+            | TestMode::RustdocJson
+            | TestMode::RunMake
+            | TestMode::RustdocJs => {
                 // do not use JSON output
             }
         }
@@ -1777,6 +1793,12 @@ impl<'test> TestCx<'test> {
         // Allow tests to use internal features.
         rustc.args(&["-A", "internal_features"]);
 
+        // Allow tests to have unused parens and braces.
+        // Add #![deny(unused_parens, unused_braces)] to the test file if you want to
+        // test that these lints are working.
+        rustc.args(&["-A", "unused_parens"]);
+        rustc.args(&["-A", "unused_braces"]);
+
         if self.props.force_host {
             self.maybe_add_external_args(&mut rustc, &self.config.host_rustcflags);
             if !is_rustdoc {
@@ -1956,7 +1978,7 @@ impl<'test> TestCx<'test> {
     /// The revision, ignored for incremental compilation since it wants all revisions in
     /// the same directory.
     fn safe_revision(&self) -> Option<&str> {
-        if self.config.mode == Incremental { None } else { self.revision }
+        if self.config.mode == TestMode::Incremental { None } else { self.revision }
     }
 
     /// Gets the absolute path to the directory where all output for the given
diff --git a/src/tools/compiletest/src/runtest/coverage.rs b/src/tools/compiletest/src/runtest/coverage.rs
index 38f0e956474..d0a0c960b45 100644
--- a/src/tools/compiletest/src/runtest/coverage.rs
+++ b/src/tools/compiletest/src/runtest/coverage.rs
@@ -6,7 +6,7 @@ use std::process::Command;
 use camino::{Utf8Path, Utf8PathBuf};
 use glob::glob;
 
-use crate::common::{UI_COVERAGE, UI_COVERAGE_MAP};
+use crate::common::{TestSuite, UI_COVERAGE, UI_COVERAGE_MAP};
 use crate::runtest::{Emit, ProcRes, TestCx, WillExecute};
 use crate::util::static_regex;
 
@@ -91,7 +91,7 @@ impl<'test> TestCx<'test> {
         let mut profraw_paths = vec![profraw_path];
         let mut bin_paths = vec![self.make_exe_name()];
 
-        if self.config.suite == "coverage-run-rustdoc" {
+        if self.config.suite == TestSuite::CoverageRunRustdoc {
             self.run_doctests_for_coverage(&mut profraw_paths, &mut bin_paths);
         }
 
diff --git a/src/tools/compiletest/src/runtest/run_make.rs b/src/tools/compiletest/src/runtest/run_make.rs
index 60e8e16e25e..c8d5190c039 100644
--- a/src/tools/compiletest/src/runtest/run_make.rs
+++ b/src/tools/compiletest/src/runtest/run_make.rs
@@ -225,6 +225,19 @@ impl TestCx<'_> {
             cmd.env("RUNNER", runner);
         }
 
+        // Guard against externally-set env vars.
+        cmd.env_remove("__RUSTC_DEBUG_ASSERTIONS_ENABLED");
+        if self.config.with_rustc_debug_assertions {
+            // Used for `run_make_support::env::rustc_debug_assertions_enabled`.
+            cmd.env("__RUSTC_DEBUG_ASSERTIONS_ENABLED", "1");
+        }
+
+        cmd.env_remove("__STD_DEBUG_ASSERTIONS_ENABLED");
+        if self.config.with_std_debug_assertions {
+            // Used for `run_make_support::env::std_debug_assertions_enabled`.
+            cmd.env("__STD_DEBUG_ASSERTIONS_ENABLED", "1");
+        }
+
         // We don't want RUSTFLAGS set from the outside to interfere with
         // compiler flags set in the test cases:
         cmd.env_remove("RUSTFLAGS");
diff --git a/src/tools/compiletest/src/runtest/rustdoc.rs b/src/tools/compiletest/src/runtest/rustdoc.rs
index 637ea833357..236f021ce82 100644
--- a/src/tools/compiletest/src/runtest/rustdoc.rs
+++ b/src/tools/compiletest/src/runtest/rustdoc.rs
@@ -4,7 +4,7 @@ use super::{TestCx, remove_and_create_dir_all};
 
 impl TestCx<'_> {
     pub(super) fn run_rustdoc_test(&self) {
-        assert!(self.revision.is_none(), "revisions not relevant here");
+        assert!(self.revision.is_none(), "revisions not supported in this test suite");
 
         let out_dir = self.output_base_dir();
         remove_and_create_dir_all(&out_dir).unwrap_or_else(|e| {
diff --git a/src/tools/compiletest/src/runtest/rustdoc_json.rs b/src/tools/compiletest/src/runtest/rustdoc_json.rs
index 9f88faca892..4f35efedfde 100644
--- a/src/tools/compiletest/src/runtest/rustdoc_json.rs
+++ b/src/tools/compiletest/src/runtest/rustdoc_json.rs
@@ -6,7 +6,7 @@ impl TestCx<'_> {
     pub(super) fn run_rustdoc_json_test(&self) {
         //FIXME: Add bless option.
 
-        assert!(self.revision.is_none(), "revisions not relevant here");
+        assert!(self.revision.is_none(), "revisions not supported in this test suite");
 
         let out_dir = self.output_base_dir();
         remove_and_create_dir_all(&out_dir).unwrap_or_else(|e| {
diff --git a/src/tools/compiletest/src/tests.rs b/src/tools/compiletest/src/tests.rs
index e3e4a81755d..174ec9381f6 100644
--- a/src/tools/compiletest/src/tests.rs
+++ b/src/tools/compiletest/src/tests.rs
@@ -67,11 +67,7 @@ fn is_test_test() {
 
 #[test]
 fn string_enums() {
-    // These imports are needed for the macro-generated code
-    use std::fmt;
-    use std::str::FromStr;
-
-    crate::common::string_enum! {
+    crate::util::string_enum! {
         #[derive(Clone, Copy, Debug, PartialEq)]
         enum Animal {
             Cat => "meow",
diff --git a/src/tools/compiletest/src/util.rs b/src/tools/compiletest/src/util.rs
index 202582bea8c..fb047548c45 100644
--- a/src/tools/compiletest/src/util.rs
+++ b/src/tools/compiletest/src/util.rs
@@ -104,3 +104,42 @@ macro_rules! static_regex {
     }};
 }
 pub(crate) use static_regex;
+
+macro_rules! string_enum {
+    ($(#[$meta:meta])* $vis:vis enum $name:ident { $($variant:ident => $repr:expr,)* }) => {
+        $(#[$meta])*
+        $vis enum $name {
+            $($variant,)*
+        }
+
+        impl $name {
+            $vis const VARIANTS: &'static [Self] = &[$(Self::$variant,)*];
+            $vis const STR_VARIANTS: &'static [&'static str] = &[$(Self::$variant.to_str(),)*];
+
+            $vis const fn to_str(&self) -> &'static str {
+                match self {
+                    $(Self::$variant => $repr,)*
+                }
+            }
+        }
+
+        impl ::std::fmt::Display for $name {
+            fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+                ::std::fmt::Display::fmt(self.to_str(), f)
+            }
+        }
+
+        impl ::std::str::FromStr for $name {
+            type Err = String;
+
+            fn from_str(s: &str) -> Result<Self, Self::Err> {
+                match s {
+                    $($repr => Ok(Self::$variant),)*
+                    _ => Err(format!(concat!("unknown `", stringify!($name), "` variant: `{}`"), s)),
+                }
+            }
+        }
+    }
+}
+
+pub(crate) use string_enum;
diff --git a/src/tools/jsondocck/src/directive.rs b/src/tools/jsondocck/src/directive.rs
index fdb2fa6dbbe..c52c1686660 100644
--- a/src/tools/jsondocck/src/directive.rs
+++ b/src/tools/jsondocck/src/directive.rs
@@ -105,13 +105,10 @@ impl DirectiveKind {
                 [_path, value] => Self::HasNotValue { value: value.clone() },
                 _ => panic!("`//@ !has` must have 2 or 3 arguments, but got {args:?}"),
             },
-            // Ignore compiletest directives, like //@ edition
-            (_, false) if KNOWN_DIRECTIVE_NAMES.contains(&directive_name) => {
-                return None;
-            }
-            _ => {
-                panic!("Invalid directive `//@ {}{directive_name}`", if negated { "!" } else { "" })
-            }
+            // Ignore unknown directives as they might be compiletest directives
+            // since they share the same `//@` prefix by convention. In any case,
+            // compiletest rejects unknown directives for us.
+            _ => return None,
         };
 
         Some((kind, &args[0]))
@@ -216,10 +213,6 @@ fn get_one<'a>(matches: &[&'a Value]) -> Result<&'a Value, String> {
     }
 }
 
-// FIXME: This setup is temporary until we figure out how to improve this situation.
-//        See <https://github.com/rust-lang/rust/issues/125813#issuecomment-2141953780>.
-include!(concat!(env!("CARGO_MANIFEST_DIR"), "/../compiletest/src/directive-list.rs"));
-
 fn string_to_value<'a>(s: &str, cache: &'a Cache) -> Cow<'a, Value> {
     if s.starts_with("$") {
         Cow::Borrowed(&cache.variables.get(&s[1..]).unwrap_or_else(|| {
diff --git a/src/tools/jsondocck/src/main.rs b/src/tools/jsondocck/src/main.rs
index d84be4d3a3a..c3487565c23 100644
--- a/src/tools/jsondocck/src/main.rs
+++ b/src/tools/jsondocck/src/main.rs
@@ -47,8 +47,8 @@ static LINE_PATTERN: LazyLock<Regex> = LazyLock::new(|| {
         ^\s*
         //@\s+
         (?P<negated>!?)
-        (?P<directive>[A-Za-z0-9]+(?:-[A-Za-z0-9]+)*)
-        (?P<args>.*)$
+        (?P<directive>.+?)
+        (?:[\s:](?P<args>.*))?$
     "#,
     )
     .ignore_whitespace(true)
@@ -58,15 +58,7 @@ static LINE_PATTERN: LazyLock<Regex> = LazyLock::new(|| {
 });
 
 static DEPRECATED_LINE_PATTERN: LazyLock<Regex> = LazyLock::new(|| {
-    RegexBuilder::new(
-        r#"
-        //\s+@
-    "#,
-    )
-    .ignore_whitespace(true)
-    .unicode(true)
-    .build()
-    .unwrap()
+    RegexBuilder::new(r"//\s+@").ignore_whitespace(true).unicode(true).build().unwrap()
 });
 
 fn print_err(msg: &str, lineno: usize) {
@@ -94,7 +86,7 @@ fn get_directives(template: &str) -> Result<Vec<Directive>, ()> {
 
         let negated = &cap["negated"] == "!";
 
-        let args_str = &cap["args"];
+        let args_str = cap.name("args").map(|m| m.as_str()).unwrap_or_default();
         let Some(args) = shlex::split(args_str) else {
             print_err(&format!("Invalid arguments to shlex::split: `{args_str}`",), lineno);
             errors = true;
diff --git a/src/tools/lint-docs/Cargo.toml b/src/tools/lint-docs/Cargo.toml
index e914a2df2ba..6e1ab84ed18 100644
--- a/src/tools/lint-docs/Cargo.toml
+++ b/src/tools/lint-docs/Cargo.toml
@@ -7,7 +7,7 @@ description = "A script to extract the lint documentation for the rustc book."
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
-rustc-literal-escaper = "0.0.4"
+rustc-literal-escaper = "0.0.5"
 serde_json = "1.0.57"
 tempfile = "3.1.0"
 walkdir = "2.3.1"
diff --git a/src/tools/lint-docs/src/groups.rs b/src/tools/lint-docs/src/groups.rs
index 78d4f87ed0d..a24fbbc0cea 100644
--- a/src/tools/lint-docs/src/groups.rs
+++ b/src/tools/lint-docs/src/groups.rs
@@ -26,6 +26,10 @@ static GROUP_DESCRIPTIONS: &[(&str, &str)] = &[
         "Lints that detect identifiers which will be come keywords in later editions",
     ),
     ("deprecated-safe", "Lints for functions which were erroneously marked as safe in the past"),
+    (
+        "unknown-or-malformed-diagnostic-attributes",
+        "detects unknown or malformed diagnostic attributes",
+    ),
 ];
 
 type LintGroups = BTreeMap<String, BTreeSet<String>>;
diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md
index b05acff72b5..7816ce1ac56 100644
--- a/src/tools/miri/README.md
+++ b/src/tools/miri/README.md
@@ -342,9 +342,9 @@ environment variable. We first document the most relevant and most commonly used
   is enabled (the default), this is also used to emulate system entropy. The default seed is 0. You
   can increase test coverage by running Miri multiple times with different seeds.
 * `-Zmiri-strict-provenance` enables [strict
-  provenance](https://github.com/rust-lang/rust/issues/95228) checking in Miri. This means that
-  casting an integer to a pointer will stop execution because the provenance of the pointer
-  cannot be determined.
+  provenance](https://doc.rust-lang.org/nightly/std/ptr/index.html#strict-provenance) checking in
+  Miri. This means that casting an integer to a pointer will stop execution because the provenance
+  of the pointer cannot be determined.
 * `-Zmiri-symbolic-alignment-check` makes the alignment check more strict.  By default, alignment is
   checked by casting the pointer to an integer, and making sure that is a multiple of the alignment.
   This can lead to cases where a program passes the alignment check by pure chance, because things
diff --git a/src/tools/miri/miri-script/Cargo.toml b/src/tools/miri/miri-script/Cargo.toml
index 462a9cc62bf..9240788d6bc 100644
--- a/src/tools/miri/miri-script/Cargo.toml
+++ b/src/tools/miri/miri-script/Cargo.toml
@@ -7,6 +7,7 @@ repository = "https://github.com/rust-lang/miri"
 version = "0.1.0"
 default-run = "miri-script"
 edition = "2024"
+rust-version = "1.85"
 
 [workspace]
 # We make this a workspace root so that cargo does not go looking in ../Cargo.toml for the workspace root.
diff --git a/src/tools/miri/miri-script/src/commands.rs b/src/tools/miri/miri-script/src/commands.rs
index e948beef004..e6ebdf54e38 100644
--- a/src/tools/miri/miri-script/src/commands.rs
+++ b/src/tools/miri/miri-script/src/commands.rs
@@ -702,7 +702,6 @@ impl Command {
         let mut early_flags = Vec::<OsString>::new();
 
         // In `dep` mode, the target is already passed via `MIRI_TEST_TARGET`
-        #[expect(clippy::collapsible_if)] // we need to wait until this is stable
         if !dep {
             if let Some(target) = &target {
                 early_flags.push("--target".into());
@@ -735,7 +734,6 @@ impl Command {
         // Add Miri flags
         let mut cmd = cmd.args(&miri_flags).args(&early_flags).args(&flags);
         // For `--dep` we also need to set the target in the env var.
-        #[expect(clippy::collapsible_if)] // we need to wait until this is stable
         if dep {
             if let Some(target) = &target {
                 cmd = cmd.env("MIRI_TEST_TARGET", target);
diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version
index 0130cf13a45..67f27e7aa2c 100644
--- a/src/tools/miri/rust-version
+++ b/src/tools/miri/rust-version
@@ -1 +1 @@
-733b47ea4b1b86216f14ef56e49440c33933f230
+7f2065a4bae1faed5bab928c670964eafbf43b55
diff --git a/src/tools/miri/src/alloc/isolated_alloc.rs b/src/tools/miri/src/alloc/isolated_alloc.rs
index a7bb9b4da75..7b2f1a3eebf 100644
--- a/src/tools/miri/src/alloc/isolated_alloc.rs
+++ b/src/tools/miri/src/alloc/isolated_alloc.rs
@@ -302,23 +302,20 @@ impl IsolatedAlloc {
         }
     }
 
-    /// Returns a vector of page addresses managed by the allocator.
-    pub fn pages(&self) -> Vec<usize> {
-        let mut pages: Vec<usize> =
-            self.page_ptrs.iter().map(|p| p.expose_provenance().get()).collect();
-        for (ptr, size) in self.huge_ptrs.iter() {
-            for i in 0..size / self.page_size {
-                pages.push(ptr.expose_provenance().get().strict_add(i * self.page_size));
-            }
-        }
-        pages
+    /// Returns a list of page addresses managed by the allocator.
+    pub fn pages(&self) -> impl Iterator<Item = usize> {
+        let pages = self.page_ptrs.iter().map(|p| p.expose_provenance().get());
+        pages.chain(self.huge_ptrs.iter().flat_map(|(ptr, size)| {
+            (0..size / self.page_size)
+                .map(|i| ptr.expose_provenance().get().strict_add(i * self.page_size))
+        }))
     }
 
     /// Protects all owned memory as `PROT_NONE`, preventing accesses.
     ///
     /// SAFETY: Accessing memory after this point will result in a segfault
     /// unless it is first unprotected.
-    pub unsafe fn prepare_ffi(&mut self) -> Result<(), nix::errno::Errno> {
+    pub unsafe fn start_ffi(&mut self) -> Result<(), nix::errno::Errno> {
         let prot = mman::ProtFlags::PROT_NONE;
         unsafe { self.mprotect(prot) }
     }
@@ -326,7 +323,7 @@ impl IsolatedAlloc {
     /// Deprotects all owned memory by setting it to RW. Erroring here is very
     /// likely unrecoverable, so it may panic if applying those permissions
     /// fails.
-    pub fn unprep_ffi(&mut self) {
+    pub fn end_ffi(&mut self) {
         let prot = mman::ProtFlags::PROT_READ | mman::ProtFlags::PROT_WRITE;
         unsafe {
             self.mprotect(prot).unwrap();
diff --git a/src/tools/miri/src/bin/log/tracing_chrome.rs b/src/tools/miri/src/bin/log/tracing_chrome.rs
index 459acea6f0b..5a96633c99e 100644
--- a/src/tools/miri/src/bin/log/tracing_chrome.rs
+++ b/src/tools/miri/src/bin/log/tracing_chrome.rs
@@ -1,8 +1,18 @@
 // SPDX-License-Identifier: MIT
 // SPDX-FileCopyrightText: Copyright (c) 2020 Thoren Paulson
-//! This file is taken unmodified from the following link, except for file attributes and
-//! `extern crate` at the top.
-//! https://github.com/thoren-d/tracing-chrome/blob/7e2625ab4aeeef2f0ef9bde9d6258dd181c04472/src/lib.rs
+//! This file was initially taken from the following link:
+//! <https://github.com/thoren-d/tracing-chrome/blob/7e2625ab4aeeef2f0ef9bde9d6258dd181c04472/src/lib.rs>
+//!
+//! The precise changes that were made to the original file can be found in git history
+//! (`git log -- path/to/tracing_chrome.rs`), but in summary:
+//! - the file attributes were changed and `extern crate` was added at the top
+//! - if a tracing span has a field called "tracing_separate_thread", it will be given a separate
+//! span ID even in [TraceStyle::Threaded] mode, to make it appear on a separate line when viewing
+//! the trace in <https://ui.perfetto.dev>. This is the syntax to trigger this behavior:
+//!   ```rust
+//!   tracing::info_span!("my_span", tracing_separate_thread = tracing::field::Empty, /* ... */)
+//!   ```
+//!
 //! Depending on the tracing-chrome crate from crates.io is unfortunately not possible, since it
 //! depends on `tracing_core` which conflicts with rustc_private's `tracing_core` (meaning it would
 //! not be possible to use the [ChromeLayer] in a context that expects a [Layer] from
@@ -79,14 +89,26 @@ where
 }
 
 /// Decides how traces will be recorded.
+/// Also see <https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview#heading=h.jh64i9l3vwa1>
 #[derive(Default)]
 pub enum TraceStyle {
-    /// Traces will be recorded as a group of threads.
+    /// Traces will be recorded as a group of threads, and all spans on the same thread will appear
+    /// on a single trace line in <https://ui.perfetto.dev>.
     /// In this style, spans should be entered and exited on the same thread.
+    ///
+    /// If a tracing span has a field called "tracing_separate_thread", it will be given a separate
+    /// span ID even in this mode, to make it appear on a separate line when viewing the trace in
+    /// <https://ui.perfetto.dev>. This is the syntax to trigger this behavior:
+    /// ```rust
+    /// tracing::info_span!("my_span", tracing_separate_thread = tracing::field::Empty, /* ... */)
+    /// ```
+    /// [tracing::field::Empty] is used so that other tracing layers (e.g. the logger) will ignore
+    /// the "tracing_separate_thread" argument and not print out anything for it.
     #[default]
     Threaded,
 
-    /// Traces will recorded as a group of asynchronous operations.
+    /// Traces will recorded as a group of asynchronous operations. All spans will be given separate
+    /// span IDs and will appear on separate trace lines in <https://ui.perfetto.dev>.
     Async,
 }
 
@@ -497,31 +519,39 @@ where
         }
     }
 
-    fn get_root_id(span: SpanRef<S>) -> u64 {
-        span.scope()
-            .from_root()
-            .take(1)
-            .next()
-            .unwrap_or(span)
-            .id()
-            .into_u64()
+    fn get_root_id(&self, span: SpanRef<S>) -> Option<u64> {
+        match self.trace_style {
+            TraceStyle::Threaded => {
+                if span.fields().field("tracing_separate_thread").is_some() {
+                    // assign an independent "id" to spans with argument "tracing_separate_thread",
+                    // so they appear a separate trace line in trace visualization tools, see
+                    // https://docs.google.com/document/d/1CvAClvFfyA5R-PhYUmn5OOQtYMH4h6I0nSsKchNAySU/preview#heading=h.jh64i9l3vwa1
+                    Some(span.id().into_u64())
+                } else {
+                    None
+                }
+            },
+            TraceStyle::Async => Some(
+                span.scope()
+                    .from_root()
+                    .take(1)
+                    .next()
+                    .unwrap_or(span)
+                    .id()
+                    .into_u64()
+            ),
+        }
     }
 
     fn enter_span(&self, span: SpanRef<S>, ts: f64) {
         let callsite = self.get_callsite(EventOrSpan::Span(&span));
-        let root_id = match self.trace_style {
-            TraceStyle::Async => Some(ChromeLayer::get_root_id(span)),
-            _ => None,
-        };
+        let root_id = self.get_root_id(span);
         self.send_message(Message::Enter(ts, callsite, root_id));
     }
 
     fn exit_span(&self, span: SpanRef<S>, ts: f64) {
         let callsite = self.get_callsite(EventOrSpan::Span(&span));
-        let root_id = match self.trace_style {
-            TraceStyle::Async => Some(ChromeLayer::get_root_id(span)),
-            _ => None,
-        };
+        let root_id = self.get_root_id(span);
         self.send_message(Message::Exit(ts, callsite, root_id));
     }
 
diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs
index e3b579a4ac6..61cebedf081 100644
--- a/src/tools/miri/src/bin/miri.rs
+++ b/src/tools/miri/src/bin/miri.rs
@@ -233,8 +233,6 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
         } else {
             let return_code = miri::eval_entry(tcx, entry_def_id, entry_type, &config, None)
                 .unwrap_or_else(|| {
-                    //#[cfg(target_os = "linux")]
-                    //miri::native_lib::register_retcode_sv(rustc_driver::EXIT_FAILURE);
                     tcx.dcx().abort_if_errors();
                     rustc_driver::EXIT_FAILURE
                 });
@@ -337,6 +335,9 @@ impl rustc_driver::Callbacks for MiriBeRustCompilerCalls {
 fn exit(exit_code: i32) -> ! {
     // Drop the tracing guard before exiting, so tracing calls are flushed correctly.
     deinit_loggers();
+    // Make sure the supervisor knows about the code code.
+    #[cfg(target_os = "linux")]
+    miri::native_lib::register_retcode_sv(exit_code);
     std::process::exit(exit_code);
 }
 
@@ -355,6 +356,11 @@ fn run_compiler_and_exit(
     args: &[String],
     callbacks: &mut (dyn rustc_driver::Callbacks + Send),
 ) -> ! {
+    // Install the ctrlc handler that sets `rustc_const_eval::CTRL_C_RECEIVED`, even if
+    // MIRI_BE_RUSTC is set. We do this late so that when `native_lib::init_sv` is called,
+    // there are no other threads.
+    rustc_driver::install_ctrlc_handler();
+
     // Invoke compiler, catch any unwinding panics and handle return code.
     let exit_code =
         rustc_driver::catch_with_exit_code(move || rustc_driver::run_compiler(args, callbacks));
@@ -439,10 +445,6 @@ fn main() {
     let args = rustc_driver::catch_fatal_errors(|| rustc_driver::args::raw_args(&early_dcx))
         .unwrap_or_else(|_| std::process::exit(rustc_driver::EXIT_FAILURE));
 
-    // Install the ctrlc handler that sets `rustc_const_eval::CTRL_C_RECEIVED`, even if
-    // MIRI_BE_RUSTC is set.
-    rustc_driver::install_ctrlc_handler();
-
     // If the environment asks us to actually be rustc, then do that.
     if let Some(crate_kind) = env::var_os("MIRI_BE_RUSTC") {
         // Earliest rustc setup.
@@ -750,15 +752,15 @@ fn main() {
 
     debug!("rustc arguments: {:?}", rustc_args);
     debug!("crate arguments: {:?}", miri_config.args);
-    #[cfg(target_os = "linux")]
     if !miri_config.native_lib.is_empty() && miri_config.native_lib_enable_tracing {
-        // FIXME: This should display a diagnostic / warning on error
-        // SAFETY: If any other threads exist at this point (namely for the ctrlc
-        // handler), they will not interact with anything on the main rustc/Miri
-        // thread in an async-signal-unsafe way such as by accessing shared
-        // semaphores, etc.; the handler only calls `sleep()` and `exit()`, which
-        // are async-signal-safe, as is accessing atomics
-        //let _ = unsafe { miri::native_lib::init_sv() };
+        // SAFETY: No other threads are running
+        #[cfg(target_os = "linux")]
+        if unsafe { miri::native_lib::init_sv() }.is_err() {
+            eprintln!(
+                "warning: The native-lib tracer could not be started. Is this an x86 Linux system, and does Miri have permissions to ptrace?\n\
+                Falling back to non-tracing native-lib mode."
+            );
+        }
     }
     run_compiler_and_exit(
         &rustc_args,
diff --git a/src/tools/miri/src/borrow_tracker/mod.rs b/src/tools/miri/src/borrow_tracker/mod.rs
index 36c61053a32..ec6c2c60ca9 100644
--- a/src/tools/miri/src/borrow_tracker/mod.rs
+++ b/src/tools/miri/src/borrow_tracker/mod.rs
@@ -260,6 +260,7 @@ impl GlobalStateInner {
         kind: MemoryKind,
         machine: &MiriMachine<'_>,
     ) -> AllocState {
+        let _span = enter_trace_span!(borrow_tracker::new_allocation, ?id, ?alloc_size, ?kind);
         match self.borrow_tracker_method {
             BorrowTrackerMethod::StackedBorrows =>
                 AllocState::StackedBorrows(Box::new(RefCell::new(Stacks::new_allocation(
@@ -280,6 +281,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         kind: RetagKind,
         val: &ImmTy<'tcx>,
     ) -> InterpResult<'tcx, ImmTy<'tcx>> {
+        let _span = enter_trace_span!(borrow_tracker::retag_ptr_value, ?kind, ?val.layout);
         let this = self.eval_context_mut();
         let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method;
         match method {
@@ -293,6 +295,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         kind: RetagKind,
         place: &PlaceTy<'tcx>,
     ) -> InterpResult<'tcx> {
+        let _span = enter_trace_span!(borrow_tracker::retag_place_contents, ?kind, ?place);
         let this = self.eval_context_mut();
         let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method;
         match method {
@@ -302,6 +305,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     }
 
     fn protect_place(&mut self, place: &MPlaceTy<'tcx>) -> InterpResult<'tcx, MPlaceTy<'tcx>> {
+        let _span = enter_trace_span!(borrow_tracker::protect_place, ?place);
         let this = self.eval_context_mut();
         let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method;
         match method {
@@ -311,6 +315,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     }
 
     fn expose_tag(&self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> {
+        let _span =
+            enter_trace_span!(borrow_tracker::expose_tag, alloc_id = alloc_id.0, tag = tag.0);
         let this = self.eval_context_ref();
         let method = this.machine.borrow_tracker.as_ref().unwrap().borrow().borrow_tracker_method;
         match method {
@@ -354,6 +360,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         &self,
         frame: &Frame<'tcx, Provenance, FrameExtra<'tcx>>,
     ) -> InterpResult<'tcx> {
+        let _span = enter_trace_span!(borrow_tracker::on_stack_pop);
         let this = self.eval_context_ref();
         let borrow_tracker = this.machine.borrow_tracker.as_ref().unwrap();
         // The body of this loop needs `borrow_tracker` immutably
@@ -431,6 +438,7 @@ impl AllocState {
         range: AllocRange,
         machine: &MiriMachine<'tcx>,
     ) -> InterpResult<'tcx> {
+        let _span = enter_trace_span!(borrow_tracker::before_memory_read, alloc_id = alloc_id.0);
         match self {
             AllocState::StackedBorrows(sb) =>
                 sb.borrow_mut().before_memory_read(alloc_id, prov_extra, range, machine),
@@ -452,6 +460,7 @@ impl AllocState {
         range: AllocRange,
         machine: &MiriMachine<'tcx>,
     ) -> InterpResult<'tcx> {
+        let _span = enter_trace_span!(borrow_tracker::before_memory_write, alloc_id = alloc_id.0);
         match self {
             AllocState::StackedBorrows(sb) =>
                 sb.get_mut().before_memory_write(alloc_id, prov_extra, range, machine),
@@ -473,6 +482,8 @@ impl AllocState {
         size: Size,
         machine: &MiriMachine<'tcx>,
     ) -> InterpResult<'tcx> {
+        let _span =
+            enter_trace_span!(borrow_tracker::before_memory_deallocation, alloc_id = alloc_id.0);
         match self {
             AllocState::StackedBorrows(sb) =>
                 sb.get_mut().before_memory_deallocation(alloc_id, prov_extra, size, machine),
@@ -482,6 +493,7 @@ impl AllocState {
     }
 
     pub fn remove_unreachable_tags(&self, tags: &FxHashSet<BorTag>) {
+        let _span = enter_trace_span!(borrow_tracker::remove_unreachable_tags);
         match self {
             AllocState::StackedBorrows(sb) => sb.borrow_mut().remove_unreachable_tags(tags),
             AllocState::TreeBorrows(tb) => tb.borrow_mut().remove_unreachable_tags(tags),
@@ -496,6 +508,11 @@ impl AllocState {
         tag: BorTag,
         alloc_id: AllocId, // diagnostics
     ) -> InterpResult<'tcx> {
+        let _span = enter_trace_span!(
+            borrow_tracker::release_protector,
+            alloc_id = alloc_id.0,
+            tag = tag.0
+        );
         match self {
             AllocState::StackedBorrows(_sb) => interp_ok(()),
             AllocState::TreeBorrows(tb) =>
@@ -506,6 +523,7 @@ impl AllocState {
 
 impl VisitProvenance for AllocState {
     fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
+        let _span = enter_trace_span!(borrow_tracker::visit_provenance);
         match self {
             AllocState::StackedBorrows(sb) => sb.visit_provenance(visit),
             AllocState::TreeBorrows(tb) => tb.visit_provenance(visit),
diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs
index b8bcacf7c99..834a4b41f22 100644
--- a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs
+++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs
@@ -30,7 +30,7 @@ pub type AllocState = Stacks;
 #[derive(Clone, Debug)]
 pub struct Stacks {
     // Even reading memory can have effects on the stack, so we need a `RefCell` here.
-    stacks: RangeMap<Stack>,
+    stacks: DedupRangeMap<Stack>,
     /// Stores past operations on this allocation
     history: AllocHistory,
     /// The set of tags that have been exposed inside this allocation.
@@ -468,7 +468,7 @@ impl<'tcx> Stacks {
         let stack = Stack::new(item);
 
         Stacks {
-            stacks: RangeMap::new(size, stack),
+            stacks: DedupRangeMap::new(size, stack),
             history: AllocHistory::new(id, item, machine),
             exposed_tags: FxHashSet::default(),
         }
diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs
index a0761cb07a1..c157c69d7c8 100644
--- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs
+++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs
@@ -314,7 +314,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let span = this.machine.current_span();
 
         // Store initial permissions and their corresponding range.
-        let mut perms_map: RangeMap<LocationState> = RangeMap::new(
+        let mut perms_map: DedupRangeMap<LocationState> = DedupRangeMap::new(
             ptr_size,
             LocationState::new_accessed(Permission::new_disabled(), IdempotentForeignAccess::None), // this will be overwritten
         );
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 48e4a19e263..1f29bcfc2b0 100644
--- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs
+++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs
@@ -247,7 +247,7 @@ pub struct Tree {
     /// `unwrap` any `perm.get(key)`.
     ///
     /// We do uphold the fact that `keys(perms)` is a subset of `keys(nodes)`
-    pub(super) rperms: RangeMap<UniValMap<LocationState>>,
+    pub(super) rperms: DedupRangeMap<UniValMap<LocationState>>,
     /// The index of the root node.
     pub(super) root: UniIndex,
 }
@@ -609,7 +609,7 @@ impl Tree {
                     IdempotentForeignAccess::None,
                 ),
             );
-            RangeMap::new(size, perms)
+            DedupRangeMap::new(size, perms)
         };
         Self { root: root_idx, nodes, rperms, tag_mapping }
     }
@@ -631,7 +631,7 @@ impl<'tcx> Tree {
         base_offset: Size,
         parent_tag: BorTag,
         new_tag: BorTag,
-        initial_perms: RangeMap<LocationState>,
+        initial_perms: DedupRangeMap<LocationState>,
         default_perm: Permission,
         protected: bool,
         span: Span,
diff --git a/src/tools/miri/src/concurrency/data_race.rs b/src/tools/miri/src/concurrency/data_race.rs
index b5e7e9d0ac0..38d76f5cf73 100644
--- a/src/tools/miri/src/concurrency/data_race.rs
+++ b/src/tools/miri/src/concurrency/data_race.rs
@@ -997,7 +997,7 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> {
 #[derive(Debug, Clone)]
 pub struct VClockAlloc {
     /// Assigning each byte a MemoryCellClocks.
-    alloc_ranges: RefCell<RangeMap<MemoryCellClocks>>,
+    alloc_ranges: RefCell<DedupRangeMap<MemoryCellClocks>>,
 }
 
 impl VisitProvenance for VClockAlloc {
@@ -1045,7 +1045,7 @@ impl VClockAlloc {
                 (VTimestamp::ZERO, global.thread_index(ThreadId::MAIN_THREAD)),
         };
         VClockAlloc {
-            alloc_ranges: RefCell::new(RangeMap::new(
+            alloc_ranges: RefCell::new(DedupRangeMap::new(
                 len,
                 MemoryCellClocks::new(alloc_timestamp, alloc_index),
             )),
diff --git a/src/tools/miri/src/concurrency/mod.rs b/src/tools/miri/src/concurrency/mod.rs
index 49bcc0d30b5..c2ea8a00dec 100644
--- a/src/tools/miri/src/concurrency/mod.rs
+++ b/src/tools/miri/src/concurrency/mod.rs
@@ -2,7 +2,6 @@ pub mod cpu_affinity;
 pub mod data_race;
 mod data_race_handler;
 pub mod init_once;
-mod range_object_map;
 pub mod sync;
 pub mod thread;
 mod vector_clock;
diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs
index 56c19794854..878afdf2517 100644
--- a/src/tools/miri/src/concurrency/thread.rs
+++ b/src/tools/miri/src/concurrency/thread.rs
@@ -186,15 +186,15 @@ pub struct Thread<'tcx> {
     /// The join status.
     join_status: ThreadJoinStatus,
 
-    /// Stack of active panic payloads for the current thread. Used for storing
-    /// the argument of the call to `miri_start_unwind` (the panic payload) when unwinding.
+    /// Stack of active unwind payloads for the current thread. Used for storing
+    /// the argument of the call to `miri_start_unwind` (the payload) when unwinding.
     /// This is pointer-sized, and matches the `Payload` type in `src/libpanic_unwind/miri.rs`.
     ///
     /// In real unwinding, the payload gets passed as an argument to the landing pad,
     /// which then forwards it to 'Resume'. However this argument is implicit in MIR,
     /// so we have to store it out-of-band. When there are multiple active unwinds,
     /// the innermost one is always caught first, so we can store them as a stack.
-    pub(crate) panic_payloads: Vec<ImmTy<'tcx>>,
+    pub(crate) unwind_payloads: Vec<ImmTy<'tcx>>,
 
     /// Last OS error location in memory. It is a 32-bit integer.
     pub(crate) last_error: Option<MPlaceTy<'tcx>>,
@@ -282,7 +282,7 @@ impl<'tcx> Thread<'tcx> {
             stack: Vec::new(),
             top_user_relevant_frame: None,
             join_status: ThreadJoinStatus::Joinable,
-            panic_payloads: Vec::new(),
+            unwind_payloads: Vec::new(),
             last_error: None,
             on_stack_empty,
         }
@@ -292,7 +292,7 @@ impl<'tcx> Thread<'tcx> {
 impl VisitProvenance for Thread<'_> {
     fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
         let Thread {
-            panic_payloads: panic_payload,
+            unwind_payloads: panic_payload,
             last_error,
             stack,
             top_user_relevant_frame: _,
@@ -677,6 +677,8 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
     fn run_on_stack_empty(&mut self) -> InterpResult<'tcx, Poll<()>> {
         let this = self.eval_context_mut();
         // Inform GenMC that a thread has finished all user code. GenMC needs to know this for scheduling.
+        // FIXME(GenMC): Thread-local destructors *are* user code, so this is odd. Also now that we
+        // support pre-main constructors, it can get called there as well.
         if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() {
             let thread_id = this.active_thread();
             genmc_ctx.handle_thread_stack_empty(thread_id);
@@ -894,7 +896,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             start_abi,
             &[func_arg],
             Some(&ret_place),
-            StackPopCleanup::Root { cleanup: true },
+            ReturnContinuation::Stop { cleanup: true },
         )?;
 
         // Restore the old active thread frame.
diff --git a/src/tools/miri/src/concurrency/weak_memory.rs b/src/tools/miri/src/concurrency/weak_memory.rs
index 95c010be2fd..a752ef7e454 100644
--- a/src/tools/miri/src/concurrency/weak_memory.rs
+++ b/src/tools/miri/src/concurrency/weak_memory.rs
@@ -90,9 +90,9 @@ use rustc_data_structures::fx::FxHashMap;
 
 use super::AllocDataRaceHandler;
 use super::data_race::{GlobalState as DataRaceState, ThreadClockSet};
-use super::range_object_map::{AccessType, RangeObjectMap};
 use super::vector_clock::{VClock, VTimestamp, VectorIdx};
 use crate::concurrency::GlobalDataRaceHandler;
+use crate::data_structures::range_object_map::{AccessType, RangeObjectMap};
 use crate::*;
 
 pub type AllocState = StoreBufferAlloc;
diff --git a/src/tools/miri/src/range_map.rs b/src/tools/miri/src/data_structures/dedup_range_map.rs
index 29a5a8537a4..56e9883b1ca 100644
--- a/src/tools/miri/src/range_map.rs
+++ b/src/tools/miri/src/data_structures/dedup_range_map.rs
@@ -17,18 +17,18 @@ struct Elem<T> {
     data: T,
 }
 #[derive(Clone, Debug)]
-pub struct RangeMap<T> {
+pub struct DedupRangeMap<T> {
     v: Vec<Elem<T>>,
 }
 
-impl<T> RangeMap<T> {
+impl<T> DedupRangeMap<T> {
     /// Creates a new `RangeMap` for the given size, and with the given initial value used for
     /// the entire range.
     #[inline(always)]
-    pub fn new(size: Size, init: T) -> RangeMap<T> {
+    pub fn new(size: Size, init: T) -> DedupRangeMap<T> {
         let size = size.bytes();
         let v = if size > 0 { vec![Elem { range: 0..size, data: init }] } else { Vec::new() };
-        RangeMap { v }
+        DedupRangeMap { v }
     }
 
     pub fn size(&self) -> Size {
@@ -246,7 +246,7 @@ mod tests {
     use super::*;
 
     /// Query the map at every offset in the range and collect the results.
-    fn to_vec<T: Copy>(map: &RangeMap<T>, offset: u64, len: u64) -> Vec<T> {
+    fn to_vec<T: Copy>(map: &DedupRangeMap<T>, offset: u64, len: u64) -> Vec<T> {
         (offset..offset + len)
             .map(|i| {
                 map.iter(Size::from_bytes(i), Size::from_bytes(1)).next().map(|(_, &t)| t).unwrap()
@@ -256,7 +256,7 @@ mod tests {
 
     #[test]
     fn basic_insert() {
-        let mut map = RangeMap::<i32>::new(Size::from_bytes(20), -1);
+        let mut map = DedupRangeMap::<i32>::new(Size::from_bytes(20), -1);
         // Insert.
         for (_, x) in map.iter_mut(Size::from_bytes(10), Size::from_bytes(1)) {
             *x = 42;
@@ -278,7 +278,7 @@ mod tests {
 
     #[test]
     fn gaps() {
-        let mut map = RangeMap::<i32>::new(Size::from_bytes(20), -1);
+        let mut map = DedupRangeMap::<i32>::new(Size::from_bytes(20), -1);
         for (_, x) in map.iter_mut(Size::from_bytes(11), Size::from_bytes(1)) {
             *x = 42;
         }
@@ -319,26 +319,26 @@ mod tests {
     #[test]
     #[should_panic]
     fn out_of_range_iter_mut() {
-        let mut map = RangeMap::<i32>::new(Size::from_bytes(20), -1);
+        let mut map = DedupRangeMap::<i32>::new(Size::from_bytes(20), -1);
         let _ = map.iter_mut(Size::from_bytes(11), Size::from_bytes(11));
     }
 
     #[test]
     #[should_panic]
     fn out_of_range_iter() {
-        let map = RangeMap::<i32>::new(Size::from_bytes(20), -1);
+        let map = DedupRangeMap::<i32>::new(Size::from_bytes(20), -1);
         let _ = map.iter(Size::from_bytes(11), Size::from_bytes(11));
     }
 
     #[test]
     fn empty_map_iter() {
-        let map = RangeMap::<i32>::new(Size::from_bytes(0), -1);
+        let map = DedupRangeMap::<i32>::new(Size::from_bytes(0), -1);
         let _ = map.iter(Size::from_bytes(0), Size::from_bytes(0));
     }
 
     #[test]
     fn empty_map_iter_mut() {
-        let mut map = RangeMap::<i32>::new(Size::from_bytes(0), -1);
+        let mut map = DedupRangeMap::<i32>::new(Size::from_bytes(0), -1);
         let _ = map.iter_mut(Size::from_bytes(0), Size::from_bytes(0));
     }
 }
diff --git a/src/tools/miri/src/data_structures/mod.rs b/src/tools/miri/src/data_structures/mod.rs
new file mode 100644
index 00000000000..d4468bc57ea
--- /dev/null
+++ b/src/tools/miri/src/data_structures/mod.rs
@@ -0,0 +1,3 @@
+pub mod dedup_range_map;
+pub mod mono_hash_map;
+pub mod range_object_map;
diff --git a/src/tools/miri/src/mono_hash_map.rs b/src/tools/miri/src/data_structures/mono_hash_map.rs
index 220233f8ff5..220233f8ff5 100644
--- a/src/tools/miri/src/mono_hash_map.rs
+++ b/src/tools/miri/src/data_structures/mono_hash_map.rs
diff --git a/src/tools/miri/src/concurrency/range_object_map.rs b/src/tools/miri/src/data_structures/range_object_map.rs
index 4c9cf3dc635..4c9cf3dc635 100644
--- a/src/tools/miri/src/concurrency/range_object_map.rs
+++ b/src/tools/miri/src/data_structures/range_object_map.rs
diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs
index 63578912c24..be6404f64e8 100644
--- a/src/tools/miri/src/eval.rs
+++ b/src/tools/miri/src/eval.rs
@@ -11,14 +11,14 @@ use rustc_abi::ExternAbi;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_hir::def::Namespace;
 use rustc_hir::def_id::DefId;
-use rustc_middle::ty::layout::LayoutCx;
+use rustc_middle::ty::layout::{HasTyCtxt, HasTypingEnv, LayoutCx};
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_session::config::EntryFnType;
 
 use crate::concurrency::GenmcCtx;
 use crate::concurrency::thread::TlsAllocAction;
 use crate::diagnostics::report_leaks;
-use crate::shims::tls;
+use crate::shims::{global_ctor, tls};
 use crate::*;
 
 #[derive(Copy, Clone, Debug)]
@@ -216,9 +216,17 @@ impl Default for MiriConfig {
 }
 
 /// The state of the main thread. Implementation detail of `on_main_stack_empty`.
-#[derive(Default, Debug)]
+#[derive(Debug)]
 enum MainThreadState<'tcx> {
-    #[default]
+    GlobalCtors {
+        ctor_state: global_ctor::GlobalCtorState<'tcx>,
+        /// The main function to call.
+        entry_id: DefId,
+        entry_type: MiriEntryFnType,
+        /// Arguments passed to `main`.
+        argc: ImmTy<'tcx>,
+        argv: ImmTy<'tcx>,
+    },
     Running,
     TlsDtors(tls::TlsDtorsState<'tcx>),
     Yield {
@@ -234,6 +242,15 @@ impl<'tcx> MainThreadState<'tcx> {
     ) -> InterpResult<'tcx, Poll<()>> {
         use MainThreadState::*;
         match self {
+            GlobalCtors { ctor_state, entry_id, entry_type, argc, argv } => {
+                match ctor_state.on_stack_empty(this)? {
+                    Poll::Pending => {} // just keep going
+                    Poll::Ready(()) => {
+                        call_main(this, *entry_id, *entry_type, argc.clone(), argv.clone())?;
+                        *self = Running;
+                    }
+                }
+            }
             Running => {
                 *self = TlsDtors(Default::default());
             }
@@ -309,13 +326,6 @@ pub fn create_ecx<'tcx>(
         MiriMachine::new(config, layout_cx, genmc_ctx),
     );
 
-    // Some parts of initialization require a full `InterpCx`.
-    MiriMachine::late_init(&mut ecx, config, {
-        let mut state = MainThreadState::default();
-        // Cannot capture anything GC-relevant here.
-        Box::new(move |m| state.on_main_stack_empty(m))
-    })?;
-
     // Make sure we have MIR. We check MIR for some stable monomorphic function in libcore.
     let sentinel =
         helpers::try_resolve_path(tcx, &["core", "ascii", "escape_default"], Namespace::ValueNS);
@@ -326,15 +336,9 @@ pub fn create_ecx<'tcx>(
         );
     }
 
-    // Setup first stack frame.
-    let entry_instance = ty::Instance::mono(tcx, entry_id);
-
-    // First argument is constructed later, because it's skipped for `miri_start.`
-
-    // Second argument (argc): length of `config.args`.
+    // Compute argc and argv from `config.args`.
     let argc =
         ImmTy::from_int(i64::try_from(config.args.len()).unwrap(), ecx.machine.layouts.isize);
-    // Third argument (`argv`): created from `config.args`.
     let argv = {
         // Put each argument in memory, collect pointers.
         let mut argvs = Vec::<Immediate<Provenance>>::with_capacity(config.args.len());
@@ -359,7 +363,7 @@ pub fn create_ecx<'tcx>(
             ecx.write_immediate(arg, &place)?;
         }
         ecx.mark_immutable(&argvs_place);
-        // Store `argc` and `argv` for macOS `_NSGetArg{c,v}`.
+        // Store `argc` and `argv` for macOS `_NSGetArg{c,v}`, and for the GC to see them.
         {
             let argc_place =
                 ecx.allocate(ecx.machine.layouts.isize, MiriMemoryKind::Machine.into())?;
@@ -374,7 +378,7 @@ pub fn create_ecx<'tcx>(
             ecx.machine.argv = Some(argv_place.ptr());
         }
         // Store command line as UTF-16 for Windows `GetCommandLineW`.
-        {
+        if tcx.sess.target.os == "windows" {
             // Construct a command string with all the arguments.
             let cmd_utf16: Vec<u16> = args_to_utf16_command_string(config.args.iter());
 
@@ -395,11 +399,43 @@ pub fn create_ecx<'tcx>(
         ImmTy::from_immediate(imm, layout)
     };
 
+    // Some parts of initialization require a full `InterpCx`.
+    MiriMachine::late_init(&mut ecx, config, {
+        let mut main_thread_state = MainThreadState::GlobalCtors {
+            entry_id,
+            entry_type,
+            argc,
+            argv,
+            ctor_state: global_ctor::GlobalCtorState::default(),
+        };
+
+        // Cannot capture anything GC-relevant here.
+        // `argc` and `argv` *are* GC_relevant, but they also get stored in `machine.argc` and
+        // `machine.argv` so we are good.
+        Box::new(move |m| main_thread_state.on_main_stack_empty(m))
+    })?;
+
+    interp_ok(ecx)
+}
+
+// Call the entry function.
+fn call_main<'tcx>(
+    ecx: &mut MiriInterpCx<'tcx>,
+    entry_id: DefId,
+    entry_type: MiriEntryFnType,
+    argc: ImmTy<'tcx>,
+    argv: ImmTy<'tcx>,
+) -> InterpResult<'tcx, ()> {
+    let tcx = ecx.tcx();
+
+    // Setup first stack frame.
+    let entry_instance = ty::Instance::mono(tcx, entry_id);
+
     // Return place (in static memory so that it does not count as leak).
     let ret_place = ecx.allocate(ecx.machine.layouts.isize, MiriMemoryKind::Machine.into())?;
     ecx.machine.main_fn_ret_place = Some(ret_place.clone());
-    // Call start function.
 
+    // Call start function.
     match entry_type {
         MiriEntryFnType::Rustc(EntryFnType::Main { .. }) => {
             let start_id = tcx.lang_items().start_fn().unwrap_or_else(|| {
@@ -409,7 +445,7 @@ pub fn create_ecx<'tcx>(
             let main_ret_ty = main_ret_ty.no_bound_vars().unwrap();
             let start_instance = ty::Instance::try_resolve(
                 tcx,
-                typing_env,
+                ecx.typing_env(),
                 start_id,
                 tcx.mk_args(&[ty::GenericArg::from(main_ret_ty)]),
             )
@@ -427,7 +463,7 @@ pub fn create_ecx<'tcx>(
                 ExternAbi::Rust,
                 &[
                     ImmTy::from_scalar(
-                        Scalar::from_pointer(main_ptr, &ecx),
+                        Scalar::from_pointer(main_ptr, ecx),
                         // FIXME use a proper fn ptr type
                         ecx.machine.layouts.const_raw_ptr,
                     ),
@@ -436,7 +472,7 @@ pub fn create_ecx<'tcx>(
                     ImmTy::from_uint(sigpipe, ecx.machine.layouts.u8),
                 ],
                 Some(&ret_place),
-                StackPopCleanup::Root { cleanup: true },
+                ReturnContinuation::Stop { cleanup: true },
             )?;
         }
         MiriEntryFnType::MiriStart => {
@@ -445,12 +481,12 @@ pub fn create_ecx<'tcx>(
                 ExternAbi::Rust,
                 &[argc, argv],
                 Some(&ret_place),
-                StackPopCleanup::Root { cleanup: true },
+                ReturnContinuation::Stop { cleanup: true },
             )?;
         }
     }
 
-    interp_ok(ecx)
+    interp_ok(())
 }
 
 /// Evaluates the entry function specified by `entry_id`.
diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs
index b259243602e..ccfff7fa94b 100644
--- a/src/tools/miri/src/helpers.rs
+++ b/src/tools/miri/src/helpers.rs
@@ -13,7 +13,7 @@ use rustc_index::IndexVec;
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use rustc_middle::middle::dependency_format::Linkage;
 use rustc_middle::middle::exported_symbols::ExportedSymbol;
-use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, MaybeResult, TyAndLayout};
+use rustc_middle::ty::layout::{LayoutOf, MaybeResult, TyAndLayout};
 use rustc_middle::ty::{self, Binder, FloatTy, FnSig, IntTy, Ty, TyCtxt, UintTy};
 use rustc_session::config::CrateType;
 use rustc_span::{Span, Symbol};
@@ -444,7 +444,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         caller_abi: ExternAbi,
         args: &[ImmTy<'tcx>],
         dest: Option<&MPlaceTy<'tcx>>,
-        stack_pop: StackPopCleanup,
+        cont: ReturnContinuation,
     ) -> InterpResult<'tcx> {
         let this = self.eval_context_mut();
 
@@ -472,7 +472,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             &args.iter().map(|a| FnArg::Copy(a.clone().into())).collect::<Vec<_>>(),
             /*with_caller_location*/ false,
             &dest.into(),
-            stack_pop,
+            cont,
         )
     }
 
@@ -1235,8 +1235,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         interp_ok(())
     }
 
-    /// Lookup an array of immediates stored as a linker section of name `name`.
-    fn lookup_link_section(&mut self, name: &str) -> InterpResult<'tcx, Vec<ImmTy<'tcx>>> {
+    /// Lookup an array of immediates from any linker sections matching the provided predicate.
+    fn lookup_link_section(
+        &mut self,
+        include_name: impl Fn(&str) -> bool,
+    ) -> InterpResult<'tcx, Vec<ImmTy<'tcx>>> {
         let this = self.eval_context_mut();
         let tcx = this.tcx.tcx;
 
@@ -1247,7 +1250,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             let Some(link_section) = attrs.link_section else {
                 return interp_ok(());
             };
-            if link_section.as_str() == name {
+            if include_name(link_section.as_str()) {
                 let instance = ty::Instance::mono(tcx, def_id);
                 let const_val = this.eval_global(instance).unwrap_or_else(|err| {
                     panic!(
@@ -1431,3 +1434,44 @@ impl ToU64 for usize {
         self.try_into().unwrap()
     }
 }
+
+/// This struct is needed to enforce `#[must_use]` on values produced by [enter_trace_span] even
+/// when the "tracing" feature is not enabled.
+#[must_use]
+pub struct MaybeEnteredTraceSpan {
+    #[cfg(feature = "tracing")]
+    pub _entered_span: tracing::span::EnteredSpan,
+}
+
+/// Enters a [tracing::info_span] only if the "tracing" feature is enabled, otherwise does nothing.
+/// This is like [rustc_const_eval::enter_trace_span] except that it does not depend on the
+/// [Machine] trait to check if tracing is enabled, because from the Miri codebase we can directly
+/// check whether the "tracing" feature is enabled, unlike from the rustc_const_eval codebase.
+///
+/// In addition to the syntax accepted by [tracing::span!], this macro optionally allows passing
+/// the span name (i.e. the first macro argument) in the form `NAME::SUBNAME` (without quotes) to
+/// indicate that the span has name "NAME" (usually the name of the component) and has an additional
+/// more specific name "SUBNAME" (usually the function name). The latter is passed to the [tracing]
+/// infrastructure as a span field with the name "NAME". This allows not being distracted by
+/// subnames when looking at the trace in <https://ui.perfetto.dev>, but when deeper introspection
+/// is needed within a component, it's still possible to view the subnames directly in the UI by
+/// selecting a span, clicking on the "NAME" argument on the right, and clicking on "Visualize
+/// argument values".
+/// ```rust
+/// // for example, the first will expand to the second
+/// enter_trace_span!(borrow_tracker::on_stack_pop, /* ... */)
+/// enter_trace_span!("borrow_tracker", borrow_tracker = "on_stack_pop", /* ... */)
+/// ```
+#[macro_export]
+macro_rules! enter_trace_span {
+    ($name:ident :: $subname:ident $($tt:tt)*) => {{
+        enter_trace_span!(stringify!($name), $name = %stringify!(subname) $($tt)*)
+    }};
+
+    ($($tt:tt)*) => {
+        $crate::MaybeEnteredTraceSpan {
+            #[cfg(feature = "tracing")]
+            _entered_span: tracing::info_span!($($tt)*).entered()
+        }
+    };
+}
diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs
index c86e33e5185..a591d21071d 100644
--- a/src/tools/miri/src/lib.rs
+++ b/src/tools/miri/src/lib.rs
@@ -15,6 +15,7 @@
 #![feature(unqualified_local_imports)]
 #![feature(derive_coerce_pointee)]
 #![feature(arbitrary_self_types)]
+#![feature(iter_advance_by)]
 // Configure clippy and other lints
 #![allow(
     clippy::collapsible_else_if,
@@ -75,16 +76,15 @@ mod alloc_addresses;
 mod borrow_tracker;
 mod clock;
 mod concurrency;
+mod data_structures;
 mod diagnostics;
 mod eval;
 mod helpers;
 mod intrinsics;
 mod machine;
 mod math;
-mod mono_hash_map;
 mod operator;
 mod provenance_gc;
-mod range_map;
 mod shims;
 
 // Establish a "crate-wide prelude": we often import `crate::*`.
@@ -97,10 +97,10 @@ pub use rustc_const_eval::interpret::{self, AllocMap, Provenance as _};
 use rustc_middle::{bug, span_bug};
 use tracing::{info, trace};
 
-//#[cfg(target_os = "linux")]
-//pub mod native_lib {
-//    pub use crate::shims::{init_sv, register_retcode_sv};
-//}
+#[cfg(target_os = "linux")]
+pub mod native_lib {
+    pub use crate::shims::{init_sv, register_retcode_sv};
+}
 
 // Type aliases that set the provenance parameter.
 pub type Pointer = interpret::Pointer<Option<machine::Provenance>>;
@@ -132,6 +132,8 @@ pub use crate::concurrency::thread::{
     ThreadManager, TimeoutAnchor, TimeoutClock, UnblockKind,
 };
 pub use crate::concurrency::{GenmcConfig, GenmcCtx};
+pub use crate::data_structures::dedup_range_map::DedupRangeMap;
+pub use crate::data_structures::mono_hash_map::MonoHashMap;
 pub use crate::diagnostics::{
     EvalContextExt as _, NonHaltingDiagnostic, TerminationInfo, report_error,
 };
@@ -139,24 +141,25 @@ pub use crate::eval::{
     AlignmentCheck, BacktraceStyle, IsolatedOp, MiriConfig, MiriEntryFnType, RejectOpWith,
     ValidationMode, create_ecx, eval_entry,
 };
-pub use crate::helpers::{AccessKind, EvalContextExt as _, ToU64 as _, ToUsize as _};
+pub use crate::helpers::{
+    AccessKind, EvalContextExt as _, MaybeEnteredTraceSpan, ToU64 as _, ToUsize as _,
+};
 pub use crate::intrinsics::EvalContextExt as _;
 pub use crate::machine::{
     AllocExtra, DynMachineCallback, FrameExtra, MachineCallback, MemoryKind, MiriInterpCx,
     MiriInterpCxExt, MiriMachine, MiriMemoryKind, PrimitiveLayouts, Provenance, ProvenanceExtra,
 };
-pub use crate::mono_hash_map::MonoHashMap;
 pub use crate::operator::EvalContextExt as _;
 pub use crate::provenance_gc::{EvalContextExt as _, LiveAllocs, VisitProvenance, VisitWith};
-pub use crate::range_map::RangeMap;
 pub use crate::shims::EmulateItemResult;
 pub use crate::shims::env::{EnvVars, EvalContextExt as _};
 pub use crate::shims::foreign_items::{DynSym, EvalContextExt as _};
 pub use crate::shims::io_error::{EvalContextExt as _, IoError, LibcError};
 pub use crate::shims::os_str::EvalContextExt as _;
-pub use crate::shims::panic::{CatchUnwindData, EvalContextExt as _};
+pub use crate::shims::panic::EvalContextExt as _;
 pub use crate::shims::time::EvalContextExt as _;
 pub use crate::shims::tls::TlsData;
+pub use crate::shims::unwind::{CatchUnwindData, EvalContextExt as _};
 
 /// Insert rustc arguments at the beginning of the argument list that Miri wants to be
 /// set per default, for maximal validation power.
diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs
index 4f3dc485325..f309e34c75b 100644
--- a/src/tools/miri/src/machine.rs
+++ b/src/tools/miri/src/machine.rs
@@ -1014,8 +1014,6 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
 
     const PANIC_ON_ALLOC_FAIL: bool = false;
 
-    const TRACING_ENABLED: bool = cfg!(feature = "tracing");
-
     #[inline(always)]
     fn enforce_alignment(ecx: &MiriInterpCx<'tcx>) -> bool {
         ecx.machine.check_alignment != AlignmentCheck::None
@@ -1088,7 +1086,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
         ecx: &MiriInterpCx<'tcx>,
         instance: ty::Instance<'tcx>,
     ) -> InterpResult<'tcx> {
-        let attrs = ecx.tcx.codegen_fn_attrs(instance.def_id());
+        let attrs = ecx.tcx.codegen_instance_attrs(instance.def);
         if attrs
             .target_features
             .iter()
@@ -1199,7 +1197,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
             ExternAbi::Rust,
             &[],
             None,
-            StackPopCleanup::Goto { ret: None, unwind: mir::UnwindAction::Unreachable },
+            ReturnContinuation::Goto { ret: None, unwind: mir::UnwindAction::Unreachable },
         )?;
         interp_ok(())
     }
@@ -1792,7 +1790,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
                 ecx.tcx.sess.opts.unstable_opts.cross_crate_inline_threshold,
                 InliningThreshold::Always
             ) || !matches!(
-                ecx.tcx.codegen_fn_attrs(instance.def_id()).inline,
+                ecx.tcx.codegen_instance_attrs(instance.def).inline,
                 InlineAttr::Never
             );
             !is_generic && !can_be_inlined
@@ -1827,6 +1825,19 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
         #[cfg(not(target_os = "linux"))]
         MiriAllocParams::Global
     }
+
+    fn enter_trace_span(span: impl FnOnce() -> tracing::Span) -> impl EnteredTraceSpan {
+        #[cfg(feature = "tracing")]
+        {
+            span().entered()
+        }
+        #[cfg(not(feature = "tracing"))]
+        #[expect(clippy::unused_unit)]
+        {
+            let _ = span; // so we avoid the "unused variable" warning
+            ()
+        }
+    }
 }
 
 /// Trait for callbacks handling asynchronous machine operations.
diff --git a/src/tools/miri/src/shims/global_ctor.rs b/src/tools/miri/src/shims/global_ctor.rs
new file mode 100644
index 00000000000..c56251bbe63
--- /dev/null
+++ b/src/tools/miri/src/shims/global_ctor.rs
@@ -0,0 +1,98 @@
+//! Implement global constructors.
+
+use std::task::Poll;
+
+use rustc_abi::ExternAbi;
+use rustc_target::spec::BinaryFormat;
+
+use crate::*;
+
+#[derive(Debug, Default)]
+pub struct GlobalCtorState<'tcx>(GlobalCtorStatePriv<'tcx>);
+
+#[derive(Debug, Default)]
+enum GlobalCtorStatePriv<'tcx> {
+    #[default]
+    Init,
+    /// The list of constructor functions that we still have to call.
+    Ctors(Vec<ImmTy<'tcx>>),
+    Done,
+}
+
+impl<'tcx> GlobalCtorState<'tcx> {
+    pub fn on_stack_empty(
+        &mut self,
+        this: &mut MiriInterpCx<'tcx>,
+    ) -> InterpResult<'tcx, Poll<()>> {
+        use GlobalCtorStatePriv::*;
+        let new_state = 'new_state: {
+            match &mut self.0 {
+                Init => {
+                    let this = this.eval_context_mut();
+
+                    // Lookup constructors from the relevant magic link section.
+                    let ctors = match this.tcx.sess.target.binary_format {
+                        // Read the CRT library section on Windows.
+                        BinaryFormat::Coff =>
+                            this.lookup_link_section(|section| section == ".CRT$XCU")?,
+
+                        // Read the `__mod_init_func` section on macOS.
+                        BinaryFormat::MachO =>
+                            this.lookup_link_section(|section| {
+                                let mut parts = section.splitn(3, ',');
+                                let (segment_name, section_name, section_type) =
+                                    (parts.next(), parts.next(), parts.next());
+
+                                segment_name == Some("__DATA")
+                                    && section_name == Some("__mod_init_func")
+                                    // The `mod_init_funcs` directive ensures that the
+                                    // `S_MOD_INIT_FUNC_POINTERS` flag is set on the section. LLVM
+                                    // adds this automatically so we currently do not require it.
+                                    // FIXME: is this guaranteed LLVM behavior? If not, we shouldn't
+                                    // implicitly add it here. Also see
+                                    // <https://github.com/rust-lang/miri/pull/4459#discussion_r2200115629>.
+                                    && matches!(section_type, None | Some("mod_init_funcs"))
+                            })?,
+
+                        // Read the standard `.init_array` section on platforms that use ELF, or WASM,
+                        // which supports the same linker directive.
+                        // FIXME: Add support for `.init_array.N` and `.ctors`?
+                        BinaryFormat::Elf | BinaryFormat::Wasm =>
+                            this.lookup_link_section(|section| section == ".init_array")?,
+
+                        // Other platforms have no global ctor support.
+                        _ => break 'new_state Done,
+                    };
+
+                    break 'new_state Ctors(ctors);
+                }
+                Ctors(ctors) => {
+                    if let Some(ctor) = ctors.pop() {
+                        let this = this.eval_context_mut();
+
+                        let ctor = ctor.to_scalar().to_pointer(this)?;
+                        let thread_callback = this.get_ptr_fn(ctor)?.as_instance()?;
+
+                        // The signature of this function is `unsafe extern "C" fn()`.
+                        this.call_function(
+                            thread_callback,
+                            ExternAbi::C { unwind: false },
+                            &[],
+                            None,
+                            ReturnContinuation::Stop { cleanup: true },
+                        )?;
+
+                        return interp_ok(Poll::Pending); // we stay in this state (but `ctors` got shorter)
+                    }
+
+                    // No more constructors to run.
+                    break 'new_state Done;
+                }
+                Done => return interp_ok(Poll::Ready(())),
+            }
+        };
+
+        self.0 = new_state;
+        interp_ok(Poll::Pending)
+    }
+}
diff --git a/src/tools/miri/src/shims/mod.rs b/src/tools/miri/src/shims/mod.rs
index f09fc546b3e..75540f6f150 100644
--- a/src/tools/miri/src/shims/mod.rs
+++ b/src/tools/miri/src/shims/mod.rs
@@ -14,15 +14,17 @@ mod x86;
 pub mod env;
 pub mod extern_static;
 pub mod foreign_items;
+pub mod global_ctor;
 pub mod io_error;
 pub mod os_str;
 pub mod panic;
 pub mod time;
 pub mod tls;
+pub mod unwind;
 
 pub use self::files::FdTable;
-//#[cfg(target_os = "linux")]
-//pub use self::native_lib::trace::{init_sv, register_retcode_sv};
+#[cfg(target_os = "linux")]
+pub use self::native_lib::trace::{init_sv, register_retcode_sv};
 pub use self::unix::{DirTable, EpollInterestTable};
 
 /// What needs to be done after emulating an item (a shim or an intrinsic) is done.
diff --git a/src/tools/miri/src/shims/native_lib/mod.rs b/src/tools/miri/src/shims/native_lib/mod.rs
index 9b30d8ce78b..fb7b1df41a4 100644
--- a/src/tools/miri/src/shims/native_lib/mod.rs
+++ b/src/tools/miri/src/shims/native_lib/mod.rs
@@ -1,9 +1,5 @@
 //! Implements calling functions from a native library.
 
-// FIXME: disabled since it fails to build on many targets.
-//#[cfg(target_os = "linux")]
-//pub mod trace;
-
 use std::ops::Deref;
 
 use libffi::high::call as ffi;
@@ -13,14 +9,68 @@ use rustc_middle::mir::interpret::Pointer;
 use rustc_middle::ty::{self as ty, IntTy, UintTy};
 use rustc_span::Symbol;
 
-//#[cfg(target_os = "linux")]
-//use self::trace::Supervisor;
+#[cfg_attr(
+    not(all(
+        target_os = "linux",
+        target_env = "gnu",
+        any(target_arch = "x86", target_arch = "x86_64")
+    )),
+    path = "trace/stub.rs"
+)]
+pub mod trace;
+
 use crate::*;
 
-//#[cfg(target_os = "linux")]
-//type CallResult<'tcx> = InterpResult<'tcx, (ImmTy<'tcx>, Option<self::trace::messages::MemEvents>)>;
-//#[cfg(not(target_os = "linux"))]
-type CallResult<'tcx> = InterpResult<'tcx, (ImmTy<'tcx>, Option<!>)>;
+/// The final results of an FFI trace, containing every relevant event detected
+/// by the tracer.
+#[allow(dead_code)]
+#[cfg_attr(target_os = "linux", derive(serde::Serialize, serde::Deserialize))]
+#[derive(Debug)]
+pub struct MemEvents {
+    /// An list of memory accesses that occurred, in the order they occurred in.
+    pub acc_events: Vec<AccessEvent>,
+}
+
+/// A single memory access.
+#[allow(dead_code)]
+#[cfg_attr(target_os = "linux", derive(serde::Serialize, serde::Deserialize))]
+#[derive(Clone, Debug)]
+pub enum AccessEvent {
+    /// A read occurred on this memory range.
+    Read(AccessRange),
+    /// A write may have occurred on this memory range.
+    /// Some instructions *may* write memory without *always* doing that,
+    /// so this can be an over-approximation.
+    /// The range info, however, is reliable if the access did happen.
+    /// If the second field is true, the access definitely happened.
+    Write(AccessRange, bool),
+}
+
+impl AccessEvent {
+    fn get_range(&self) -> AccessRange {
+        match self {
+            AccessEvent::Read(access_range) => access_range.clone(),
+            AccessEvent::Write(access_range, _) => access_range.clone(),
+        }
+    }
+}
+
+/// The memory touched by a given access.
+#[allow(dead_code)]
+#[cfg_attr(target_os = "linux", derive(serde::Serialize, serde::Deserialize))]
+#[derive(Clone, Debug)]
+pub struct AccessRange {
+    /// The base address in memory where an access occurred.
+    pub addr: usize,
+    /// The number of bytes affected from the base.
+    pub size: usize,
+}
+
+impl AccessRange {
+    fn end(&self) -> usize {
+        self.addr.strict_add(self.size)
+    }
+}
 
 impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
 trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
@@ -31,18 +81,17 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
         dest: &MPlaceTy<'tcx>,
         ptr: CodePtr,
         libffi_args: Vec<libffi::high::Arg<'a>>,
-    ) -> CallResult<'tcx> {
+    ) -> InterpResult<'tcx, (crate::ImmTy<'tcx>, Option<MemEvents>)> {
         let this = self.eval_context_mut();
-        //#[cfg(target_os = "linux")]
-        //let alloc = this.machine.allocator.as_ref().unwrap();
-
-        // SAFETY: We don't touch the machine memory past this point.
-        //#[cfg(target_os = "linux")]
-        //let (guard, stack_ptr) = unsafe { Supervisor::start_ffi(alloc) };
+        #[cfg(target_os = "linux")]
+        let alloc = this.machine.allocator.as_ref().unwrap();
+        #[cfg(not(target_os = "linux"))]
+        // Placeholder value.
+        let alloc = ();
 
-        // Call the function (`ptr`) with arguments `libffi_args`, and obtain the return value
-        // as the specified primitive integer type
-        let res = 'res: {
+        trace::Supervisor::do_ffi(alloc, || {
+            // Call the function (`ptr`) with arguments `libffi_args`, and obtain the return value
+            // as the specified primitive integer type
             let scalar = match dest.layout.ty.kind() {
                 // ints
                 ty::Int(IntTy::I8) => {
@@ -93,7 +142,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 // have the output_type `Tuple([])`.
                 ty::Tuple(t_list) if (*t_list).deref().is_empty() => {
                     unsafe { ffi::call::<()>(ptr, libffi_args.as_slice()) };
-                    break 'res interp_ok(ImmTy::uninit(dest.layout));
+                    return interp_ok(ImmTy::uninit(dest.layout));
                 }
                 ty::RawPtr(..) => {
                     let x = unsafe { ffi::call::<*const ()>(ptr, libffi_args.as_slice()) };
@@ -101,23 +150,14 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     Scalar::from_pointer(ptr, this)
                 }
                 _ =>
-                    break 'res Err(err_unsup_format!(
+                    return Err(err_unsup_format!(
                         "unsupported return type for native call: {:?}",
                         link_name
                     ))
                     .into(),
             };
             interp_ok(ImmTy::from_scalar(scalar, dest.layout))
-        };
-
-        // SAFETY: We got the guard and stack pointer from start_ffi, and
-        // the allocator is the same
-        //#[cfg(target_os = "linux")]
-        //let events = unsafe { Supervisor::end_ffi(alloc, guard, stack_ptr) };
-        //#[cfg(not(target_os = "linux"))]
-        let events = None;
-
-        interp_ok((res?, events))
+        })
     }
 
     /// Get the pointer to the function of the specified name in the shared object file,
@@ -169,6 +209,73 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
         }
         None
     }
+
+    /// Applies the `events` to Miri's internal state. The event vector must be
+    /// ordered sequentially by when the accesses happened, and the sizes are
+    /// assumed to be exact.
+    fn tracing_apply_accesses(&mut self, events: MemEvents) -> InterpResult<'tcx> {
+        let this = self.eval_context_mut();
+
+        for evt in events.acc_events {
+            let evt_rg = evt.get_range();
+            // LLVM at least permits vectorising accesses to adjacent allocations,
+            // so we cannot assume 1 access = 1 allocation. :(
+            let mut rg = evt_rg.addr..evt_rg.end();
+            while let Some(curr) = rg.next() {
+                let Some(alloc_id) = this.alloc_id_from_addr(
+                    curr.to_u64(),
+                    rg.len().try_into().unwrap(),
+                    /* only_exposed_allocations */ true,
+                ) else {
+                    throw_ub_format!("Foreign code did an out-of-bounds access!")
+                };
+                let alloc = this.get_alloc_raw(alloc_id)?;
+                // The logical and physical address of the allocation coincide, so we can use
+                // this instead of `addr_from_alloc_id`.
+                let alloc_addr = alloc.get_bytes_unchecked_raw().addr();
+
+                // Determine the range inside the allocation that this access covers. This range is
+                // in terms of offsets from the start of `alloc`. The start of the overlap range
+                // will be `curr`; the end will be the minimum of the end of the allocation and the
+                // end of the access' range.
+                let overlap = curr.strict_sub(alloc_addr)
+                    ..std::cmp::min(alloc.len(), rg.end.strict_sub(alloc_addr));
+                // Skip forward however many bytes of the access are contained in the current
+                // allocation, subtracting 1 since the overlap range includes the current addr
+                // that was already popped off of the range.
+                rg.advance_by(overlap.len().strict_sub(1)).unwrap();
+
+                match evt {
+                    AccessEvent::Read(_) => {
+                        // FIXME: ProvenanceMap should have something like get_range().
+                        let p_map = alloc.provenance();
+                        for idx in overlap {
+                            // If a provenance was read by the foreign code, expose it.
+                            if let Some(prov) = p_map.get(Size::from_bytes(idx), this) {
+                                this.expose_provenance(prov)?;
+                            }
+                        }
+                    }
+                    AccessEvent::Write(_, certain) => {
+                        // Sometimes we aren't certain if a write happened, in which case we
+                        // only initialise that data if the allocation is mutable.
+                        if certain || alloc.mutability.is_mut() {
+                            let (alloc, cx) = this.get_alloc_raw_mut(alloc_id)?;
+                            alloc.process_native_write(
+                                &cx.tcx,
+                                Some(AllocRange {
+                                    start: Size::from_bytes(overlap.start),
+                                    size: Size::from_bytes(overlap.len()),
+                                }),
+                            )
+                        }
+                    }
+                }
+            }
+        }
+
+        interp_ok(())
+    }
 }
 
 impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
@@ -194,6 +301,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             }
         };
 
+        // Do we have ptrace?
+        let tracing = trace::Supervisor::is_enabled();
+
         // Get the function arguments, and convert them to `libffi`-compatible form.
         let mut libffi_args = Vec::<CArg>::with_capacity(args.len());
         for arg in args.iter() {
@@ -213,12 +323,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 // The first time this happens, print a warning.
                 if !this.machine.native_call_mem_warned.replace(true) {
                     // Newly set, so first time we get here.
-                    this.emit_diagnostic(NonHaltingDiagnostic::NativeCallSharedMem {
-                        //#[cfg(target_os = "linux")]
-                        //tracing: self::trace::Supervisor::is_enabled(),
-                        //#[cfg(not(target_os = "linux"))]
-                        tracing: false,
-                    });
+                    this.emit_diagnostic(NonHaltingDiagnostic::NativeCallSharedMem { tracing });
                 }
 
                 this.expose_provenance(prov)?;
@@ -231,7 +336,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             .collect::<Vec<libffi::high::Arg<'_>>>();
 
         // Prepare all exposed memory (both previously exposed, and just newly exposed since a
-        // pointer was passed as argument).
+        // pointer was passed as argument). Uninitialised memory is left as-is, but any data
+        // exposed this way is garbage anyway.
         this.visit_reachable_allocs(this.exposed_allocs(), |this, alloc_id, info| {
             // If there is no data behind this pointer, skip this.
             if !matches!(info.kind, AllocKind::LiveData) {
@@ -244,15 +350,23 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // be read by FFI. The `black_box` is defensive programming as LLVM likes
             // to (incorrectly) optimize away ptr2int casts whose result is unused.
             std::hint::black_box(alloc.get_bytes_unchecked_raw().expose_provenance());
-            // Expose all provenances in this allocation, since the native code can do $whatever.
-            for prov in alloc.provenance().provenances() {
-                this.expose_provenance(prov)?;
+
+            if !tracing {
+                // Expose all provenances in this allocation, since the native code can do $whatever.
+                // Can be skipped when tracing; in that case we'll expose just the actually-read parts later.
+                for prov in alloc.provenance().provenances() {
+                    this.expose_provenance(prov)?;
+                }
             }
 
             // Prepare for possible write from native code if mutable.
             if info.mutbl.is_mut() {
-                let alloc = &mut this.get_alloc_raw_mut(alloc_id)?.0;
-                alloc.prepare_for_native_access();
+                let (alloc, cx) = this.get_alloc_raw_mut(alloc_id)?;
+                // These writes could initialize everything and wreck havoc with the pointers.
+                // We can skip that when tracing; in that case we'll later do that only for the memory that got actually written.
+                if !tracing {
+                    alloc.process_native_write(&cx.tcx, None);
+                }
                 // Also expose *mutable* provenance for the interpreter-level allocation.
                 std::hint::black_box(alloc.get_bytes_unchecked_raw_mut().expose_provenance());
             }
@@ -264,10 +378,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let (ret, maybe_memevents) =
             this.call_native_with_args(link_name, dest, code_ptr, libffi_args)?;
 
-        if cfg!(target_os = "linux")
-            && let Some(events) = maybe_memevents
-        {
-            trace!("Registered FFI events:\n{events:#0x?}");
+        if tracing {
+            this.tracing_apply_accesses(maybe_memevents.unwrap())?;
         }
 
         this.write_immediate(*ret, dest)?;
diff --git a/src/tools/miri/src/shims/native_lib/trace/child.rs b/src/tools/miri/src/shims/native_lib/trace/child.rs
index 4961e875c77..de26cb0fe55 100644
--- a/src/tools/miri/src/shims/native_lib/trace/child.rs
+++ b/src/tools/miri/src/shims/native_lib/trace/child.rs
@@ -4,12 +4,22 @@ use std::rc::Rc;
 use ipc_channel::ipc;
 use nix::sys::{ptrace, signal};
 use nix::unistd;
+use rustc_const_eval::interpret::InterpResult;
 
 use super::CALLBACK_STACK_SIZE;
-use super::messages::{Confirmation, MemEvents, StartFfiInfo, TraceRequest};
+use super::messages::{Confirmation, StartFfiInfo, TraceRequest};
 use super::parent::{ChildListener, sv_loop};
 use crate::alloc::isolated_alloc::IsolatedAlloc;
+use crate::shims::native_lib::MemEvents;
 
+/// A handle to the single, shared supervisor process across all `MiriMachine`s.
+/// Since it would be very difficult to trace multiple FFI calls in parallel, we
+/// need to ensure that either (a) only one `MiriMachine` is performing an FFI call
+/// at any given time, or (b) there are distinct supervisor and child processes for
+/// each machine. The former was chosen here.
+///
+/// This should only contain a `None` if the supervisor has not (yet) been initialised;
+/// otherwise, if `init_sv` was called and did not error, this will always be nonempty.
 static SUPERVISOR: std::sync::Mutex<Option<Supervisor>> = std::sync::Mutex::new(None);
 
 /// The main means of communication between the child and parent process,
@@ -34,32 +44,23 @@ impl Supervisor {
         SUPERVISOR.lock().unwrap().is_some()
     }
 
-    /// Begins preparations for doing an FFI call. This should be called at
-    /// the last possible moment before entering said call. `alloc` points to
-    /// the allocator which handed out the memory used for this machine.
-    ///
+    /// Performs an arbitrary FFI call, enabling tracing from the supervisor.
     /// As this locks the supervisor via a mutex, no other threads may enter FFI
-    /// until this one returns and its guard is dropped via `end_ffi`. The
-    /// pointer returned should be passed to `end_ffi` to avoid a memory leak.
-    ///
-    /// SAFETY: The resulting guard must be dropped *via `end_ffi`* immediately
-    /// after the desired call has concluded.
-    pub unsafe fn start_ffi(
+    /// until this function returns.
+    pub fn do_ffi<'tcx>(
         alloc: &Rc<RefCell<IsolatedAlloc>>,
-    ) -> (std::sync::MutexGuard<'static, Option<Supervisor>>, Option<*mut [u8; CALLBACK_STACK_SIZE]>)
-    {
+        f: impl FnOnce() -> InterpResult<'tcx, crate::ImmTy<'tcx>>,
+    ) -> InterpResult<'tcx, (crate::ImmTy<'tcx>, Option<MemEvents>)> {
         let mut sv_guard = SUPERVISOR.lock().unwrap();
-        // If the supervisor is not initialised for whatever reason, fast-fail.
-        // This might be desired behaviour, as even on platforms where ptracing
-        // is not implemented it enables us to enforce that only one FFI call
+        // If the supervisor is not initialised for whatever reason, fast-return.
+        // As a side-effect, even on platforms where ptracing
+        // is not implemented, we enforce that only one FFI call
         // happens at a time.
-        let Some(sv) = sv_guard.take() else {
-            return (sv_guard, None);
-        };
+        let Some(sv) = sv_guard.as_mut() else { return f().map(|v| (v, None)) };
 
         // Get pointers to all the pages the supervisor must allow accesses in
         // and prepare the callback stack.
-        let page_ptrs = alloc.borrow().pages();
+        let page_ptrs = alloc.borrow().pages().collect();
         let raw_stack_ptr: *mut [u8; CALLBACK_STACK_SIZE] =
             Box::leak(Box::new([0u8; CALLBACK_STACK_SIZE])).as_mut_ptr().cast();
         let stack_ptr = raw_stack_ptr.expose_provenance();
@@ -68,9 +69,9 @@ impl Supervisor {
         // SAFETY: We do not access machine memory past this point until the
         // supervisor is ready to allow it.
         unsafe {
-            if alloc.borrow_mut().prepare_ffi().is_err() {
+            if alloc.borrow_mut().start_ffi().is_err() {
                 // Don't mess up unwinding by maybe leaving the memory partly protected
-                alloc.borrow_mut().unprep_ffi();
+                alloc.borrow_mut().end_ffi();
                 panic!("Cannot protect memory for FFI call!");
             }
         }
@@ -82,27 +83,13 @@ impl Supervisor {
         // enforce an ordering for these events.
         sv.message_tx.send(TraceRequest::StartFfi(start_info)).unwrap();
         sv.confirm_rx.recv().unwrap();
-        *sv_guard = Some(sv);
         // We need to be stopped for the supervisor to be able to make certain
         // modifications to our memory - simply waiting on the recv() doesn't
         // count.
         signal::raise(signal::SIGSTOP).unwrap();
-        (sv_guard, Some(raw_stack_ptr))
-    }
 
-    /// Undoes FFI-related preparations, allowing Miri to continue as normal, then
-    /// gets the memory accesses and changes performed during the FFI call. Note
-    /// that this may include some spurious accesses done by `libffi` itself in
-    /// the process of executing the function call.
-    ///
-    /// SAFETY: The `sv_guard` and `raw_stack_ptr` passed must be the same ones
-    /// received by a prior call to `start_ffi`, and the allocator must be the
-    /// one passed to it also.
-    pub unsafe fn end_ffi(
-        alloc: &Rc<RefCell<IsolatedAlloc>>,
-        mut sv_guard: std::sync::MutexGuard<'static, Option<Supervisor>>,
-        raw_stack_ptr: Option<*mut [u8; CALLBACK_STACK_SIZE]>,
-    ) -> Option<MemEvents> {
+        let res = f();
+
         // We can't use IPC channels here to signal that FFI mode has ended,
         // since they might allocate memory which could get us stuck in a SIGTRAP
         // with no easy way out! While this could be worked around, it is much
@@ -113,42 +100,40 @@ impl Supervisor {
         signal::raise(signal::SIGUSR1).unwrap();
 
         // This is safe! It just sets memory to normal expected permissions.
-        alloc.borrow_mut().unprep_ffi();
+        alloc.borrow_mut().end_ffi();
 
-        // If this is `None`, then `raw_stack_ptr` is None and does not need to
-        // be deallocated (and there's no need to worry about the guard, since
-        // it contains nothing).
-        let sv = sv_guard.take()?;
         // SAFETY: Caller upholds that this pointer was allocated as a box with
         // this type.
         unsafe {
-            drop(Box::from_raw(raw_stack_ptr.unwrap()));
+            drop(Box::from_raw(raw_stack_ptr));
         }
         // On the off-chance something really weird happens, don't block forever.
-        let ret = sv
+        let events = sv
             .event_rx
             .try_recv_timeout(std::time::Duration::from_secs(5))
             .map_err(|e| {
                 match e {
                     ipc::TryRecvError::IpcError(_) => (),
                     ipc::TryRecvError::Empty =>
-                        eprintln!("Waiting for accesses from supervisor timed out!"),
+                        panic!("Waiting for accesses from supervisor timed out!"),
                 }
             })
             .ok();
-        // Do *not* leave the supervisor empty, or else we might get another fork...
-        *sv_guard = Some(sv);
-        ret
+
+        res.map(|v| (v, events))
     }
 }
 
 /// Initialises the supervisor process. If this function errors, then the
 /// supervisor process could not be created successfully; else, the caller
-/// is now the child process and can communicate via `start_ffi`/`end_ffi`,
-/// receiving back events through `get_events`.
+/// is now the child process and can communicate via `do_ffi`, receiving back
+/// events at the end.
 ///
 /// # Safety
-/// The invariants for `fork()` must be upheld by the caller.
+/// The invariants for `fork()` must be upheld by the caller, namely either:
+/// - Other threads do not exist, or;
+/// - If they do exist, either those threads or the resulting child process
+///   only ever act in [async-signal-safe](https://www.man7.org/linux/man-pages/man7/signal-safety.7.html) ways.
 pub unsafe fn init_sv() -> Result<(), SvInitError> {
     // FIXME: Much of this could be reimplemented via the mitosis crate if we upstream the
     // relevant missing bits.
@@ -191,8 +176,7 @@ pub unsafe fn init_sv() -> Result<(), SvInitError> {
                 // The child process is free to unwind, so we won't to avoid doubly freeing
                 // system resources.
                 let init = std::panic::catch_unwind(|| {
-                    let listener =
-                        ChildListener { message_rx, attached: false, override_retcode: None };
+                    let listener = ChildListener::new(message_rx, confirm_tx.clone());
                     // Trace as many things as possible, to be able to handle them as needed.
                     let options = ptrace::Options::PTRACE_O_TRACESYSGOOD
                         | ptrace::Options::PTRACE_O_TRACECLONE
@@ -218,7 +202,9 @@ pub unsafe fn init_sv() -> Result<(), SvInitError> {
                     // The "Ok" case means that we couldn't ptrace.
                     Ok(e) => return Err(e),
                     Err(p) => {
-                        eprintln!("Supervisor process panicked!\n{p:?}");
+                        eprintln!(
+                            "Supervisor process panicked!\n{p:?}\n\nTry running again without using the native-lib tracer."
+                        );
                         std::process::exit(1);
                     }
                 }
@@ -239,13 +225,11 @@ pub unsafe fn init_sv() -> Result<(), SvInitError> {
 }
 
 /// Instruct the supervisor process to return a particular code. Useful if for
-/// whatever reason this code fails to be intercepted normally. In the case of
-/// `abort_if_errors()` used in `bin/miri.rs`, the return code is erroneously
-/// given as a 0 if this is not used.
+/// whatever reason this code fails to be intercepted normally.
 pub fn register_retcode_sv(code: i32) {
     let mut sv_guard = SUPERVISOR.lock().unwrap();
-    if let Some(sv) = sv_guard.take() {
+    if let Some(sv) = sv_guard.as_mut() {
         sv.message_tx.send(TraceRequest::OverrideRetcode(code)).unwrap();
-        *sv_guard = Some(sv);
+        sv.confirm_rx.recv().unwrap();
     }
 }
diff --git a/src/tools/miri/src/shims/native_lib/trace/messages.rs b/src/tools/miri/src/shims/native_lib/trace/messages.rs
index 8a83dab5c09..1f9df556b57 100644
--- a/src/tools/miri/src/shims/native_lib/trace/messages.rs
+++ b/src/tools/miri/src/shims/native_lib/trace/messages.rs
@@ -1,25 +1,28 @@
 //! Houses the types that are directly sent across the IPC channels.
 //!
-//! The overall structure of a traced FFI call, from the child process's POV, is
-//! as follows:
+//! When forking to initialise the supervisor during `init_sv`, the child raises
+//! a `SIGSTOP`; if the parent successfully ptraces the child, it will allow it
+//! to resume. Else, the child will be killed by the parent.
+//!
+//! After initialisation is done, the overall structure of a traced FFI call from
+//! the child process's POV is as follows:
 //! ```
 //! message_tx.send(TraceRequest::StartFfi);
-//! confirm_rx.recv();
+//! confirm_rx.recv(); // receives a `Confirmation`
 //! raise(SIGSTOP);
 //! /* do ffi call */
 //! raise(SIGUSR1); // morally equivalent to some kind of "TraceRequest::EndFfi"
-//! let events = event_rx.recv();
+//! let events = event_rx.recv(); // receives a `MemEvents`
 //! ```
 //! `TraceRequest::OverrideRetcode` can be sent at any point in the above, including
-//! before or after all of them.
+//! before or after all of them. `confirm_rx.recv()` is to be called after, to ensure
+//! that the child does not exit before the supervisor has registered the return code.
 //!
 //! NB: sending these events out of order, skipping steps, etc. will result in
 //! unspecified behaviour from the supervisor process, so use the abstractions
-//! in `super::child` (namely `start_ffi()` and `end_ffi()`) to handle this. It is
+//! in `super::child` (namely `do_ffi()`) to handle this. It is
 //! trivially easy to cause a deadlock or crash by messing this up!
 
-use std::ops::Range;
-
 /// An IPC request sent by the child process to the parent.
 ///
 /// The sender for this channel should live on the child process.
@@ -34,6 +37,8 @@ pub enum TraceRequest {
     StartFfi(StartFfiInfo),
     /// Manually overrides the code that the supervisor will return upon exiting.
     /// Once set, it is permanent. This can be called again to change the value.
+    ///
+    /// After sending this, the child must wait to receive a `Confirmation`.
     OverrideRetcode(i32),
 }
 
@@ -41,7 +46,7 @@ pub enum TraceRequest {
 #[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
 pub struct StartFfiInfo {
     /// A vector of page addresses. These should have been automatically obtained
-    /// with `IsolatedAlloc::pages` and prepared with `IsolatedAlloc::prepare_ffi`.
+    /// with `IsolatedAlloc::pages` and prepared with `IsolatedAlloc::start_ffi`.
     pub page_ptrs: Vec<usize>,
     /// The address of an allocation that can serve as a temporary stack.
     /// This should be a leaked `Box<[u8; CALLBACK_STACK_SIZE]>` cast to an int.
@@ -54,27 +59,3 @@ pub struct StartFfiInfo {
 /// The sender for this channel should live on the parent process.
 #[derive(serde::Serialize, serde::Deserialize, Debug)]
 pub struct Confirmation;
-
-/// The final results of an FFI trace, containing every relevant event detected
-/// by the tracer. Sent by the supervisor after receiving a `SIGUSR1` signal.
-///
-/// The sender for this channel should live on the parent process.
-#[derive(serde::Serialize, serde::Deserialize, Debug)]
-pub struct MemEvents {
-    /// An ordered list of memory accesses that occurred. These should be assumed
-    /// to be overcautious; that is, if the size of an access is uncertain it is
-    /// pessimistically rounded up, and if the type (read/write/both) is uncertain
-    /// it is reported as whatever would be safest to assume; i.e. a read + maybe-write
-    /// becomes a read + write, etc.
-    pub acc_events: Vec<AccessEvent>,
-}
-
-/// A single memory access, conservatively overestimated
-/// in case of ambiguity.
-#[derive(serde::Serialize, serde::Deserialize, Debug)]
-pub enum AccessEvent {
-    /// A read may have occurred on no more than the specified address range.
-    Read(Range<usize>),
-    /// A write may have occurred on no more than the specified address range.
-    Write(Range<usize>),
-}
diff --git a/src/tools/miri/src/shims/native_lib/trace/mod.rs b/src/tools/miri/src/shims/native_lib/trace/mod.rs
index 174b06b3ac5..c8abacfb5e2 100644
--- a/src/tools/miri/src/shims/native_lib/trace/mod.rs
+++ b/src/tools/miri/src/shims/native_lib/trace/mod.rs
@@ -5,4 +5,6 @@ mod parent;
 pub use self::child::{Supervisor, init_sv, register_retcode_sv};
 
 /// The size of the temporary stack we use for callbacks that the server executes in the client.
+/// This should be big enough that `mempr_on` and `mempr_off` can safely be jumped into with the
+/// stack pointer pointing to a "stack" of this size without overflowing it.
 const CALLBACK_STACK_SIZE: usize = 1024;
diff --git a/src/tools/miri/src/shims/native_lib/trace/parent.rs b/src/tools/miri/src/shims/native_lib/trace/parent.rs
index dfb0b35da69..83f6c7a13fc 100644
--- a/src/tools/miri/src/shims/native_lib/trace/parent.rs
+++ b/src/tools/miri/src/shims/native_lib/trace/parent.rs
@@ -5,26 +5,17 @@ use nix::sys::{ptrace, signal, wait};
 use nix::unistd;
 
 use super::CALLBACK_STACK_SIZE;
-use super::messages::{AccessEvent, Confirmation, MemEvents, StartFfiInfo, TraceRequest};
+use super::messages::{Confirmation, StartFfiInfo, TraceRequest};
+use crate::shims::native_lib::{AccessEvent, AccessRange, MemEvents};
 
 /// The flags to use when calling `waitid()`.
-/// Since bitwise or on the nix version of these flags is implemented as a trait,
-/// this cannot be const directly so we do it this way.
 const WAIT_FLAGS: wait::WaitPidFlag =
-    wait::WaitPidFlag::from_bits_truncate(libc::WUNTRACED | libc::WEXITED);
-
-/// Arch-specific maximum size a single access might perform. x86 value is set
-/// assuming nothing bigger than AVX-512 is available.
-#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
-const ARCH_MAX_ACCESS_SIZE: usize = 64;
-/// The largest arm64 simd instruction operates on 16 bytes.
-#[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
-const ARCH_MAX_ACCESS_SIZE: usize = 16;
+    wait::WaitPidFlag::WUNTRACED.union(wait::WaitPidFlag::WEXITED);
 
 /// The default word size on a given platform, in bytes.
-#[cfg(any(target_arch = "x86", target_arch = "arm"))]
+#[cfg(target_arch = "x86")]
 const ARCH_WORD_SIZE: usize = 4;
-#[cfg(any(target_arch = "x86_64", target_arch = "aarch64"))]
+#[cfg(target_arch = "x86_64")]
 const ARCH_WORD_SIZE: usize = 8;
 
 /// The address of the page set to be edited, initialised to a sentinel null
@@ -53,39 +44,25 @@ trait ArchIndependentRegs {
 // It's fine / desirable behaviour for values to wrap here, we care about just
 // preserving the bit pattern.
 #[cfg(target_arch = "x86_64")]
-#[expect(clippy::as_conversions)]
 #[rustfmt::skip]
 impl ArchIndependentRegs for libc::user_regs_struct {
     #[inline]
-    fn ip(&self) -> usize { self.rip as _ }
+    fn ip(&self) -> usize { self.rip.try_into().unwrap() }
     #[inline]
-    fn set_ip(&mut self, ip: usize) { self.rip = ip as _ }
+    fn set_ip(&mut self, ip: usize) { self.rip = ip.try_into().unwrap() }
     #[inline]
-    fn set_sp(&mut self, sp: usize) { self.rsp = sp as _ }
+    fn set_sp(&mut self, sp: usize) { self.rsp = sp.try_into().unwrap() }
 }
 
 #[cfg(target_arch = "x86")]
-#[expect(clippy::as_conversions)]
 #[rustfmt::skip]
 impl ArchIndependentRegs for libc::user_regs_struct {
     #[inline]
-    fn ip(&self) -> usize { self.eip as _ }
+    fn ip(&self) -> usize { self.eip.cast_unsigned().try_into().unwrap() }
     #[inline]
-    fn set_ip(&mut self, ip: usize) { self.eip = ip as _ }
+    fn set_ip(&mut self, ip: usize) { self.eip = ip.cast_signed().try_into().unwrap() }
     #[inline]
-    fn set_sp(&mut self, sp: usize) { self.esp = sp as _ }
-}
-
-#[cfg(target_arch = "aarch64")]
-#[expect(clippy::as_conversions)]
-#[rustfmt::skip]
-impl ArchIndependentRegs for libc::user_regs_struct {
-    #[inline]
-    fn ip(&self) -> usize { self.pc as _ }
-    #[inline]
-    fn set_ip(&mut self, ip: usize) { self.pc = ip as _ }
-    #[inline]
-    fn set_sp(&mut self, sp: usize) { self.sp = sp as _ }
+    fn set_sp(&mut self, sp: usize) { self.esp = sp.cast_signed().try_into().unwrap() }
 }
 
 /// A unified event representing something happening on the child process. Wraps
@@ -109,11 +86,24 @@ pub enum ExecEvent {
 /// A listener for the FFI start info channel along with relevant state.
 pub struct ChildListener {
     /// The matching channel for the child's `Supervisor` struct.
-    pub message_rx: ipc::IpcReceiver<TraceRequest>,
+    message_rx: ipc::IpcReceiver<TraceRequest>,
+    /// ...
+    confirm_tx: ipc::IpcSender<Confirmation>,
     /// Whether an FFI call is currently ongoing.
-    pub attached: bool,
+    attached: bool,
     /// If `Some`, overrides the return code with the given value.
-    pub override_retcode: Option<i32>,
+    override_retcode: Option<i32>,
+    /// Last code obtained from a child exiting.
+    last_code: Option<i32>,
+}
+
+impl ChildListener {
+    pub fn new(
+        message_rx: ipc::IpcReceiver<TraceRequest>,
+        confirm_tx: ipc::IpcSender<Confirmation>,
+    ) -> Self {
+        Self { message_rx, confirm_tx, attached: false, override_retcode: None, last_code: None }
+    }
 }
 
 impl Iterator for ChildListener {
@@ -133,16 +123,10 @@ impl Iterator for ChildListener {
                 Ok(stat) =>
                     match stat {
                         // Child exited normally with a specific code set.
-                        wait::WaitStatus::Exited(_, code) => {
-                            let code = self.override_retcode.unwrap_or(code);
-                            return Some(ExecEvent::Died(Some(code)));
-                        }
+                        wait::WaitStatus::Exited(_, code) => self.last_code = Some(code),
                         // Child was killed by a signal, without giving a code.
-                        wait::WaitStatus::Signaled(_, _, _) =>
-                            return Some(ExecEvent::Died(self.override_retcode)),
-                        // Child entered a syscall. Since we're always technically
-                        // tracing, only pass this along if we're actively
-                        // monitoring the child.
+                        wait::WaitStatus::Signaled(_, _, _) => self.last_code = None,
+                        // Child entered or exited a syscall.
                         wait::WaitStatus::PtraceSyscall(pid) =>
                             if self.attached {
                                 return Some(ExecEvent::Syscall(pid));
@@ -179,10 +163,8 @@ impl Iterator for ChildListener {
                             },
                         _ => (),
                     },
-                // This case should only trigger if all children died and we
-                // somehow missed that, but it's best we not allow any room
-                // for deadlocks.
-                Err(_) => return Some(ExecEvent::Died(None)),
+                // This case should only trigger when all children died.
+                Err(_) => return Some(ExecEvent::Died(self.override_retcode.or(self.last_code))),
             }
 
             // Similarly, do a non-blocking poll of the IPC channel.
@@ -196,7 +178,10 @@ impl Iterator for ChildListener {
                             self.attached = true;
                             return Some(ExecEvent::Start(info));
                         },
-                    TraceRequest::OverrideRetcode(code) => self.override_retcode = Some(code),
+                    TraceRequest::OverrideRetcode(code) => {
+                        self.override_retcode = Some(code);
+                        self.confirm_tx.send(Confirmation).unwrap();
+                    }
                 }
             }
 
@@ -211,6 +196,12 @@ impl Iterator for ChildListener {
 #[derive(Debug)]
 pub struct ExecEnd(pub Option<i32>);
 
+/// Whether to call `ptrace::cont()` immediately. Used exclusively by `wait_for_signal`.
+enum InitialCont {
+    Yes,
+    No,
+}
+
 /// This is the main loop of the supervisor process. It runs in a separate
 /// process from the rest of Miri (but because we fork, addresses for anything
 /// created before the fork - like statics - are the same).
@@ -239,12 +230,12 @@ pub fn sv_loop(
     let mut curr_pid = init_pid;
 
     // There's an initial sigstop we need to deal with.
-    wait_for_signal(Some(curr_pid), signal::SIGSTOP, false)?;
+    wait_for_signal(Some(curr_pid), signal::SIGSTOP, InitialCont::No)?;
     ptrace::cont(curr_pid, None).unwrap();
 
     for evt in listener {
         match evt {
-            // start_ffi was called by the child, so prep memory.
+            // Child started ffi, so prep memory.
             ExecEvent::Start(ch_info) => {
                 // All the pages that the child process is "allowed to" access.
                 ch_pages = ch_info.page_ptrs;
@@ -252,17 +243,17 @@ pub fn sv_loop(
                 ch_stack = Some(ch_info.stack_ptr);
 
                 // We received the signal and are no longer in the main listener loop,
-                // so we can let the child move on to the end of start_ffi where it will
+                // so we can let the child move on to the end of the ffi prep where it will
                 // raise a SIGSTOP. We need it to be signal-stopped *and waited for* in
                 // order to do most ptrace operations!
                 confirm_tx.send(Confirmation).unwrap();
                 // We can't trust simply calling `Pid::this()` in the child process to give the right
                 // PID for us, so we get it this way.
-                curr_pid = wait_for_signal(None, signal::SIGSTOP, false).unwrap();
+                curr_pid = wait_for_signal(None, signal::SIGSTOP, InitialCont::No).unwrap();
 
                 ptrace::syscall(curr_pid, None).unwrap();
             }
-            // end_ffi was called by the child.
+            // Child wants to end tracing.
             ExecEvent::End => {
                 // Hand over the access info we traced.
                 event_tx.send(MemEvents { acc_events }).unwrap();
@@ -322,10 +313,6 @@ fn get_disasm() -> capstone::Capstone {
         {cs_pre.x86().mode(arch::x86::ArchMode::Mode64)}
         #[cfg(target_arch = "x86")]
         {cs_pre.x86().mode(arch::x86::ArchMode::Mode32)}
-        #[cfg(target_arch = "aarch64")]
-        {cs_pre.arm64().mode(arch::arm64::ArchMode::Arm)}
-        #[cfg(target_arch = "arm")]
-        {cs_pre.arm().mode(arch::arm::ArchMode::Arm)}
     }
     .detail(true)
     .build()
@@ -339,9 +326,9 @@ fn get_disasm() -> capstone::Capstone {
 fn wait_for_signal(
     pid: Option<unistd::Pid>,
     wait_signal: signal::Signal,
-    init_cont: bool,
+    init_cont: InitialCont,
 ) -> Result<unistd::Pid, ExecEnd> {
-    if init_cont {
+    if matches!(init_cont, InitialCont::Yes) {
         ptrace::cont(pid.unwrap(), None).unwrap();
     }
     // Repeatedly call `waitid` until we get the signal we want, or the process dies.
@@ -374,6 +361,84 @@ fn wait_for_signal(
     }
 }
 
+/// Add the memory events from `op` being executed while there is a memory access at `addr` to
+/// `acc_events`. Return whether this was a memory operand.
+fn capstone_find_events(
+    addr: usize,
+    op: &capstone::arch::ArchOperand,
+    acc_events: &mut Vec<AccessEvent>,
+) -> bool {
+    use capstone::prelude::*;
+    match op {
+        #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+        arch::ArchOperand::X86Operand(x86_operand) => {
+            match x86_operand.op_type {
+                // We only care about memory accesses
+                arch::x86::X86OperandType::Mem(_) => {
+                    let push = AccessRange { addr, size: x86_operand.size.into() };
+                    // It's called a "RegAccessType" but it also applies to memory
+                    let acc_ty = x86_operand.access.unwrap();
+                    // The same instruction might do both reads and writes, so potentially add both.
+                    // We do not know the order in which they happened, but writing and then reading
+                    // makes little sense so we put the read first. That is also the more
+                    // conservative choice.
+                    if acc_ty.is_readable() {
+                        acc_events.push(AccessEvent::Read(push.clone()));
+                    }
+                    if acc_ty.is_writable() {
+                        // FIXME: This could be made certain; either determine all cases where
+                        // only reads happen, or have an intermediate mempr_* function to first
+                        // map the page(s) as readonly and check if a segfault occurred.
+
+                        // Per https://docs.rs/iced-x86/latest/iced_x86/enum.OpAccess.html,
+                        // we know that the possible access types are Read, CondRead, Write,
+                        // CondWrite, ReadWrite, and ReadCondWrite. Since we got a segfault
+                        // we know some kind of access happened so Cond{Read, Write}s are
+                        // certain reads and writes; the only uncertainty is with an RW op
+                        // as it might be a ReadCondWrite with the write condition unmet.
+                        acc_events.push(AccessEvent::Write(push, !acc_ty.is_readable()));
+                    }
+
+                    return true;
+                }
+                _ => (),
+            }
+        }
+        // FIXME: arm64
+        _ => unimplemented!(),
+    }
+
+    false
+}
+
+/// Extract the events from the given instruction.
+fn capstone_disassemble(
+    instr: &[u8],
+    addr: usize,
+    cs: &capstone::Capstone,
+    acc_events: &mut Vec<AccessEvent>,
+) -> capstone::CsResult<()> {
+    // The arch_detail is what we care about, but it relies on these temporaries
+    // that we can't drop. 0x1000 is the default base address for Captsone, and
+    // we're expecting 1 instruction.
+    let insns = cs.disasm_count(instr, 0x1000, 1)?;
+    let ins_detail = cs.insn_detail(&insns[0])?;
+    let arch_detail = ins_detail.arch_detail();
+
+    let mut found_mem_op = false;
+
+    for op in arch_detail.operands() {
+        if capstone_find_events(addr, &op, acc_events) {
+            if found_mem_op {
+                panic!("more than one memory operand found; we don't know which one accessed what");
+            }
+            found_mem_op = true;
+        }
+    }
+
+    Ok(())
+}
+
 /// Grabs the access that caused a segfault and logs it down if it's to our memory,
 /// or kills the child and returns the appropriate error otherwise.
 fn handle_segfault(
@@ -384,116 +449,10 @@ fn handle_segfault(
     cs: &capstone::Capstone,
     acc_events: &mut Vec<AccessEvent>,
 ) -> Result<(), ExecEnd> {
-    /// This is just here to not pollute the main namespace with `capstone::prelude::*`.
-    #[inline]
-    fn capstone_disassemble(
-        instr: &[u8],
-        addr: usize,
-        cs: &capstone::Capstone,
-        acc_events: &mut Vec<AccessEvent>,
-    ) -> capstone::CsResult<()> {
-        use capstone::prelude::*;
-
-        // The arch_detail is what we care about, but it relies on these temporaries
-        // that we can't drop. 0x1000 is the default base address for Captsone, and
-        // we're expecting 1 instruction.
-        let insns = cs.disasm_count(instr, 0x1000, 1)?;
-        let ins_detail = cs.insn_detail(&insns[0])?;
-        let arch_detail = ins_detail.arch_detail();
-
-        for op in arch_detail.operands() {
-            match op {
-                #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
-                arch::ArchOperand::X86Operand(x86_operand) => {
-                    match x86_operand.op_type {
-                        // We only care about memory accesses
-                        arch::x86::X86OperandType::Mem(_) => {
-                            let push = addr..addr.strict_add(usize::from(x86_operand.size));
-                            // It's called a "RegAccessType" but it also applies to memory
-                            let acc_ty = x86_operand.access.unwrap();
-                            if acc_ty.is_readable() {
-                                acc_events.push(AccessEvent::Read(push.clone()));
-                            }
-                            if acc_ty.is_writable() {
-                                acc_events.push(AccessEvent::Write(push));
-                            }
-                        }
-                        _ => (),
-                    }
-                }
-                #[cfg(target_arch = "aarch64")]
-                arch::ArchOperand::Arm64Operand(arm64_operand) => {
-                    // Annoyingly, we don't always get the size here, so just be pessimistic for now.
-                    match arm64_operand.op_type {
-                        arch::arm64::Arm64OperandType::Mem(_) => {
-                            // B = 1 byte, H = 2 bytes, S = 4 bytes, D = 8 bytes, Q = 16 bytes.
-                            let size = match arm64_operand.vas {
-                                // Not an fp/simd instruction.
-                                arch::arm64::Arm64Vas::ARM64_VAS_INVALID => ARCH_WORD_SIZE,
-                                // 1 byte.
-                                arch::arm64::Arm64Vas::ARM64_VAS_1B => 1,
-                                // 2 bytes.
-                                arch::arm64::Arm64Vas::ARM64_VAS_1H => 2,
-                                // 4 bytes.
-                                arch::arm64::Arm64Vas::ARM64_VAS_4B
-                                | arch::arm64::Arm64Vas::ARM64_VAS_2H
-                                | arch::arm64::Arm64Vas::ARM64_VAS_1S => 4,
-                                // 8 bytes.
-                                arch::arm64::Arm64Vas::ARM64_VAS_8B
-                                | arch::arm64::Arm64Vas::ARM64_VAS_4H
-                                | arch::arm64::Arm64Vas::ARM64_VAS_2S
-                                | arch::arm64::Arm64Vas::ARM64_VAS_1D => 8,
-                                // 16 bytes.
-                                arch::arm64::Arm64Vas::ARM64_VAS_16B
-                                | arch::arm64::Arm64Vas::ARM64_VAS_8H
-                                | arch::arm64::Arm64Vas::ARM64_VAS_4S
-                                | arch::arm64::Arm64Vas::ARM64_VAS_2D
-                                | arch::arm64::Arm64Vas::ARM64_VAS_1Q => 16,
-                            };
-                            let push = addr..addr.strict_add(size);
-                            // FIXME: This now has access type info in the latest
-                            // git version of capstone because this pissed me off
-                            // and I added it. Change this when it updates.
-                            acc_events.push(AccessEvent::Read(push.clone()));
-                            acc_events.push(AccessEvent::Write(push));
-                        }
-                        _ => (),
-                    }
-                }
-                #[cfg(target_arch = "arm")]
-                arch::ArchOperand::ArmOperand(arm_operand) =>
-                    match arm_operand.op_type {
-                        arch::arm::ArmOperandType::Mem(_) => {
-                            // We don't get info on the size of the access, but
-                            // we're at least told if it's a vector instruction.
-                            let size = if arm_operand.vector_index.is_some() {
-                                ARCH_MAX_ACCESS_SIZE
-                            } else {
-                                ARCH_WORD_SIZE
-                            };
-                            let push = addr..addr.strict_add(size);
-                            let acc_ty = arm_operand.access.unwrap();
-                            if acc_ty.is_readable() {
-                                acc_events.push(AccessEvent::Read(push.clone()));
-                            }
-                            if acc_ty.is_writable() {
-                                acc_events.push(AccessEvent::Write(push));
-                            }
-                        }
-                        _ => (),
-                    },
-                _ => unimplemented!(),
-            }
-        }
-
-        Ok(())
-    }
-
     // Get information on what caused the segfault. This contains the address
     // that triggered it.
     let siginfo = ptrace::getsiginfo(pid).unwrap();
-    // All x86, ARM, etc. instructions only have at most one memory operand
-    // (thankfully!)
+    // All x86 instructions only have at most one memory operand (thankfully!)
     // SAFETY: si_addr is safe to call.
     let addr = unsafe { siginfo.si_addr().addr() };
     let page_addr = addr.strict_sub(addr.strict_rem(page_size));
@@ -515,7 +474,7 @@ fn handle_segfault(
     //   global atomic variables. This is what we use the temporary callback stack for.
     // - Step 1 instruction
     // - Parse executed code to estimate size & type of access
-    // - Reprotect the memory by executing `mempr_on` in the child.
+    // - Reprotect the memory by executing `mempr_on` in the child, using the callback stack again.
     // - Continue
 
     // Ensure the stack is properly zeroed out!
@@ -540,7 +499,7 @@ fn handle_segfault(
     ptrace::write(
         pid,
         (&raw const PAGE_ADDR).cast_mut().cast(),
-        libc::c_long::try_from(page_addr).unwrap(),
+        libc::c_long::try_from(page_addr.cast_signed()).unwrap(),
     )
     .unwrap();
 
@@ -552,7 +511,7 @@ fn handle_segfault(
     ptrace::setregs(pid, new_regs).unwrap();
 
     // Our mempr_* functions end with a raise(SIGSTOP).
-    wait_for_signal(Some(pid), signal::SIGSTOP, true)?;
+    wait_for_signal(Some(pid), signal::SIGSTOP, InitialCont::Yes)?;
 
     // Step 1 instruction.
     ptrace::setregs(pid, regs_bak).unwrap();
@@ -573,6 +532,12 @@ fn handle_segfault(
     let regs_bak = ptrace::getregs(pid).unwrap();
     new_regs = regs_bak;
     let ip_poststep = regs_bak.ip();
+
+    // Ensure that we've actually gone forwards.
+    assert!(ip_poststep > ip_prestep);
+    // But not by too much. 64 bytes should be "big enough" on ~any architecture.
+    assert!(ip_prestep.strict_add(64) > ip_poststep);
+
     // We need to do reads/writes in word-sized chunks.
     let diff = (ip_poststep.strict_sub(ip_prestep)).div_ceil(ARCH_WORD_SIZE);
     let instr = (ip_prestep..ip_prestep.strict_add(diff)).fold(vec![], |mut ret, ip| {
@@ -587,20 +552,14 @@ fn handle_segfault(
     });
 
     // Now figure out the size + type of access and log it down.
-    // This will mark down e.g. the same area being read multiple times,
-    // since it's more efficient to compress the accesses at the end.
-    if capstone_disassemble(&instr, addr, cs, acc_events).is_err() {
-        // Read goes first because we need to be pessimistic.
-        acc_events.push(AccessEvent::Read(addr..addr.strict_add(ARCH_MAX_ACCESS_SIZE)));
-        acc_events.push(AccessEvent::Write(addr..addr.strict_add(ARCH_MAX_ACCESS_SIZE)));
-    }
+    capstone_disassemble(&instr, addr, cs, acc_events).expect("Failed to disassemble instruction");
 
     // Reprotect everything and continue.
     #[expect(clippy::as_conversions)]
     new_regs.set_ip(mempr_on as usize);
     new_regs.set_sp(stack_ptr);
     ptrace::setregs(pid, new_regs).unwrap();
-    wait_for_signal(Some(pid), signal::SIGSTOP, true)?;
+    wait_for_signal(Some(pid), signal::SIGSTOP, InitialCont::Yes)?;
 
     ptrace::setregs(pid, regs_bak).unwrap();
     ptrace::syscall(pid, None).unwrap();
diff --git a/src/tools/miri/src/shims/native_lib/trace/stub.rs b/src/tools/miri/src/shims/native_lib/trace/stub.rs
new file mode 100644
index 00000000000..22787a6f6fa
--- /dev/null
+++ b/src/tools/miri/src/shims/native_lib/trace/stub.rs
@@ -0,0 +1,34 @@
+use rustc_const_eval::interpret::InterpResult;
+
+static SUPERVISOR: std::sync::Mutex<()> = std::sync::Mutex::new(());
+
+pub struct Supervisor;
+
+#[derive(Debug)]
+pub struct SvInitError;
+
+impl Supervisor {
+    #[inline(always)]
+    pub fn is_enabled() -> bool {
+        false
+    }
+
+    pub fn do_ffi<'tcx, T>(
+        _: T,
+        f: impl FnOnce() -> InterpResult<'tcx, crate::ImmTy<'tcx>>,
+    ) -> InterpResult<'tcx, (crate::ImmTy<'tcx>, Option<super::MemEvents>)> {
+        // We acquire the lock to ensure that no two FFI calls run concurrently.
+        let _g = SUPERVISOR.lock().unwrap();
+        f().map(|v| (v, None))
+    }
+}
+
+#[inline(always)]
+#[allow(dead_code, clippy::missing_safety_doc)]
+pub unsafe fn init_sv() -> Result<!, SvInitError> {
+    Err(SvInitError)
+}
+
+#[inline(always)]
+#[allow(dead_code)]
+pub fn register_retcode_sv<T>(_: T) {}
diff --git a/src/tools/miri/src/shims/panic.rs b/src/tools/miri/src/shims/panic.rs
index a6bce830149..85564e47685 100644
--- a/src/tools/miri/src/shims/panic.rs
+++ b/src/tools/miri/src/shims/panic.rs
@@ -1,162 +1,12 @@
-//! Panic runtime for Miri.
-//!
-//! The core pieces of the runtime are:
-//! - An implementation of `__rust_maybe_catch_panic` that pushes the invoked stack frame with
-//!   some extra metadata derived from the panic-catching arguments of `__rust_maybe_catch_panic`.
-//! - A hack in `libpanic_unwind` that calls the `miri_start_unwind` intrinsic instead of the
-//!   target-native panic runtime. (This lives in the rustc repo.)
-//! - An implementation of `miri_start_unwind` that stores its argument (the panic payload), and then
-//!   immediately returns, but on the *unwind* edge (not the normal return edge), thus initiating unwinding.
-//! - A hook executed each time a frame is popped, such that if the frame pushed by `__rust_maybe_catch_panic`
-//!   gets popped *during unwinding*, we take the panic payload and store it according to the extra
-//!   metadata we remembered when pushing said frame.
+//! Helper functions for causing panics.
 
 use rustc_abi::ExternAbi;
 use rustc_middle::{mir, ty};
-use rustc_target::spec::PanicStrategy;
 
-use self::helpers::check_intrinsic_arg_count;
 use crate::*;
 
-/// Holds all of the relevant data for when unwinding hits a `try` frame.
-#[derive(Debug)]
-pub struct CatchUnwindData<'tcx> {
-    /// The `catch_fn` callback to call in case of a panic.
-    catch_fn: Pointer,
-    /// The `data` argument for that callback.
-    data: ImmTy<'tcx>,
-    /// The return place from the original call to `try`.
-    dest: MPlaceTy<'tcx>,
-    /// The return block from the original call to `try`.
-    ret: Option<mir::BasicBlock>,
-}
-
-impl VisitProvenance for CatchUnwindData<'_> {
-    fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
-        let CatchUnwindData { catch_fn, data, dest, ret: _ } = self;
-        catch_fn.visit_provenance(visit);
-        data.visit_provenance(visit);
-        dest.visit_provenance(visit);
-    }
-}
-
 impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
 pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
-    /// Handles the special `miri_start_unwind` intrinsic, which is called
-    /// by libpanic_unwind to delegate the actual unwinding process to Miri.
-    fn handle_miri_start_unwind(&mut self, payload: &OpTy<'tcx>) -> InterpResult<'tcx> {
-        let this = self.eval_context_mut();
-
-        trace!("miri_start_unwind: {:?}", this.frame().instance());
-
-        let payload = this.read_immediate(payload)?;
-        let thread = this.active_thread_mut();
-        thread.panic_payloads.push(payload);
-
-        interp_ok(())
-    }
-
-    /// Handles the `catch_unwind` intrinsic.
-    fn handle_catch_unwind(
-        &mut self,
-        args: &[OpTy<'tcx>],
-        dest: &MPlaceTy<'tcx>,
-        ret: Option<mir::BasicBlock>,
-    ) -> InterpResult<'tcx> {
-        let this = self.eval_context_mut();
-
-        // Signature:
-        //   fn catch_unwind(try_fn: fn(*mut u8), data: *mut u8, catch_fn: fn(*mut u8, *mut u8)) -> i32
-        // Calls `try_fn` with `data` as argument. If that executes normally, returns 0.
-        // If that unwinds, calls `catch_fn` with the first argument being `data` and
-        // then second argument being a target-dependent `payload` (i.e. it is up to us to define
-        // what that is), and returns 1.
-        // The `payload` is passed (by libstd) to `__rust_panic_cleanup`, which is then expected to
-        // return a `Box<dyn Any + Send + 'static>`.
-        // In Miri, `miri_start_unwind` is passed exactly that type, so we make the `payload` simply
-        // a pointer to `Box<dyn Any + Send + 'static>`.
-
-        // Get all the arguments.
-        let [try_fn, data, catch_fn] = check_intrinsic_arg_count(args)?;
-        let try_fn = this.read_pointer(try_fn)?;
-        let data = this.read_immediate(data)?;
-        let catch_fn = this.read_pointer(catch_fn)?;
-
-        // Now we make a function call, and pass `data` as first and only argument.
-        let f_instance = this.get_ptr_fn(try_fn)?.as_instance()?;
-        trace!("try_fn: {:?}", f_instance);
-        #[allow(clippy::cloned_ref_to_slice_refs)] // the code is clearer as-is
-        this.call_function(
-            f_instance,
-            ExternAbi::Rust,
-            &[data.clone()],
-            None,
-            // Directly return to caller.
-            StackPopCleanup::Goto { ret, unwind: mir::UnwindAction::Continue },
-        )?;
-
-        // We ourselves will return `0`, eventually (will be overwritten if we catch a panic).
-        this.write_null(dest)?;
-
-        // In unwind mode, we tag this frame with the extra data needed to catch unwinding.
-        // This lets `handle_stack_pop` (below) know that we should stop unwinding
-        // when we pop this frame.
-        if this.tcx.sess.panic_strategy() == PanicStrategy::Unwind {
-            this.frame_mut().extra.catch_unwind =
-                Some(CatchUnwindData { catch_fn, data, dest: dest.clone(), ret });
-        }
-
-        interp_ok(())
-    }
-
-    fn handle_stack_pop_unwind(
-        &mut self,
-        mut extra: FrameExtra<'tcx>,
-        unwinding: bool,
-    ) -> InterpResult<'tcx, ReturnAction> {
-        let this = self.eval_context_mut();
-        trace!("handle_stack_pop_unwind(extra = {:?}, unwinding = {})", extra, unwinding);
-
-        // We only care about `catch_panic` if we're unwinding - if we're doing a normal
-        // return, then we don't need to do anything special.
-        if let (true, Some(catch_unwind)) = (unwinding, extra.catch_unwind.take()) {
-            // We've just popped a frame that was pushed by `catch_unwind`,
-            // and we are unwinding, so we should catch that.
-            trace!(
-                "unwinding: found catch_panic frame during unwinding: {:?}",
-                this.frame().instance()
-            );
-
-            // We set the return value of `catch_unwind` to 1, since there was a panic.
-            this.write_scalar(Scalar::from_i32(1), &catch_unwind.dest)?;
-
-            // The Thread's `panic_payload` holds what was passed to `miri_start_unwind`.
-            // This is exactly the second argument we need to pass to `catch_fn`.
-            let payload = this.active_thread_mut().panic_payloads.pop().unwrap();
-
-            // Push the `catch_fn` stackframe.
-            let f_instance = this.get_ptr_fn(catch_unwind.catch_fn)?.as_instance()?;
-            trace!("catch_fn: {:?}", f_instance);
-            this.call_function(
-                f_instance,
-                ExternAbi::Rust,
-                &[catch_unwind.data, payload],
-                None,
-                // Directly return to caller of `catch_unwind`.
-                StackPopCleanup::Goto {
-                    ret: catch_unwind.ret,
-                    // `catch_fn` must not unwind.
-                    unwind: mir::UnwindAction::Unreachable,
-                },
-            )?;
-
-            // We pushed a new stack frame, the engine should not do any jumping now!
-            interp_ok(ReturnAction::NoJump)
-        } else {
-            interp_ok(ReturnAction::Normal)
-        }
-    }
-
     /// Start a panic in the interpreter with the given message as payload.
     fn start_panic(&mut self, msg: &str, unwind: mir::UnwindAction) -> InterpResult<'tcx> {
         let this = self.eval_context_mut();
@@ -172,7 +22,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             ExternAbi::Rust,
             &[this.mplace_to_ref(&msg)?],
             None,
-            StackPopCleanup::Goto { ret: None, unwind },
+            ReturnContinuation::Goto { ret: None, unwind },
         )
     }
 
@@ -191,7 +41,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             ExternAbi::Rust,
             &[this.mplace_to_ref(&msg)?],
             None,
-            StackPopCleanup::Goto { ret: None, unwind: mir::UnwindAction::Unreachable },
+            ReturnContinuation::Goto { ret: None, unwind: mir::UnwindAction::Unreachable },
         )
     }
 
@@ -220,7 +70,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     ExternAbi::Rust,
                     &[index, len],
                     None,
-                    StackPopCleanup::Goto { ret: None, unwind },
+                    ReturnContinuation::Goto { ret: None, unwind },
                 )?;
             }
             MisalignedPointerDereference { required, found } => {
@@ -241,7 +91,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     ExternAbi::Rust,
                     &[required, found],
                     None,
-                    StackPopCleanup::Goto { ret: None, unwind },
+                    ReturnContinuation::Goto { ret: None, unwind },
                 )?;
             }
 
@@ -254,7 +104,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     ExternAbi::Rust,
                     &[],
                     None,
-                    StackPopCleanup::Goto { ret: None, unwind },
+                    ReturnContinuation::Goto { ret: None, unwind },
                 )?;
             }
         }
diff --git a/src/tools/miri/src/shims/tls.rs b/src/tools/miri/src/shims/tls.rs
index 46a417689a2..1200029692d 100644
--- a/src/tools/miri/src/shims/tls.rs
+++ b/src/tools/miri/src/shims/tls.rs
@@ -302,7 +302,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
         // Windows has a special magic linker section that is run on certain events.
         // We don't support most of that, but just enough to make thread-local dtors in `std` work.
-        interp_ok(this.lookup_link_section(".CRT$XLB")?)
+        interp_ok(this.lookup_link_section(|section| section == ".CRT$XLB")?)
     }
 
     fn schedule_windows_tls_dtor(&mut self, dtor: ImmTy<'tcx>) -> InterpResult<'tcx> {
@@ -325,7 +325,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             ExternAbi::System { unwind: false },
             &[null_ptr.clone(), ImmTy::from_scalar(reason, this.machine.layouts.u32), null_ptr],
             None,
-            StackPopCleanup::Root { cleanup: true },
+            ReturnContinuation::Stop { cleanup: true },
         )?;
         interp_ok(())
     }
@@ -346,7 +346,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 ExternAbi::C { unwind: false },
                 &[ImmTy::from_scalar(data, this.machine.layouts.mut_raw_ptr)],
                 None,
-                StackPopCleanup::Root { cleanup: true },
+                ReturnContinuation::Stop { cleanup: true },
             )?;
 
             return interp_ok(Poll::Pending);
@@ -383,7 +383,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 ExternAbi::C { unwind: false },
                 &[ImmTy::from_scalar(ptr, this.machine.layouts.mut_raw_ptr)],
                 None,
-                StackPopCleanup::Root { cleanup: true },
+                ReturnContinuation::Stop { cleanup: true },
             )?;
 
             return interp_ok(Poll::Pending);
diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs
index 0f7d453b296..0f2878ad26c 100644
--- a/src/tools/miri/src/shims/unix/fs.rs
+++ b/src/tools/miri/src/shims/unix/fs.rs
@@ -132,12 +132,12 @@ trait EvalContextExtPrivate<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let buf = this.deref_pointer_as(buf_op, this.libc_ty_layout("stat"))?;
         this.write_int_fields_named(
             &[
-                ("st_dev", 0),
+                ("st_dev", metadata.dev.into()),
                 ("st_mode", mode.try_into().unwrap()),
                 ("st_nlink", 0),
                 ("st_ino", 0),
-                ("st_uid", 0),
-                ("st_gid", 0),
+                ("st_uid", metadata.uid.into()),
+                ("st_gid", metadata.gid.into()),
                 ("st_rdev", 0),
                 ("st_atime", access_sec.into()),
                 ("st_mtime", modified_sec.into()),
@@ -1544,6 +1544,9 @@ struct FileMetadata {
     created: Option<(u64, u32)>,
     accessed: Option<(u64, u32)>,
     modified: Option<(u64, u32)>,
+    dev: u64,
+    uid: u32,
+    gid: u32,
 }
 
 impl FileMetadata {
@@ -1601,6 +1604,21 @@ impl FileMetadata {
         let modified = extract_sec_and_nsec(metadata.modified())?;
 
         // FIXME: Provide more fields using platform specific methods.
-        interp_ok(Ok(FileMetadata { mode, size, created, accessed, modified }))
+
+        cfg_select! {
+            unix => {
+                use std::os::unix::fs::MetadataExt;
+                let dev = metadata.dev();
+                let uid = metadata.uid();
+                let gid = metadata.gid();
+            }
+            _ => {
+                let dev = 0;
+                let uid = 0;
+                let gid = 0;
+            }
+        }
+
+        interp_ok(Ok(FileMetadata { mode, size, created, accessed, modified, dev, uid, gid }))
     }
 }
diff --git a/src/tools/miri/src/shims/unwind.rs b/src/tools/miri/src/shims/unwind.rs
new file mode 100644
index 00000000000..ba0c50b54b4
--- /dev/null
+++ b/src/tools/miri/src/shims/unwind.rs
@@ -0,0 +1,160 @@
+//! Unwinding runtime for Miri.
+//!
+//! The core pieces of the runtime are:
+//! - An implementation of `catch_unwind` that pushes the invoked stack frame with
+//!   some extra metadata derived from the panic-catching arguments of `catch_unwind`.
+//! - A hack in `libpanic_unwind` that calls the `miri_start_unwind` intrinsic instead of the
+//!   target-native panic runtime. (This lives in the rustc repo.)
+//! - An implementation of `miri_start_unwind` that stores its argument (the panic payload), and
+//!   then immediately returns, but on the *unwind* edge (not the normal return edge), thus
+//!   initiating unwinding.
+//! - A hook executed each time a frame is popped, such that if the frame pushed by `catch_unwind`
+//!   gets popped *during unwinding*, we take the panic payload and store it according to the extra
+//!   metadata we remembered when pushing said frame.
+
+use rustc_abi::ExternAbi;
+use rustc_middle::mir;
+use rustc_target::spec::PanicStrategy;
+
+use self::helpers::check_intrinsic_arg_count;
+use crate::*;
+
+/// Holds all of the relevant data for when unwinding hits a `try` frame.
+#[derive(Debug)]
+pub struct CatchUnwindData<'tcx> {
+    /// The `catch_fn` callback to call in case of a panic.
+    catch_fn: Pointer,
+    /// The `data` argument for that callback.
+    data: ImmTy<'tcx>,
+    /// The return place from the original call to `try`.
+    dest: MPlaceTy<'tcx>,
+    /// The return block from the original call to `try`.
+    ret: Option<mir::BasicBlock>,
+}
+
+impl VisitProvenance for CatchUnwindData<'_> {
+    fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
+        let CatchUnwindData { catch_fn, data, dest, ret: _ } = self;
+        catch_fn.visit_provenance(visit);
+        data.visit_provenance(visit);
+        dest.visit_provenance(visit);
+    }
+}
+
+impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
+pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
+    /// Handles the special `miri_start_unwind` intrinsic, which is called
+    /// by libpanic_unwind to delegate the actual unwinding process to Miri.
+    fn handle_miri_start_unwind(&mut self, payload: &OpTy<'tcx>) -> InterpResult<'tcx> {
+        let this = self.eval_context_mut();
+
+        trace!("miri_start_unwind: {:?}", this.frame().instance());
+
+        let payload = this.read_immediate(payload)?;
+        let thread = this.active_thread_mut();
+        thread.unwind_payloads.push(payload);
+
+        interp_ok(())
+    }
+
+    /// Handles the `catch_unwind` intrinsic.
+    fn handle_catch_unwind(
+        &mut self,
+        args: &[OpTy<'tcx>],
+        dest: &MPlaceTy<'tcx>,
+        ret: Option<mir::BasicBlock>,
+    ) -> InterpResult<'tcx> {
+        let this = self.eval_context_mut();
+
+        // Signature:
+        //   fn catch_unwind(try_fn: fn(*mut u8), data: *mut u8, catch_fn: fn(*mut u8, *mut u8)) -> i32
+        // Calls `try_fn` with `data` as argument. If that executes normally, returns 0.
+        // If that unwinds, calls `catch_fn` with the first argument being `data` and
+        // then second argument being a target-dependent `payload` (i.e. it is up to us to define
+        // what that is), and returns 1.
+        // The `payload` is passed (by libstd) to `__rust_panic_cleanup`, which is then expected to
+        // return a `Box<dyn Any + Send + 'static>`.
+        // In Miri, `miri_start_unwind` is passed exactly that type, so we make the `payload` simply
+        // a pointer to `Box<dyn Any + Send + 'static>`.
+
+        // Get all the arguments.
+        let [try_fn, data, catch_fn] = check_intrinsic_arg_count(args)?;
+        let try_fn = this.read_pointer(try_fn)?;
+        let data = this.read_immediate(data)?;
+        let catch_fn = this.read_pointer(catch_fn)?;
+
+        // Now we make a function call, and pass `data` as first and only argument.
+        let f_instance = this.get_ptr_fn(try_fn)?.as_instance()?;
+        trace!("try_fn: {:?}", f_instance);
+        #[allow(clippy::cloned_ref_to_slice_refs)] // the code is clearer as-is
+        this.call_function(
+            f_instance,
+            ExternAbi::Rust,
+            &[data.clone()],
+            None,
+            // Directly return to caller.
+            ReturnContinuation::Goto { ret, unwind: mir::UnwindAction::Continue },
+        )?;
+
+        // We ourselves will return `0`, eventually (will be overwritten if we catch a panic).
+        this.write_null(dest)?;
+
+        // In unwind mode, we tag this frame with the extra data needed to catch unwinding.
+        // This lets `handle_stack_pop` (below) know that we should stop unwinding
+        // when we pop this frame.
+        if this.tcx.sess.panic_strategy() == PanicStrategy::Unwind {
+            this.frame_mut().extra.catch_unwind =
+                Some(CatchUnwindData { catch_fn, data, dest: dest.clone(), ret });
+        }
+
+        interp_ok(())
+    }
+
+    fn handle_stack_pop_unwind(
+        &mut self,
+        mut extra: FrameExtra<'tcx>,
+        unwinding: bool,
+    ) -> InterpResult<'tcx, ReturnAction> {
+        let this = self.eval_context_mut();
+        trace!("handle_stack_pop_unwind(extra = {:?}, unwinding = {})", extra, unwinding);
+
+        // We only care about `catch_panic` if we're unwinding - if we're doing a normal
+        // return, then we don't need to do anything special.
+        if let (true, Some(catch_unwind)) = (unwinding, extra.catch_unwind.take()) {
+            // We've just popped a frame that was pushed by `catch_unwind`,
+            // and we are unwinding, so we should catch that.
+            trace!(
+                "unwinding: found catch_panic frame during unwinding: {:?}",
+                this.frame().instance()
+            );
+
+            // We set the return value of `catch_unwind` to 1, since there was a panic.
+            this.write_scalar(Scalar::from_i32(1), &catch_unwind.dest)?;
+
+            // The Thread's `panic_payload` holds what was passed to `miri_start_unwind`.
+            // This is exactly the second argument we need to pass to `catch_fn`.
+            let payload = this.active_thread_mut().unwind_payloads.pop().unwrap();
+
+            // Push the `catch_fn` stackframe.
+            let f_instance = this.get_ptr_fn(catch_unwind.catch_fn)?.as_instance()?;
+            trace!("catch_fn: {:?}", f_instance);
+            this.call_function(
+                f_instance,
+                ExternAbi::Rust,
+                &[catch_unwind.data, payload],
+                None,
+                // Directly return to caller of `catch_unwind`.
+                ReturnContinuation::Goto {
+                    ret: catch_unwind.ret,
+                    // `catch_fn` must not unwind.
+                    unwind: mir::UnwindAction::Unreachable,
+                },
+            )?;
+
+            // We pushed a new stack frame, the engine should not do any jumping now!
+            interp_ok(ReturnAction::NoJump)
+        } else {
+            interp_ok(ReturnAction::Normal)
+        }
+    }
+}
diff --git a/src/tools/miri/test_dependencies/Cargo.toml b/src/tools/miri/test_dependencies/Cargo.toml
index fa833b51fa3..35555723f5d 100644
--- a/src/tools/miri/test_dependencies/Cargo.toml
+++ b/src/tools/miri/test_dependencies/Cargo.toml
@@ -34,4 +34,5 @@ windows-sys = { version = "0.59", features = [
     "Wdk_Storage_FileSystem",
 ] }
 
+# Make sure we are not part of the rustc workspace.
 [workspace]
diff --git a/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_read_neg_offset.rs b/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_read_neg_offset.rs
new file mode 100644
index 00000000000..107a3db91d8
--- /dev/null
+++ b/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_read_neg_offset.rs
@@ -0,0 +1,6 @@
+fn main() {
+    let v: Vec<u16> = vec![1, 2];
+    // This read is also misaligned. We make sure that the OOB message has priority.
+    let x = unsafe { *v.as_ptr().wrapping_byte_sub(5) }; //~ ERROR: before the beginning of the allocation
+    panic!("this should never print: {}", x);
+}
diff --git a/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_read_neg_offset.stderr b/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_read_neg_offset.stderr
new file mode 100644
index 00000000000..5c37caa1ebf
--- /dev/null
+++ b/src/tools/miri/tests/fail/dangling_pointers/out_of_bounds_read_neg_offset.stderr
@@ -0,0 +1,21 @@
+error: Undefined Behavior: memory access failed: attempting to access 2 bytes, but got ALLOC-0x5 which points to before the beginning of the allocation
+  --> tests/fail/dangling_pointers/out_of_bounds_read_neg_offset.rs:LL:CC
+   |
+LL |     let x = unsafe { *v.as_ptr().wrapping_byte_sub(5) };
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
+   |
+   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
+   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+help: ALLOC was allocated here:
+  --> tests/fail/dangling_pointers/out_of_bounds_read_neg_offset.rs:LL:CC
+   |
+LL |     let v: Vec<u16> = vec![1, 2];
+   |                       ^^^^^^^^^^
+   = note: BACKTRACE (of the first span):
+   = note: inside `main` at tests/fail/dangling_pointers/out_of_bounds_read_neg_offset.rs:LL:CC
+   = note: this error originates in the macro `vec` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_out_of_bounds_neg2.rs b/src/tools/miri/tests/fail/intrinsics/ptr_offset_out_of_bounds_neg2.rs
new file mode 100644
index 00000000000..0085e2af367
--- /dev/null
+++ b/src/tools/miri/tests/fail/intrinsics/ptr_offset_out_of_bounds_neg2.rs
@@ -0,0 +1,6 @@
+fn main() {
+    let v = [0i8; 4];
+    let x = &v as *const i8;
+    let x = unsafe { x.wrapping_offset(-1).offset(-1) }; //~ERROR: before the beginning of the allocation
+    panic!("this should never print: {:?}", x);
+}
diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_offset_out_of_bounds_neg2.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_offset_out_of_bounds_neg2.stderr
new file mode 100644
index 00000000000..495aaf8d40e
--- /dev/null
+++ b/src/tools/miri/tests/fail/intrinsics/ptr_offset_out_of_bounds_neg2.stderr
@@ -0,0 +1,20 @@
+error: Undefined Behavior: in-bounds pointer arithmetic failed: attempting to offset pointer by -1 bytes, but got ALLOC-0x1 which points to before the beginning of the allocation
+  --> tests/fail/intrinsics/ptr_offset_out_of_bounds_neg2.rs:LL:CC
+   |
+LL |     let x = unsafe { x.wrapping_offset(-1).offset(-1) };
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
+   |
+   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
+   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+help: ALLOC was allocated here:
+  --> tests/fail/intrinsics/ptr_offset_out_of_bounds_neg2.rs:LL:CC
+   |
+LL |     let v = [0i8; 4];
+   |         ^
+   = note: BACKTRACE (of the first span):
+   = note: inside `main` at tests/fail/intrinsics/ptr_offset_out_of_bounds_neg2.rs:LL:CC
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/native-lib/fail/tracing/partial_init.rs b/src/tools/miri/tests/native-lib/fail/tracing/partial_init.rs
new file mode 100644
index 00000000000..e267f82e215
--- /dev/null
+++ b/src/tools/miri/tests/native-lib/fail/tracing/partial_init.rs
@@ -0,0 +1,25 @@
+//@only-target: x86_64-unknown-linux-gnu i686-unknown-linux-gnu
+//@compile-flags: -Zmiri-native-lib-enable-tracing
+
+extern "C" {
+    fn init_n(n: i32, ptr: *mut u8);
+}
+
+fn main() {
+    partial_init();
+}
+
+// Initialise the first 2 elements of the slice from native code, and check
+// that the 3rd is correctly deemed uninit.
+fn partial_init() {
+    let mut slice = std::mem::MaybeUninit::<[u8; 3]>::uninit();
+    let slice_ptr = slice.as_mut_ptr().cast::<u8>();
+    unsafe {
+        // Initialize the first two elements.
+        init_n(2, slice_ptr);
+        assert!(*slice_ptr == 0);
+        assert!(*slice_ptr.offset(1) == 0);
+        // Reading the third is UB!
+        let _val = *slice_ptr.offset(2); //~ ERROR: Undefined Behavior: using uninitialized data
+    }
+}
diff --git a/src/tools/miri/tests/native-lib/fail/tracing/partial_init.stderr b/src/tools/miri/tests/native-lib/fail/tracing/partial_init.stderr
new file mode 100644
index 00000000000..84fd913b5e5
--- /dev/null
+++ b/src/tools/miri/tests/native-lib/fail/tracing/partial_init.stderr
@@ -0,0 +1,39 @@
+warning: sharing memory with a native function called via FFI
+  --> tests/native-lib/fail/tracing/partial_init.rs:LL:CC
+   |
+LL |         init_n(2, slice_ptr);
+   |         ^^^^^^^^^^^^^^^^^^^^ sharing memory with a native function
+   |
+   = help: when memory is shared with a native function call, Miri can only track initialisation and provenance on a best-effort basis
+   = help: in particular, Miri assumes that the native call initializes all memory it has written to
+   = help: Miri also assumes that any part of this memory may be a pointer that is permitted to point to arbitrary exposed memory
+   = help: what this means is that Miri will easily miss Undefined Behavior related to incorrect usage of this shared memory, so you should not take a clean Miri run as a signal that your FFI code is UB-free
+   = help: tracing memory accesses in native code is not yet fully implemented, so there can be further imprecisions beyond what is documented here
+   = note: BACKTRACE:
+   = note: inside `partial_init` at tests/native-lib/fail/tracing/partial_init.rs:LL:CC
+note: inside `main`
+  --> tests/native-lib/fail/tracing/partial_init.rs:LL:CC
+   |
+LL |     partial_init();
+   |     ^^^^^^^^^^^^^^
+
+error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+  --> tests/native-lib/fail/tracing/partial_init.rs:LL:CC
+   |
+LL |         let _val = *slice_ptr.offset(2);
+   |                    ^^^^^^^^^^^^^^^^^^^^ Undefined Behavior occurred here
+   |
+   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
+   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+   = note: BACKTRACE:
+   = note: inside `partial_init` at tests/native-lib/fail/tracing/partial_init.rs:LL:CC
+note: inside `main`
+  --> tests/native-lib/fail/tracing/partial_init.rs:LL:CC
+   |
+LL |     partial_init();
+   |     ^^^^^^^^^^^^^^
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error; 1 warning emitted
+
diff --git a/src/tools/miri/tests/native-lib/fail/tracing/unexposed_reachable_alloc.rs b/src/tools/miri/tests/native-lib/fail/tracing/unexposed_reachable_alloc.rs
new file mode 100644
index 00000000000..b78c29d98db
--- /dev/null
+++ b/src/tools/miri/tests/native-lib/fail/tracing/unexposed_reachable_alloc.rs
@@ -0,0 +1,29 @@
+//@only-target: x86_64-unknown-linux-gnu i686-unknown-linux-gnu
+//@compile-flags: -Zmiri-permissive-provenance -Zmiri-native-lib-enable-tracing
+
+extern "C" {
+    fn do_one_deref(ptr: *const *const *const i32) -> usize;
+}
+
+fn main() {
+    unexposed_reachable_alloc();
+}
+
+// Expose 2 pointers by virtue of doing a native read and assert that the 3rd in
+// the chain remains properly unexposed.
+fn unexposed_reachable_alloc() {
+    let inner = 42;
+    let intermediate_a = &raw const inner;
+    let intermediate_b = &raw const intermediate_a;
+    let exposed = &raw const intermediate_b;
+    // Discard the return value; it's just there so the access in C doesn't get optimised away.
+    unsafe { do_one_deref(exposed) };
+    // Native read should have exposed the address of intermediate_b...
+    let valid: *const i32 = std::ptr::with_exposed_provenance(intermediate_b.addr());
+    // but not of intermediate_a.
+    let invalid: *const i32 = std::ptr::with_exposed_provenance(intermediate_a.addr());
+    unsafe {
+        let _ok = *valid;
+        let _not_ok = *invalid; //~ ERROR: Undefined Behavior: memory access failed: attempting to access
+    }
+}
diff --git a/src/tools/miri/tests/native-lib/fail/tracing/unexposed_reachable_alloc.stderr b/src/tools/miri/tests/native-lib/fail/tracing/unexposed_reachable_alloc.stderr
new file mode 100644
index 00000000000..2d34dac1b3f
--- /dev/null
+++ b/src/tools/miri/tests/native-lib/fail/tracing/unexposed_reachable_alloc.stderr
@@ -0,0 +1,39 @@
+warning: sharing memory with a native function called via FFI
+  --> tests/native-lib/fail/tracing/unexposed_reachable_alloc.rs:LL:CC
+   |
+LL |     unsafe { do_one_deref(exposed) };
+   |              ^^^^^^^^^^^^^^^^^^^^^ sharing memory with a native function
+   |
+   = help: when memory is shared with a native function call, Miri can only track initialisation and provenance on a best-effort basis
+   = help: in particular, Miri assumes that the native call initializes all memory it has written to
+   = help: Miri also assumes that any part of this memory may be a pointer that is permitted to point to arbitrary exposed memory
+   = help: what this means is that Miri will easily miss Undefined Behavior related to incorrect usage of this shared memory, so you should not take a clean Miri run as a signal that your FFI code is UB-free
+   = help: tracing memory accesses in native code is not yet fully implemented, so there can be further imprecisions beyond what is documented here
+   = note: BACKTRACE:
+   = note: inside `unexposed_reachable_alloc` at tests/native-lib/fail/tracing/unexposed_reachable_alloc.rs:LL:CC
+note: inside `main`
+  --> tests/native-lib/fail/tracing/unexposed_reachable_alloc.rs:LL:CC
+   |
+LL |     unexposed_reachable_alloc();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: Undefined Behavior: memory access failed: attempting to access 4 bytes, but got $HEX[noalloc] which is a dangling pointer (it has no provenance)
+  --> tests/native-lib/fail/tracing/unexposed_reachable_alloc.rs:LL:CC
+   |
+LL |         let _not_ok = *invalid;
+   |                       ^^^^^^^^ Undefined Behavior occurred here
+   |
+   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
+   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+   = note: BACKTRACE:
+   = note: inside `unexposed_reachable_alloc` at tests/native-lib/fail/tracing/unexposed_reachable_alloc.rs:LL:CC
+note: inside `main`
+  --> tests/native-lib/fail/tracing/unexposed_reachable_alloc.rs:LL:CC
+   |
+LL |     unexposed_reachable_alloc();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error; 1 warning emitted
+
diff --git a/src/tools/miri/tests/native-lib/pass/ptr_read_access.stderr b/src/tools/miri/tests/native-lib/pass/ptr_read_access.notrace.stderr
index 04a3025baef..04a3025baef 100644
--- a/src/tools/miri/tests/native-lib/pass/ptr_read_access.stderr
+++ b/src/tools/miri/tests/native-lib/pass/ptr_read_access.notrace.stderr
diff --git a/src/tools/miri/tests/native-lib/pass/ptr_read_access.stdout b/src/tools/miri/tests/native-lib/pass/ptr_read_access.notrace.stdout
index 1a8799abfc9..1a8799abfc9 100644
--- a/src/tools/miri/tests/native-lib/pass/ptr_read_access.stdout
+++ b/src/tools/miri/tests/native-lib/pass/ptr_read_access.notrace.stdout
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 4c852135367..4f3c37f00c1 100644
--- a/src/tools/miri/tests/native-lib/pass/ptr_read_access.rs
+++ b/src/tools/miri/tests/native-lib/pass/ptr_read_access.rs
@@ -1,3 +1,7 @@
+//@revisions: trace notrace
+//@[trace] only-target: x86_64-unknown-linux-gnu i686-unknown-linux-gnu
+//@[trace] compile-flags: -Zmiri-native-lib-enable-tracing
+
 fn main() {
     test_access_pointer();
     test_access_simple();
diff --git a/src/tools/miri/tests/native-lib/pass/ptr_read_access.trace.stderr b/src/tools/miri/tests/native-lib/pass/ptr_read_access.trace.stderr
new file mode 100644
index 00000000000..c2a4508b7fc
--- /dev/null
+++ b/src/tools/miri/tests/native-lib/pass/ptr_read_access.trace.stderr
@@ -0,0 +1,19 @@
+warning: sharing memory with a native function called via FFI
+  --> tests/native-lib/pass/ptr_read_access.rs:LL:CC
+   |
+LL |     unsafe { print_pointer(&x) };
+   |              ^^^^^^^^^^^^^^^^^ sharing memory with a native function
+   |
+   = help: when memory is shared with a native function call, Miri can only track initialisation and provenance on a best-effort basis
+   = help: in particular, Miri assumes that the native call initializes all memory it has written to
+   = help: Miri also assumes that any part of this memory may be a pointer that is permitted to point to arbitrary exposed memory
+   = help: what this means is that Miri will easily miss Undefined Behavior related to incorrect usage of this shared memory, so you should not take a clean Miri run as a signal that your FFI code is UB-free
+   = help: tracing memory accesses in native code is not yet fully implemented, so there can be further imprecisions beyond what is documented here
+   = note: BACKTRACE:
+   = note: inside `test_access_pointer` at tests/native-lib/pass/ptr_read_access.rs:LL:CC
+note: inside `main`
+  --> tests/native-lib/pass/ptr_read_access.rs:LL:CC
+   |
+LL |     test_access_pointer();
+   |     ^^^^^^^^^^^^^^^^^^^^^
+
diff --git a/src/tools/miri/tests/native-lib/pass/ptr_read_access.trace.stdout b/src/tools/miri/tests/native-lib/pass/ptr_read_access.trace.stdout
new file mode 100644
index 00000000000..1a8799abfc9
--- /dev/null
+++ b/src/tools/miri/tests/native-lib/pass/ptr_read_access.trace.stdout
@@ -0,0 +1 @@
+printing pointer dereference from C: 42
diff --git a/src/tools/miri/tests/native-lib/pass/ptr_write_access.stderr b/src/tools/miri/tests/native-lib/pass/ptr_write_access.notrace.stderr
index c893b0d9f65..c893b0d9f65 100644
--- a/src/tools/miri/tests/native-lib/pass/ptr_write_access.stderr
+++ b/src/tools/miri/tests/native-lib/pass/ptr_write_access.notrace.stderr
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
index 86a9c97f4ce..57def78b0ab 100644
--- a/src/tools/miri/tests/native-lib/pass/ptr_write_access.rs
+++ b/src/tools/miri/tests/native-lib/pass/ptr_write_access.rs
@@ -1,3 +1,6 @@
+//@revisions: trace notrace
+//@[trace] only-target: x86_64-unknown-linux-gnu i686-unknown-linux-gnu
+//@[trace] compile-flags: -Zmiri-native-lib-enable-tracing
 //@compile-flags: -Zmiri-permissive-provenance
 
 #![feature(box_as_ptr)]
diff --git a/src/tools/miri/tests/native-lib/pass/ptr_write_access.trace.stderr b/src/tools/miri/tests/native-lib/pass/ptr_write_access.trace.stderr
new file mode 100644
index 00000000000..dbf021b15be
--- /dev/null
+++ b/src/tools/miri/tests/native-lib/pass/ptr_write_access.trace.stderr
@@ -0,0 +1,19 @@
+warning: sharing memory with a native function called via FFI
+  --> tests/native-lib/pass/ptr_write_access.rs:LL:CC
+   |
+LL |     unsafe { increment_int(&mut x) };
+   |              ^^^^^^^^^^^^^^^^^^^^^ sharing memory with a native function
+   |
+   = help: when memory is shared with a native function call, Miri can only track initialisation and provenance on a best-effort basis
+   = help: in particular, Miri assumes that the native call initializes all memory it has written to
+   = help: Miri also assumes that any part of this memory may be a pointer that is permitted to point to arbitrary exposed memory
+   = help: what this means is that Miri will easily miss Undefined Behavior related to incorrect usage of this shared memory, so you should not take a clean Miri run as a signal that your FFI code is UB-free
+   = help: tracing memory accesses in native code is not yet fully implemented, so there can be further imprecisions beyond what is documented here
+   = note: BACKTRACE:
+   = note: inside `test_increment_int` at tests/native-lib/pass/ptr_write_access.rs:LL:CC
+note: inside `main`
+  --> tests/native-lib/pass/ptr_write_access.rs:LL:CC
+   |
+LL |     test_increment_int();
+   |     ^^^^^^^^^^^^^^^^^^^^
+
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 b89126d3d7c..021eb6adca4 100644
--- a/src/tools/miri/tests/native-lib/ptr_read_access.c
+++ b/src/tools/miri/tests/native-lib/ptr_read_access.c
@@ -49,3 +49,9 @@ typedef struct Static {
 EXPORT int32_t access_static(const Static *s_ptr) {
   return s_ptr->recurse->recurse->value;
 }
+
+/* Test: unexposed_reachable_alloc */
+
+EXPORT uintptr_t do_one_deref(const int32_t ***ptr) {
+  return (uintptr_t)*ptr;
+}
diff --git a/src/tools/miri/tests/native-lib/ptr_write_access.c b/src/tools/miri/tests/native-lib/ptr_write_access.c
index fd8b005499c..5260d0b3651 100644
--- a/src/tools/miri/tests/native-lib/ptr_write_access.c
+++ b/src/tools/miri/tests/native-lib/ptr_write_access.c
@@ -107,3 +107,11 @@ EXPORT void set_shared_mem(int32_t** ptr) {
 EXPORT void init_ptr_stored_in_shared_mem(int32_t val) {
   **shared_place = val;
 }
+
+/* Test: partial_init */
+
+EXPORT void init_n(int32_t n, char* ptr) {
+  for (int i=0; i<n; i++) {
+    *(ptr+i) = 0;
+  }
+}
diff --git a/src/tools/miri/tests/pass/0weak_memory_consistency.rs b/src/tools/miri/tests/pass/0weak_memory_consistency.rs
index b33aefaf1d5..e4ed9675de8 100644
--- a/src/tools/miri/tests/pass/0weak_memory_consistency.rs
+++ b/src/tools/miri/tests/pass/0weak_memory_consistency.rs
@@ -41,7 +41,15 @@ fn static_atomic_bool(val: bool) -> &'static AtomicBool {
 }
 
 /// Spins until it acquires a pre-determined value.
-fn loads_value(loc: &AtomicI32, ord: Ordering, val: i32) -> i32 {
+fn spin_until_i32(loc: &AtomicI32, ord: Ordering, val: i32) -> i32 {
+    while loc.load(ord) != val {
+        std::hint::spin_loop();
+    }
+    val
+}
+
+/// Spins until it acquires a pre-determined boolean.
+fn spin_until_bool(loc: &AtomicBool, ord: Ordering, val: bool) -> bool {
     while loc.load(ord) != val {
         std::hint::spin_loop();
     }
@@ -65,7 +73,7 @@ fn test_corr() {
     }); //                                           |                    |
     #[rustfmt::skip] //                              |synchronizes-with   |happens-before
     let j3 = spawn(move || { //                      |                    |
-        loads_value(&y, Acquire, 1); // <------------+                    |
+        spin_until_i32(&y, Acquire, 1); // <---------+                    |
         x.load(Relaxed) // <----------------------------------------------+
         // The two reads on x are ordered by hb, so they cannot observe values
         // differently from the modification order. If the first read observed
@@ -90,12 +98,12 @@ fn test_wrc() {
     }); //                                           |                     |
     #[rustfmt::skip] //                              |synchronizes-with    |
     let j2 = spawn(move || { //                      |                     |
-        loads_value(&x, Acquire, 1); // <------------+                     |
+        spin_until_i32(&x, Acquire, 1); // <---------+                     |
         y.store(1, Release); // ---------------------+                     |happens-before
     }); //                                           |                     |
     #[rustfmt::skip] //                              |synchronizes-with    |
     let j3 = spawn(move || { //                      |                     |
-        loads_value(&y, Acquire, 1); // <------------+                     |
+        spin_until_i32(&y, Acquire, 1); // <---------+                     |
         x.load(Relaxed) // <-----------------------------------------------+
     });
 
@@ -121,7 +129,7 @@ fn test_message_passing() {
     #[rustfmt::skip] //                              |synchronizes-with  | happens-before
     let j2 = spawn(move || { //                      |                   |
         let x = x; // avoid field capturing          |                   |
-        loads_value(&y, Acquire, 1); // <------------+                   |
+        spin_until_i32(&y, Acquire, 1); // <---------+                   |
         unsafe { *x.0 } // <---------------------------------------------+
     });
 
@@ -216,12 +224,12 @@ fn test_sync_through_rmw_and_fences() {
     let go = static_atomic_bool(false);
 
     let t1 = spawn(move || {
-        while !go.load(Relaxed) {}
+        spin_until_bool(go, Relaxed, true);
         rdmw(y, x, z)
     });
 
     let t2 = spawn(move || {
-        while !go.load(Relaxed) {}
+        spin_until_bool(go, Relaxed, true);
         rdmw(z, x, y)
     });
 
diff --git a/src/tools/miri/tests/pass/0weak_memory_consistency_sc.rs b/src/tools/miri/tests/pass/0weak_memory_consistency_sc.rs
index 45cc5e6e722..937c2a8cf28 100644
--- a/src/tools/miri/tests/pass/0weak_memory_consistency_sc.rs
+++ b/src/tools/miri/tests/pass/0weak_memory_consistency_sc.rs
@@ -20,7 +20,15 @@ fn static_atomic_bool(val: bool) -> &'static AtomicBool {
 }
 
 /// Spins until it acquires a pre-determined value.
-fn loads_value(loc: &AtomicI32, ord: Ordering, val: i32) -> i32 {
+fn spin_until_i32(loc: &AtomicI32, ord: Ordering, val: i32) -> i32 {
+    while loc.load(ord) != val {
+        std::hint::spin_loop();
+    }
+    val
+}
+
+/// Spins until it acquires a pre-determined boolean.
+fn spin_until_bool(loc: &AtomicBool, ord: Ordering, val: bool) -> bool {
     while loc.load(ord) != val {
         std::hint::spin_loop();
     }
@@ -60,11 +68,11 @@ fn test_iriw_sc_rlx() {
     let a = spawn(move || x.store(true, Relaxed));
     let b = spawn(move || y.store(true, Relaxed));
     let c = spawn(move || {
-        while !x.load(SeqCst) {}
+        spin_until_bool(x, SeqCst, true);
         y.load(SeqCst)
     });
     let d = spawn(move || {
-        while !y.load(SeqCst) {}
+        spin_until_bool(y, SeqCst, true);
         x.load(SeqCst)
     });
 
@@ -136,7 +144,7 @@ fn test_cpp20_rwc_syncs() {
     });
 
     let j2 = spawn(move || {
-        loads_value(&x, Relaxed, 1);
+        spin_until_i32(&x, Relaxed, 1);
         fence(SeqCst);
         y.load(Relaxed)
     });
diff --git a/src/tools/miri/tests/pass/alloc-access-tracking.rs b/src/tools/miri/tests/pass/alloc-access-tracking.rs
index 0e88951dc43..9eba0ca171b 100644
--- a/src/tools/miri/tests/pass/alloc-access-tracking.rs
+++ b/src/tools/miri/tests/pass/alloc-access-tracking.rs
@@ -1,7 +1,7 @@
 #![no_std]
 #![no_main]
-//@compile-flags: -Zmiri-track-alloc-id=20 -Zmiri-track-alloc-accesses -Cpanic=abort
-//@normalize-stderr-test: "id 20" -> "id $$ALLOC"
+//@compile-flags: -Zmiri-track-alloc-id=19 -Zmiri-track-alloc-accesses -Cpanic=abort
+//@normalize-stderr-test: "id 19" -> "id $$ALLOC"
 //@only-target: linux # alloc IDs differ between OSes (due to extern static allocations)
 
 extern "Rust" {
diff --git a/src/tools/miri/tests/pass/shims/ctor.rs b/src/tools/miri/tests/pass/shims/ctor.rs
new file mode 100644
index 00000000000..b997d2386b8
--- /dev/null
+++ b/src/tools/miri/tests/pass/shims/ctor.rs
@@ -0,0 +1,46 @@
+use std::sync::atomic::{AtomicUsize, Ordering};
+
+static COUNT: AtomicUsize = AtomicUsize::new(0);
+
+unsafe extern "C" fn ctor() {
+    COUNT.fetch_add(1, Ordering::Relaxed);
+}
+
+#[rustfmt::skip]
+macro_rules! ctor {
+    ($ident:ident = $ctor:ident) => {
+        #[cfg_attr(
+            all(any(
+                target_os = "linux",
+                target_os = "android",
+                target_os = "dragonfly",
+                target_os = "freebsd",
+                target_os = "haiku",
+                target_os = "illumos",
+                target_os = "netbsd",
+                target_os = "openbsd",
+                target_os = "solaris",
+                target_os = "none",
+                target_family = "wasm",
+            )),
+            link_section = ".init_array"
+        )]
+        #[cfg_attr(windows, link_section = ".CRT$XCU")]
+        #[cfg_attr(
+            any(target_os = "macos", target_os = "ios"),
+            // We do not set the `mod_init_funcs` flag here since ctor/inventory also do not do
+            // that. See <https://github.com/rust-lang/miri/pull/4459#discussion_r2200115629>.
+            link_section = "__DATA,__mod_init_func"
+        )]
+        #[used]
+        static $ident: unsafe extern "C" fn() = $ctor;
+    };
+}
+
+ctor! { CTOR1 = ctor }
+ctor! { CTOR2 = ctor }
+ctor! { CTOR3 = ctor }
+
+fn main() {
+    assert_eq!(COUNT.load(Ordering::Relaxed), 3, "ctors did not run");
+}
diff --git a/src/tools/miri/tests/pass/shims/random.rs b/src/tools/miri/tests/pass/shims/random.rs
index ae75ebdcd3f..2a5c8993662 100644
--- a/src/tools/miri/tests/pass/shims/random.rs
+++ b/src/tools/miri/tests/pass/shims/random.rs
@@ -1,5 +1,5 @@
 #![feature(random)]
 
 fn main() {
-    let _x: i32 = std::random::random();
+    let _x: i32 = std::random::random(..);
 }
diff --git a/src/tools/miri/tests/pass/weak_memory/weak.rs b/src/tools/miri/tests/pass/weak_memory/weak.rs
index eeab4ebf129..199f83f0528 100644
--- a/src/tools/miri/tests/pass/weak_memory/weak.rs
+++ b/src/tools/miri/tests/pass/weak_memory/weak.rs
@@ -24,7 +24,7 @@ fn static_atomic(val: usize) -> &'static AtomicUsize {
 }
 
 // Spins until it reads the given value
-fn reads_value(loc: &AtomicUsize, val: usize) -> usize {
+fn spin_until(loc: &AtomicUsize, val: usize) -> usize {
     while loc.load(Relaxed) != val {
         std::hint::spin_loop();
     }
@@ -85,7 +85,7 @@ fn initialization_write(add_fence: bool) -> bool {
     });
 
     let j2 = spawn(move || {
-        reads_value(wait, 1);
+        spin_until(wait, 1);
         if add_fence {
             fence(AcqRel);
         }
@@ -119,12 +119,12 @@ fn faa_replaced_by_load() -> bool {
     let go = static_atomic(0);
 
     let t1 = spawn(move || {
-        while go.load(Relaxed) == 0 {}
+        spin_until(go, 1);
         rdmw(y, x, z)
     });
 
     let t2 = spawn(move || {
-        while go.load(Relaxed) == 0 {}
+        spin_until(go, 1);
         rdmw(z, x, y)
     });
 
diff --git a/src/tools/miri/tests/ui.rs b/src/tools/miri/tests/ui.rs
index 5239f8338ee..43f855d57dd 100644
--- a/src/tools/miri/tests/ui.rs
+++ b/src/tools/miri/tests/ui.rs
@@ -97,6 +97,8 @@ fn miri_config(
     let mut config = Config {
         target: Some(target.to_owned()),
         program,
+        // When changing this, remember to also adjust the logic in bootstrap, in Miri's test step,
+        // that deletes the `miri_ui` dir when it needs a rebuild.
         out_dir: PathBuf::from(env!("CARGO_TARGET_TMPDIR")).join("miri_ui"),
         threads: std::env::var("MIRI_TEST_THREADS")
             .ok()
diff --git a/src/tools/opt-dist/Cargo.toml b/src/tools/opt-dist/Cargo.toml
index dfa884bc3f7..2ed3fbac709 100644
--- a/src/tools/opt-dist/Cargo.toml
+++ b/src/tools/opt-dist/Cargo.toml
@@ -10,7 +10,7 @@ log = "0.4"
 anyhow = "1"
 humantime = "2"
 humansize = "2"
-sysinfo = { version = "0.35.0", default-features = false, features = ["disk"] }
+sysinfo = { version = "0.36.0", default-features = false, features = ["disk"] }
 fs_extra = "1"
 camino = "1"
 tar = "0.4"
diff --git a/src/tools/opt-dist/src/tests.rs b/src/tools/opt-dist/src/tests.rs
index 705a1750ae8..2d2aab86eda 100644
--- a/src/tools/opt-dist/src/tests.rs
+++ b/src/tools/opt-dist/src/tests.rs
@@ -106,7 +106,10 @@ llvm-config = "{llvm_config}"
             "tests/incremental",
             "tests/mir-opt",
             "tests/pretty",
+            // Make sure that we don't use too new GLIBC symbols on x64
             "tests/run-make/glibc-symbols-x86_64-unknown-linux-gnu",
+            // Make sure that we use LLD by default on x64
+            "tests/run-make/rust-lld-x86_64-unknown-linux-gnu-dist",
             "tests/ui",
             "tests/crashes",
         ];
diff --git a/src/tools/remote-test-client/src/main.rs b/src/tools/remote-test-client/src/main.rs
index bda08423487..b9741431b50 100644
--- a/src/tools/remote-test-client/src/main.rs
+++ b/src/tools/remote-test-client/src/main.rs
@@ -335,7 +335,9 @@ fn run(support_lib_count: usize, exe: String, all_args: Vec<String>) {
         std::process::exit(code);
     } else {
         println!("died due to signal {}", code);
-        std::process::exit(3);
+        // Behave like bash and other tools and exit with 128 + the signal
+        // number. That way we can avoid special case code in other places.
+        std::process::exit(128 + code);
     }
 }
 
diff --git a/src/tools/run-make-support/CHANGELOG.md b/src/tools/run-make-support/CHANGELOG.md
deleted file mode 100644
index c1b7b618a92..00000000000
--- a/src/tools/run-make-support/CHANGELOG.md
+++ /dev/null
@@ -1,83 +0,0 @@
-# Changelog
-
-All notable changes to the `run_make_support` library should be documented in this file.
-
-The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the support
-library should adhere to [Semantic Versioning](https://semver.org/spec/v2.0.0.html) even if it's
-not intended for public consumption (it's moreso to help internally, to help test writers track
-changes to the support library).
-
-This support library will probably never reach 1.0. Please bump the minor version in `Cargo.toml` if
-you make any breaking changes or other significant changes, or bump the patch version for bug fixes.
-
-## [0.2.0] - 2024-06-11
-
-### Added
-
-- Added `fs_wrapper` module which provides panic-on-fail helpers for their respective `std::fs`
-  counterparts, the motivation is to:
-    - Reduce littering `.unwrap()` or `.expect()` everywhere for fs operations
-    - Help the test writer avoid forgetting to check fs results (even though enforced by
-      `-Dunused_must_use`)
-    - Provide better panic messages by default
-- Added `path()` helper which creates a `Path` relative to `cwd()` (but is less noisy).
-
-### Changed
-
-- Marked many functions with `#[must_use]`, and rmake.rs are now compiled with `-Dunused_must_use`.
-
-## [0.1.0] - 2024-06-09
-
-### Changed
-
-- Use *drop bombs* to enforce that commands are executed; a command invocation will panic if it is
-  constructed but never executed. Execution methods `Command::{run, run_fail}` will defuse the drop
-  bomb.
-- Added `Command` helpers that forward to `std::process::Command` counterparts.
-
-### Removed
-
-- The `env_var` method which was incorrectly named and is `env_clear` underneath and is a footgun
-  from `impl_common_helpers`. For example, removing `TMPDIR` on Unix and `TMP`/`TEMP` breaks
-  `std::env::temp_dir` and wrecks anything using that, such as rustc's codgen.
-- Removed `Deref`/`DerefMut` for `run_make_support::Command` -> `std::process::Command` because it
-  causes a method chain like `htmldocck().arg().run()` to fail, because `arg()` resolves to
-  `std::process::Command` which also returns a `&mut std::process::Command`, causing the `run()` to
-  be not found.
-
-## [0.0.0] - 2024-06-09
-
-Consider this version to contain all changes made to the support library before we started to track
-changes in this changelog.
-
-### Added
-
-- Custom command wrappers around `std::process::Command` (`run_make_support::Command`) and custom
-  wrapper around `std::process::Output` (`CompletedProcess`) to make it more convenient to work with
-  commands and their output, and help avoid forgetting to check for exit status.
-    - `Command`: `set_stdin`, `run`, `run_fail`.
-    - `CompletedProcess`: `std{err,out}_utf8`, `status`, `assert_std{err,out}_{equals, contains,
-      not_contains}`, `assert_exit_code`.
-- `impl_common_helpers` macro to avoid repeating adding common convenience methods, including:
-    - Environment manipulation methods: `env`, `env_remove`
-    - Command argument providers: `arg`, `args`
-    - Common invocation inspection (of the command invocation up until `inspect` is called):
-      `inspect`
-    - Execution methods: `run` (for commands expected to succeed execution, exit status `0`) and
-      `run_fail` (for commands expected to fail execution, exit status non-zero).
-- Command wrappers around: `rustc`, `clang`, `cc`, `rustc`, `rustdoc`, `llvm-readobj`.
-- Thin helpers to construct `python` and `htmldocck` commands.
-- `run` and `run_fail` (like `Command::{run, run_fail}`) for running binaries, which sets suitable
-  env vars (like `LD_LIB_PATH` or equivalent, `TARGET_RPATH_ENV`, `PATH` on Windows).
-- Pseudo command `diff` which has similar functionality as the cli util but not the same API.
-- Convenience panic-on-fail helpers `env_var`, `env_var_os`, `cwd` for their `std::env` conterparts.
-- Convenience panic-on-fail helpers for reading respective env vars: `target`, `source_root`.
-- Platform check helpers: `is_windows`, `is_msvc`, `cygpath_windows`, `uname`.
-- fs helpers: `copy_dir_all`.
-- `recursive_diff` helper.
-- Generic `assert_not_contains` helper.
-- Scoped run-with-teardown helper `run_in_tmpdir` which is designed to run commands in a temporary
-  directory that is cleared when closure returns.
-- Helpers for constructing the name of binaries and libraries: `rust_lib_name`, `static_lib_name`,
-  `bin_name`, `dynamic_lib_name`.
-- Re-export libraries: `gimli`, `object`, `regex`, `wasmparsmer`.
diff --git a/src/tools/run-make-support/Cargo.toml b/src/tools/run-make-support/Cargo.toml
index 3226f467ba4..a4e7534137d 100644
--- a/src/tools/run-make-support/Cargo.toml
+++ b/src/tools/run-make-support/Cargo.toml
@@ -1,18 +1,27 @@
 [package]
 name = "run_make_support"
-version = "0.2.0"
-edition = "2021"
+version = "0.0.0"
+edition = "2024"
 
 [dependencies]
+
+# These dependencies are either used to implement part of support library
+# functionality, or re-exported to test recipe programs via the support library,
+# or both.
+
+# tidy-alphabetical-start
 bstr = "1.12"
+gimli = "0.32"
+libc = "0.2"
 object = "0.37"
+regex = "1.11"
+serde_json = "1.0"
 similar = "2.7"
 wasmparser = { version = "0.219", default-features = false, features = ["std"] }
-regex = "1.11"
-gimli = "0.32"
+# tidy-alphabetical-end
+
+# Shared with bootstrap and compiletest
 build_helper = { path = "../../build_helper" }
-serde_json = "1.0"
-libc = "0.2"
 
 [lib]
 crate-type = ["lib", "dylib"]
diff --git a/src/tools/run-make-support/src/artifact_names.rs b/src/tools/run-make-support/src/artifact_names.rs
index b0d588d3550..a889b30e145 100644
--- a/src/tools/run-make-support/src/artifact_names.rs
+++ b/src/tools/run-make-support/src/artifact_names.rs
@@ -2,7 +2,7 @@
 //! libraries which are target-dependent.
 
 use crate::target;
-use crate::targets::is_msvc;
+use crate::targets::is_windows_msvc;
 
 /// Construct the static library name based on the target.
 #[track_caller]
@@ -10,7 +10,7 @@ use crate::targets::is_msvc;
 pub fn static_lib_name(name: &str) -> String {
     assert!(!name.contains(char::is_whitespace), "static library name cannot contain whitespace");
 
-    if is_msvc() { format!("{name}.lib") } else { format!("lib{name}.a") }
+    if is_windows_msvc() { format!("{name}.lib") } else { format!("lib{name}.a") }
 }
 
 /// Construct the dynamic library name based on the target.
@@ -45,7 +45,7 @@ pub fn dynamic_lib_extension() -> &'static str {
 #[track_caller]
 #[must_use]
 pub fn msvc_import_dynamic_lib_name(name: &str) -> String {
-    assert!(is_msvc(), "this function is exclusive to MSVC");
+    assert!(is_windows_msvc(), "this function is exclusive to MSVC");
     assert!(!name.contains(char::is_whitespace), "import library name cannot contain whitespace");
 
     format!("{name}.dll.lib")
diff --git a/src/tools/run-make-support/src/env.rs b/src/tools/run-make-support/src/env.rs
index 9acbb16d73e..cf1a6f7351a 100644
--- a/src/tools/run-make-support/src/env.rs
+++ b/src/tools/run-make-support/src/env.rs
@@ -18,11 +18,20 @@ pub fn env_var_os(name: &str) -> OsString {
     }
 }
 
-/// Check if `NO_DEBUG_ASSERTIONS` is set (usually this may be set in CI jobs).
+/// Check if staged `rustc`-under-test was built with debug assertions.
 #[track_caller]
 #[must_use]
-pub fn no_debug_assertions() -> bool {
-    std::env::var_os("NO_DEBUG_ASSERTIONS").is_some()
+pub fn rustc_debug_assertions_enabled() -> bool {
+    // Note: we assume this env var is set when the test recipe is being executed.
+    std::env::var_os("__RUSTC_DEBUG_ASSERTIONS_ENABLED").is_some()
+}
+
+/// Check if staged `std`-under-test was built with debug assertions.
+#[track_caller]
+#[must_use]
+pub fn std_debug_assertions_enabled() -> bool {
+    // Note: we assume this env var is set when the test recipe is being executed.
+    std::env::var_os("__STD_DEBUG_ASSERTIONS_ENABLED").is_some()
 }
 
 /// A wrapper around [`std::env::set_current_dir`] which includes the directory
diff --git a/src/tools/run-make-support/src/external_deps/c_build.rs b/src/tools/run-make-support/src/external_deps/c_build.rs
index 9dd30713f95..ecbf5ba8fe0 100644
--- a/src/tools/run-make-support/src/external_deps/c_build.rs
+++ b/src/tools/run-make-support/src/external_deps/c_build.rs
@@ -4,7 +4,7 @@ use crate::artifact_names::{dynamic_lib_name, static_lib_name};
 use crate::external_deps::c_cxx_compiler::{cc, cxx};
 use crate::external_deps::llvm::llvm_ar;
 use crate::path_helpers::path;
-use crate::targets::{is_darwin, is_msvc, is_windows};
+use crate::targets::{is_darwin, is_windows, is_windows_msvc};
 
 // FIXME(Oneirical): These native build functions should take a Path-based generic.
 
@@ -24,12 +24,12 @@ pub fn build_native_static_lib_optimized(lib_name: &str) -> PathBuf {
 
 #[track_caller]
 fn build_native_static_lib_internal(lib_name: &str, optimzed: bool) -> PathBuf {
-    let obj_file = if is_msvc() { format!("{lib_name}") } else { format!("{lib_name}.o") };
+    let obj_file = if is_windows_msvc() { format!("{lib_name}") } else { format!("{lib_name}.o") };
     let src = format!("{lib_name}.c");
     let lib_path = static_lib_name(lib_name);
 
     let mut cc = cc();
-    if !is_msvc() {
+    if !is_windows_msvc() {
         cc.arg("-v");
     }
     if optimzed {
@@ -37,7 +37,7 @@ fn build_native_static_lib_internal(lib_name: &str, optimzed: bool) -> PathBuf {
     }
     cc.arg("-c").out_exe(&obj_file).input(src).optimize().run();
 
-    let obj_file = if is_msvc() {
+    let obj_file = if is_windows_msvc() {
         PathBuf::from(format!("{lib_name}.obj"))
     } else {
         PathBuf::from(format!("{lib_name}.o"))
@@ -50,16 +50,17 @@ fn build_native_static_lib_internal(lib_name: &str, optimzed: bool) -> PathBuf {
 /// [`std::env::consts::DLL_PREFIX`] and [`std::env::consts::DLL_EXTENSION`].
 #[track_caller]
 pub fn build_native_dynamic_lib(lib_name: &str) -> PathBuf {
-    let obj_file = if is_msvc() { format!("{lib_name}") } else { format!("{lib_name}.o") };
+    let obj_file = if is_windows_msvc() { format!("{lib_name}") } else { format!("{lib_name}.o") };
     let src = format!("{lib_name}.c");
     let lib_path = dynamic_lib_name(lib_name);
-    if is_msvc() {
+    if is_windows_msvc() {
         cc().arg("-c").out_exe(&obj_file).input(src).run();
     } else {
         cc().arg("-v").arg("-c").out_exe(&obj_file).input(src).run();
     };
-    let obj_file = if is_msvc() { format!("{lib_name}.obj") } else { format!("{lib_name}.o") };
-    if is_msvc() {
+    let obj_file =
+        if is_windows_msvc() { format!("{lib_name}.obj") } else { format!("{lib_name}.o") };
+    if is_windows_msvc() {
         let out_arg = format!("-out:{lib_path}");
         cc().input(&obj_file).args(&["-link", "-dll", &out_arg]).run();
     } else if is_darwin() {
@@ -79,15 +80,15 @@ pub fn build_native_dynamic_lib(lib_name: &str) -> PathBuf {
 /// Built from a C++ file.
 #[track_caller]
 pub fn build_native_static_lib_cxx(lib_name: &str) -> PathBuf {
-    let obj_file = if is_msvc() { format!("{lib_name}") } else { format!("{lib_name}.o") };
+    let obj_file = if is_windows_msvc() { format!("{lib_name}") } else { format!("{lib_name}.o") };
     let src = format!("{lib_name}.cpp");
     let lib_path = static_lib_name(lib_name);
-    if is_msvc() {
+    if is_windows_msvc() {
         cxx().arg("-EHs").arg("-c").out_exe(&obj_file).input(src).run();
     } else {
         cxx().arg("-c").out_exe(&obj_file).input(src).run();
     };
-    let obj_file = if is_msvc() {
+    let obj_file = if is_windows_msvc() {
         PathBuf::from(format!("{lib_name}.obj"))
     } else {
         PathBuf::from(format!("{lib_name}.o"))
diff --git a/src/tools/run-make-support/src/external_deps/c_cxx_compiler/cc.rs b/src/tools/run-make-support/src/external_deps/c_cxx_compiler/cc.rs
index 0e6d6ea6075..31469e669e1 100644
--- a/src/tools/run-make-support/src/external_deps/c_cxx_compiler/cc.rs
+++ b/src/tools/run-make-support/src/external_deps/c_cxx_compiler/cc.rs
@@ -1,7 +1,7 @@
 use std::path::Path;
 
 use crate::command::Command;
-use crate::{env_var, is_msvc};
+use crate::{env_var, is_windows_msvc};
 
 /// Construct a new platform-specific C compiler invocation.
 ///
@@ -82,7 +82,7 @@ impl Cc {
     pub fn out_exe(&mut self, name: &str) -> &mut Self {
         let mut path = std::path::PathBuf::from(name);
 
-        if is_msvc() {
+        if is_windows_msvc() {
             path.set_extension("exe");
             let fe_path = path.clone();
             path.set_extension("");
@@ -108,7 +108,7 @@ impl Cc {
     /// Optimize the output.
     /// Equivalent to `-O3` for GNU-compatible linkers or `-O2` for MSVC linkers.
     pub fn optimize(&mut self) -> &mut Self {
-        if is_msvc() {
+        if is_windows_msvc() {
             self.cmd.arg("-O2");
         } else {
             self.cmd.arg("-O3");
diff --git a/src/tools/run-make-support/src/external_deps/c_cxx_compiler/extras.rs b/src/tools/run-make-support/src/external_deps/c_cxx_compiler/extras.rs
index c0317633873..ac7392641c0 100644
--- a/src/tools/run-make-support/src/external_deps/c_cxx_compiler/extras.rs
+++ b/src/tools/run-make-support/src/external_deps/c_cxx_compiler/extras.rs
@@ -1,9 +1,9 @@
-use crate::{is_msvc, is_win7, is_windows, uname};
+use crate::{is_win7, is_windows, is_windows_msvc, uname};
 
 /// `EXTRACFLAGS`
 pub fn extra_c_flags() -> Vec<&'static str> {
     if is_windows() {
-        if is_msvc() {
+        if is_windows_msvc() {
             let mut libs =
                 vec!["ws2_32.lib", "userenv.lib", "bcrypt.lib", "ntdll.lib", "synchronization.lib"];
             if is_win7() {
@@ -29,7 +29,7 @@ pub fn extra_c_flags() -> Vec<&'static str> {
 /// `EXTRACXXFLAGS`
 pub fn extra_cxx_flags() -> Vec<&'static str> {
     if is_windows() {
-        if is_msvc() { vec![] } else { vec!["-lstdc++"] }
+        if is_windows_msvc() { vec![] } else { vec!["-lstdc++"] }
     } else {
         match &uname()[..] {
             "Darwin" => vec!["-lc++"],
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 72a1e062a38..1ea549ca7ea 100644
--- a/src/tools/run-make-support/src/external_deps/rustc.rs
+++ b/src/tools/run-make-support/src/external_deps/rustc.rs
@@ -6,7 +6,7 @@ use crate::command::Command;
 use crate::env::env_var;
 use crate::path_helpers::cwd;
 use crate::util::set_host_compiler_dylib_path;
-use crate::{is_aix, is_darwin, is_msvc, is_windows, target, uname};
+use crate::{is_aix, is_darwin, is_windows, is_windows_msvc, target, uname};
 
 /// Construct a new `rustc` invocation. This will automatically set the library
 /// search path as `-L cwd()`. Use [`bare_rustc`] to avoid this.
@@ -377,7 +377,7 @@ impl Rustc {
             // So we end up with the following hack: we link use static:-bundle to only
             // link the parts of libstdc++ that we actually use, which doesn't include
             // the dependency on the pthreads DLL.
-            if !is_msvc() {
+            if !is_windows_msvc() {
                 self.cmd.arg("-lstatic:-bundle=stdc++");
             };
         } else if is_darwin() {
diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs
index 67d8c351a59..29cd6c4ad15 100644
--- a/src/tools/run-make-support/src/lib.rs
+++ b/src/tools/run-make-support/src/lib.rs
@@ -3,9 +3,6 @@
 //! notably is built via cargo: this means that if your test wants some non-trivial utility, such
 //! as `object` or `wasmparser`, they can be re-exported and be made available through this library.
 
-// We want to control use declaration ordering and spacing (and preserve use group comments), so
-// skip rustfmt on this file.
-#![cfg_attr(rustfmt, rustfmt::skip)]
 #![warn(unreachable_pub)]
 
 mod command;
@@ -22,8 +19,8 @@ pub mod path_helpers;
 pub mod run;
 pub mod scoped_run;
 pub mod string;
-pub mod targets;
 pub mod symbols;
+pub mod targets;
 
 // Internally we call our fs-related support module as `fs`, but re-export its content as `rfs`
 // to tests to avoid colliding with commonly used `use std::fs;`.
@@ -36,77 +33,56 @@ pub mod rfs {
 }
 
 // Re-exports of third-party library crates.
-// tidy-alphabetical-start
-pub use bstr;
-pub use gimli;
-pub use libc;
-pub use object;
-pub use regex;
-pub use serde_json;
-pub use similar;
-pub use wasmparser;
-// tidy-alphabetical-end
+pub use {bstr, gimli, libc, object, regex, serde_json, similar, wasmparser};
 
-// Re-exports of external dependencies.
-pub use external_deps::{
-    cargo, c_build, c_cxx_compiler, clang, htmldocck, llvm, python, rustc, rustdoc
+// Helpers for building names of output artifacts that are potentially target-specific.
+pub use crate::artifact_names::{
+    bin_name, dynamic_lib_extension, dynamic_lib_name, msvc_import_dynamic_lib_name, rust_lib_name,
+    static_lib_name,
 };
-
-// These rely on external dependencies.
-pub use c_cxx_compiler::{Cc, Gcc, cc, cxx, extra_c_flags, extra_cxx_flags, gcc};
-pub use c_build::{
+pub use crate::assertion_helpers::{
+    assert_contains, assert_contains_regex, assert_count_is, assert_dirs_are_equal, assert_equals,
+    assert_not_contains, assert_not_contains_regex,
+};
+// `diff` is implemented in terms of the [similar] library.
+//
+// [similar]: https://github.com/mitsuhiko/similar
+pub use crate::diff::{Diff, diff};
+// Panic-on-fail [`std::env::var`] and [`std::env::var_os`] wrappers.
+pub use crate::env::{env_var, env_var_os, set_current_dir};
+pub use crate::external_deps::c_build::{
     build_native_dynamic_lib, build_native_static_lib, build_native_static_lib_cxx,
     build_native_static_lib_optimized,
 };
-pub use cargo::cargo;
-pub use clang::{clang, Clang};
-pub use htmldocck::htmldocck;
-pub use llvm::{
-    llvm_ar, llvm_bcanalyzer, llvm_dis, llvm_dwarfdump, llvm_filecheck, llvm_nm, llvm_objcopy,
-    llvm_objdump, llvm_profdata, llvm_readobj, LlvmAr, LlvmBcanalyzer, LlvmDis, LlvmDwarfdump,
-    LlvmFilecheck, LlvmNm, LlvmObjcopy, LlvmObjdump, LlvmProfdata, LlvmReadobj,
-};
-pub use python::python_command;
-pub use rustc::{bare_rustc, rustc, rustc_path, Rustc};
-pub use rustdoc::{bare_rustdoc, rustdoc, Rustdoc};
-
-/// [`diff`][mod@diff] is implemented in terms of the [similar] library.
-///
-/// [similar]: https://github.com/mitsuhiko/similar
-pub use diff::{diff, Diff};
-
-/// Panic-on-fail [`std::env::var`] and [`std::env::var_os`] wrappers.
-pub use env::{env_var, env_var_os, set_current_dir};
-
-/// Convenience helpers for running binaries and other commands.
-pub use run::{cmd, run, run_fail, run_with_args};
-
-/// Helpers for checking target information.
-pub use targets::{
-    apple_os, is_aix, is_darwin, is_msvc, is_windows, is_windows_gnu, is_windows_msvc, is_win7, llvm_components_contain,
-    target, uname,
+// Re-exports of external dependencies.
+pub use crate::external_deps::c_cxx_compiler::{
+    Cc, Gcc, cc, cxx, extra_c_flags, extra_cxx_flags, gcc,
 };
-
-/// Helpers for building names of output artifacts that are potentially target-specific.
-pub use artifact_names::{
-    bin_name, dynamic_lib_extension, dynamic_lib_name, msvc_import_dynamic_lib_name, rust_lib_name,
-    static_lib_name,
+pub use crate::external_deps::cargo::cargo;
+pub use crate::external_deps::clang::{Clang, clang};
+pub use crate::external_deps::htmldocck::htmldocck;
+pub use crate::external_deps::llvm::{
+    self, LlvmAr, LlvmBcanalyzer, LlvmDis, LlvmDwarfdump, LlvmFilecheck, LlvmNm, LlvmObjcopy,
+    LlvmObjdump, LlvmProfdata, LlvmReadobj, llvm_ar, llvm_bcanalyzer, llvm_dis, llvm_dwarfdump,
+    llvm_filecheck, llvm_nm, llvm_objcopy, llvm_objdump, llvm_profdata, llvm_readobj,
 };
-
-/// Path-related helpers.
-pub use path_helpers::{
+pub use crate::external_deps::python::python_command;
+pub use crate::external_deps::rustc::{self, Rustc, bare_rustc, rustc, rustc_path};
+pub use crate::external_deps::rustdoc::{Rustdoc, bare_rustdoc, rustdoc};
+// Path-related helpers.
+pub use crate::path_helpers::{
     build_root, cwd, filename_contains, filename_not_in_denylist, has_extension, has_prefix,
     has_suffix, not_contains, path, shallow_find_directories, shallow_find_files, source_root,
 };
-
-/// Helpers for scoped test execution where certain properties are attempted to be maintained.
-pub use scoped_run::{run_in_tmpdir, test_while_readonly};
-
-pub use assertion_helpers::{
-    assert_contains, assert_contains_regex, assert_count_is, assert_dirs_are_equal, assert_equals,
-    assert_not_contains, assert_not_contains_regex,
-};
-
-pub use string::{
+// Convenience helpers for running binaries and other commands.
+pub use crate::run::{cmd, run, run_fail, run_with_args};
+// Helpers for scoped test execution where certain properties are attempted to be maintained.
+pub use crate::scoped_run::{run_in_tmpdir, test_while_readonly};
+pub use crate::string::{
     count_regex_matches_in_files_with_extension, invalid_utf8_contains, invalid_utf8_not_contains,
 };
+// Helpers for checking target information.
+pub use crate::targets::{
+    apple_os, is_aix, is_darwin, is_win7, is_windows, is_windows_gnu, is_windows_msvc,
+    llvm_components_contain, target, uname,
+};
diff --git a/src/tools/run-make-support/src/linker.rs b/src/tools/run-make-support/src/linker.rs
index 89093cf0113..b2893ad88fe 100644
--- a/src/tools/run-make-support/src/linker.rs
+++ b/src/tools/run-make-support/src/linker.rs
@@ -1,6 +1,6 @@
 use regex::Regex;
 
-use crate::{Rustc, is_msvc};
+use crate::{Rustc, is_windows_msvc};
 
 /// Asserts that `rustc` uses LLD for linking when executed.
 pub fn assert_rustc_uses_lld(rustc: &mut Rustc) {
@@ -22,7 +22,7 @@ pub fn assert_rustc_doesnt_use_lld(rustc: &mut Rustc) {
 
 fn get_stderr_with_linker_messages(rustc: &mut Rustc) -> String {
     // lld-link is used if msvc, otherwise a gnu-compatible lld is used.
-    let linker_version_flag = if is_msvc() { "--version" } else { "-Wl,-v" };
+    let linker_version_flag = if is_windows_msvc() { "--version" } else { "-Wl,-v" };
 
     let output = rustc.arg("-Wlinker-messages").link_arg(linker_version_flag).run();
     output.stderr_utf8()
diff --git a/src/tools/run-make-support/src/symbols.rs b/src/tools/run-make-support/src/symbols.rs
index e4d244e14a4..0e11360bd5a 100644
--- a/src/tools/run-make-support/src/symbols.rs
+++ b/src/tools/run-make-support/src/symbols.rs
@@ -1,6 +1,7 @@
+use std::collections::BTreeSet;
 use std::path::Path;
 
-use object::{self, Object, ObjectSymbol, SymbolIterator};
+use object::{self, Object, ObjectSymbol};
 
 /// Given an [`object::File`], find the exported dynamic symbol names via
 /// [`object::Object::exports`]. This does not distinguish between which section the symbols appear
@@ -14,47 +15,180 @@ pub fn exported_dynamic_symbol_names<'file>(file: &'file object::File<'file>) ->
         .collect()
 }
 
-/// Iterate through the symbols in an object file. See [`object::Object::symbols`].
+/// Check an object file's symbols for any matching **substrings**. That is, if an object file
+/// contains a symbol named `hello_world`, it will be matched against a provided `substrings` of
+/// `["hello", "bar"]`.
+///
+/// Returns `true` if **any** of the symbols found in the object file at `path` contain a
+/// **substring** listed in `substrings`.
 ///
 /// Panics if `path` is not a valid object file readable by the current user or if `path` cannot be
 /// parsed as a recognized object file.
+///
+/// # Platform-specific behavior
+///
+/// On Windows MSVC, the binary (e.g. `main.exe`) does not contain the symbols, but in the separate
+/// PDB file instead. Furthermore, you will need to use [`crate::llvm::llvm_pdbutil`] as `object`
+/// crate does not handle PDB files.
 #[track_caller]
-pub fn with_symbol_iter<P, F, R>(path: P, func: F) -> R
+pub fn object_contains_any_symbol_substring<P, S>(path: P, substrings: &[S]) -> bool
 where
     P: AsRef<Path>,
-    F: FnOnce(&mut SymbolIterator<'_, '_>) -> R,
+    S: AsRef<str>,
 {
     let path = path.as_ref();
     let blob = crate::fs::read(path);
-    let f = object::File::parse(&*blob)
+    let obj = object::File::parse(&*blob)
         .unwrap_or_else(|e| panic!("failed to parse `{}`: {e}", path.display()));
-    let mut iter = f.symbols();
-    func(&mut iter)
+    let substrings = substrings.iter().map(|s| s.as_ref()).collect::<Vec<_>>();
+    for sym in obj.symbols() {
+        for substring in &substrings {
+            if sym.name_bytes().unwrap().windows(substring.len()).any(|x| x == substring.as_bytes())
+            {
+                return true;
+            }
+        }
+    }
+    false
 }
 
-/// Check an object file's symbols for substrings.
+/// Check an object file's symbols for any exact matches against those provided in
+/// `candidate_symbols`.
 ///
-/// Returns `true` if any of the symbols found in the object file at `path` contain a substring
-/// listed in `substrings`.
+/// Returns `true` if **any** of the symbols found in the object file at `path` contain an **exact
+/// match** against those listed in `candidate_symbols`. Take care to account for (1) platform
+/// differences and (2) calling convention and symbol decorations differences.
 ///
 /// Panics if `path` is not a valid object file readable by the current user or if `path` cannot be
 /// parsed as a recognized object file.
+///
+/// # Platform-specific behavior
+///
+/// See [`object_contains_any_symbol_substring`].
 #[track_caller]
-pub fn any_symbol_contains(path: impl AsRef<Path>, substrings: &[&str]) -> bool {
-    with_symbol_iter(path, |syms| {
-        for sym in syms {
-            for substring in substrings {
-                if sym
-                    .name_bytes()
-                    .unwrap()
-                    .windows(substring.len())
-                    .any(|x| x == substring.as_bytes())
-                {
-                    eprintln!("{:?} contains {}", sym, substring);
-                    return true;
-                }
+pub fn object_contains_any_symbol<P, S>(path: P, candidate_symbols: &[S]) -> bool
+where
+    P: AsRef<Path>,
+    S: AsRef<str>,
+{
+    let path = path.as_ref();
+    let blob = crate::fs::read(path);
+    let obj = object::File::parse(&*blob)
+        .unwrap_or_else(|e| panic!("failed to parse `{}`: {e}", path.display()));
+    let candidate_symbols = candidate_symbols.iter().map(|s| s.as_ref()).collect::<Vec<_>>();
+    for sym in obj.symbols() {
+        for candidate_symbol in &candidate_symbols {
+            if sym.name_bytes().unwrap() == candidate_symbol.as_bytes() {
+                return true;
             }
         }
-        false
-    })
+    }
+    false
+}
+
+#[derive(Debug, PartialEq)]
+pub enum ContainsAllSymbolSubstringsOutcome<'a> {
+    Ok,
+    MissingSymbolSubstrings(BTreeSet<&'a str>),
+}
+
+/// Check an object file's symbols for presence of all of provided **substrings**. That is, if an
+/// object file contains symbols `["hello", "goodbye", "world"]`, it will be matched against a list
+/// of `substrings` of `["he", "go"]`. In this case, `he` is a substring of `hello`, and `go` is a
+/// substring of `goodbye`, so each of `substrings` was found.
+///
+/// Returns `true` if **all** `substrings` were present in the names of symbols for the given object
+/// file (as substrings of symbol names).
+///
+/// Panics if `path` is not a valid object file readable by the current user or if `path` cannot be
+/// parsed as a recognized object file.
+///
+/// # Platform-specific behavior
+///
+/// See [`object_contains_any_symbol_substring`].
+#[track_caller]
+pub fn object_contains_all_symbol_substring<'s, P, S>(
+    path: P,
+    substrings: &'s [S],
+) -> ContainsAllSymbolSubstringsOutcome<'s>
+where
+    P: AsRef<Path>,
+    S: AsRef<str>,
+{
+    let path = path.as_ref();
+    let blob = crate::fs::read(path);
+    let obj = object::File::parse(&*blob)
+        .unwrap_or_else(|e| panic!("failed to parse `{}`: {e}", path.display()));
+    let substrings = substrings.iter().map(|s| s.as_ref());
+    let mut unmatched_symbol_substrings = BTreeSet::from_iter(substrings);
+    unmatched_symbol_substrings.retain(|unmatched_symbol_substring| {
+        for sym in obj.symbols() {
+            if sym
+                .name_bytes()
+                .unwrap()
+                .windows(unmatched_symbol_substring.len())
+                .any(|x| x == unmatched_symbol_substring.as_bytes())
+            {
+                return false;
+            }
+        }
+
+        true
+    });
+
+    if unmatched_symbol_substrings.is_empty() {
+        ContainsAllSymbolSubstringsOutcome::Ok
+    } else {
+        ContainsAllSymbolSubstringsOutcome::MissingSymbolSubstrings(unmatched_symbol_substrings)
+    }
+}
+
+#[derive(Debug, PartialEq)]
+pub enum ContainsAllSymbolsOutcome<'a> {
+    Ok,
+    MissingSymbols(BTreeSet<&'a str>),
+}
+
+/// Check an object file contains all symbols provided in `candidate_symbols`.
+///
+/// Returns `true` if **all** of the symbols in `candidate_symbols` are found within the object file
+/// at `path` by **exact match**. Take care to account for (1) platform differences and (2) calling
+/// convention and symbol decorations differences.
+///
+/// Panics if `path` is not a valid object file readable by the current user or if `path` cannot be
+/// parsed as a recognized object file.
+///
+/// # Platform-specific behavior
+///
+/// See [`object_contains_any_symbol_substring`].
+#[track_caller]
+pub fn object_contains_all_symbols<P, S>(
+    path: P,
+    candidate_symbols: &[S],
+) -> ContainsAllSymbolsOutcome<'_>
+where
+    P: AsRef<Path>,
+    S: AsRef<str>,
+{
+    let path = path.as_ref();
+    let blob = crate::fs::read(path);
+    let obj = object::File::parse(&*blob)
+        .unwrap_or_else(|e| panic!("failed to parse `{}`: {e}", path.display()));
+    let candidate_symbols = candidate_symbols.iter().map(|s| s.as_ref());
+    let mut unmatched_symbols = BTreeSet::from_iter(candidate_symbols);
+    unmatched_symbols.retain(|unmatched_symbol| {
+        for sym in obj.symbols() {
+            if sym.name_bytes().unwrap() == unmatched_symbol.as_bytes() {
+                return false;
+            }
+        }
+
+        true
+    });
+
+    if unmatched_symbols.is_empty() {
+        ContainsAllSymbolsOutcome::Ok
+    } else {
+        ContainsAllSymbolsOutcome::MissingSymbols(unmatched_symbols)
+    }
 }
diff --git a/src/tools/run-make-support/src/targets.rs b/src/tools/run-make-support/src/targets.rs
index 1ab2e2ab2be..b20e12561fb 100644
--- a/src/tools/run-make-support/src/targets.rs
+++ b/src/tools/run-make-support/src/targets.rs
@@ -16,10 +16,10 @@ pub fn is_windows() -> bool {
     target().contains("windows")
 }
 
-/// Check if target uses msvc.
+/// Check if target is windows-msvc.
 #[must_use]
-pub fn is_msvc() -> bool {
-    target().contains("msvc")
+pub fn is_windows_msvc() -> bool {
+    target().ends_with("windows-msvc")
 }
 
 /// Check if target is windows-gnu.
@@ -28,12 +28,6 @@ pub fn is_windows_gnu() -> bool {
     target().ends_with("windows-gnu")
 }
 
-/// Check if target is windows-msvc.
-#[must_use]
-pub fn is_windows_msvc() -> bool {
-    target().ends_with("windows-msvc")
-}
-
 /// Check if target is win7.
 #[must_use]
 pub fn is_win7() -> bool {
diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock
index caa8f28d8e2..7432a82080d 100644
--- a/src/tools/rust-analyzer/Cargo.lock
+++ b/src/tools/rust-analyzer/Cargo.lock
@@ -570,12 +570,6 @@ dependencies = [
 ]
 
 [[package]]
-name = "heck"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
-
-[[package]]
 name = "hermit-abi"
 version = "0.5.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1021,6 +1015,15 @@ dependencies = [
 ]
 
 [[package]]
+name = "intrusive-collections"
+version = "0.9.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "189d0897e4cbe8c75efedf3502c18c887b05046e59d28404d4d8e46cbc4d1e86"
+dependencies = [
+ "memoffset",
+]
+
+[[package]]
 name = "itertools"
 version = "0.12.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1428,6 +1431,16 @@ dependencies = [
 ]
 
 [[package]]
+name = "papaya"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f92dd0b07c53a0a0c764db2ace8c541dc47320dad97c2200c2a637ab9dd2328f"
+dependencies = [
+ "equivalent",
+ "seize",
+]
+
+[[package]]
 name = "parking_lot"
 version = "0.12.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1955,16 +1968,18 @@ checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
 
 [[package]]
 name = "salsa"
-version = "0.22.0"
+version = "0.23.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8fff508e3d6ef42a32607f7538e17171a877a12015e32036f46e99d00c95781"
+checksum = "2e235afdb8e510f38a07138fbe5a0b64691894358a9c0cbd813b1aade110efc9"
 dependencies = [
  "boxcar",
  "crossbeam-queue",
- "dashmap",
+ "crossbeam-utils",
  "hashbrown 0.15.4",
  "hashlink",
  "indexmap",
+ "intrusive-collections",
+ "papaya",
  "parking_lot",
  "portable-atomic",
  "rayon",
@@ -1978,17 +1993,16 @@ dependencies = [
 
 [[package]]
 name = "salsa-macro-rules"
-version = "0.22.0"
+version = "0.23.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8ea72b3c06f2ce6350fe3a0eeb7aaaf842d1d8352b706973c19c4f02e298a87c"
+checksum = "2edb86a7e9c91f6d30c9ce054312721dbe773a162db27bbfae834d16177b30ce"
 
 [[package]]
 name = "salsa-macros"
-version = "0.22.0"
+version = "0.23.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0ce92025bc160b27814a207cb78d680973af17f863c7f4fc56cf3a535e22f378"
+checksum = "d0778d6e209051bc4e75acfe83bcd7848601ec3dbe9c3dbb982829020e9128af"
 dependencies = [
- "heck",
  "proc-macro2",
  "quote",
  "syn",
@@ -2026,6 +2040,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
 
 [[package]]
+name = "seize"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e4b8d813387d566f627f3ea1b914c068aac94c40ae27ec43f5f33bde65abefe7"
+dependencies = [
+ "libc",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
 name = "semver"
 version = "1.0.26"
 source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml
index 0a8e6feb46e..d268ce5b0bb 100644
--- a/src/tools/rust-analyzer/Cargo.toml
+++ b/src/tools/rust-analyzer/Cargo.toml
@@ -49,6 +49,8 @@ debug = 2
 # ungrammar = { path = "../ungrammar" }
 
 # salsa = { path = "../salsa" }
+# salsa-macros = { path = "../salsa/components/salsa-macros" }
+# salsa-macro-rules = { path = "../salsa/components/salsa-macro-rules" }
 
 [workspace.dependencies]
 # local crates
@@ -136,8 +138,8 @@ rayon = "1.10.0"
 rowan = "=0.15.15"
 # Ideally we'd not enable the macros feature but unfortunately the `tracked` attribute does not work
 # on impls without it
-salsa = { version = "0.22.0", default-features = true, features = ["rayon","salsa_unstable", "macros"] }
-salsa-macros = "0.22.0"
+salsa = { version = "0.23.0", default-features = true, features = ["rayon","salsa_unstable", "macros"] }
+salsa-macros = "0.23.0"
 semver = "1.0.26"
 serde = { version = "1.0.219" }
 serde_derive = { version = "1.0.219" }
diff --git a/src/tools/rust-analyzer/crates/base-db/src/input.rs b/src/tools/rust-analyzer/crates/base-db/src/input.rs
index 2a87b152487..8c9393bcc93 100644
--- a/src/tools/rust-analyzer/crates/base-db/src/input.rs
+++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs
@@ -6,6 +6,7 @@
 //! actual IO. See `vfs` and `project_model` in the `rust-analyzer` crate for how
 //! actual IO is done and lowered to input.
 
+use std::error::Error;
 use std::hash::BuildHasherDefault;
 use std::{fmt, mem, ops};
 
@@ -22,7 +23,49 @@ use vfs::{AbsPathBuf, AnchoredPath, FileId, VfsPath, file_set::FileSet};
 
 use crate::{CrateWorkspaceData, EditionedFileId, FxIndexSet, RootQueryDb};
 
-pub type ProcMacroPaths = FxHashMap<CrateBuilderId, Result<(String, AbsPathBuf), String>>;
+pub type ProcMacroPaths =
+    FxHashMap<CrateBuilderId, Result<(String, AbsPathBuf), ProcMacroLoadingError>>;
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub enum ProcMacroLoadingError {
+    Disabled,
+    FailedToBuild,
+    MissingDylibPath,
+    NotYetBuilt,
+    NoProcMacros,
+    ProcMacroSrvError(Box<str>),
+}
+impl ProcMacroLoadingError {
+    pub fn is_hard_error(&self) -> bool {
+        match self {
+            ProcMacroLoadingError::Disabled | ProcMacroLoadingError::NotYetBuilt => false,
+            ProcMacroLoadingError::FailedToBuild
+            | ProcMacroLoadingError::MissingDylibPath
+            | ProcMacroLoadingError::NoProcMacros
+            | ProcMacroLoadingError::ProcMacroSrvError(_) => true,
+        }
+    }
+}
+
+impl Error for ProcMacroLoadingError {}
+impl fmt::Display for ProcMacroLoadingError {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        match self {
+            ProcMacroLoadingError::Disabled => write!(f, "proc-macro expansion is disabled"),
+            ProcMacroLoadingError::FailedToBuild => write!(f, "proc-macro failed to build"),
+            ProcMacroLoadingError::MissingDylibPath => {
+                write!(f, "proc-macro crate build data is missing a dylib path")
+            }
+            ProcMacroLoadingError::NotYetBuilt => write!(f, "proc-macro not yet built"),
+            ProcMacroLoadingError::NoProcMacros => {
+                write!(f, "proc macro library has no proc macros")
+            }
+            ProcMacroLoadingError::ProcMacroSrvError(msg) => {
+                write!(f, "proc macro server error: {msg}")
+            }
+        }
+    }
+}
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
 pub struct SourceRootId(pub u32);
diff --git a/src/tools/rust-analyzer/crates/base-db/src/lib.rs b/src/tools/rust-analyzer/crates/base-db/src/lib.rs
index 478fae67c80..ad17f1730be 100644
--- a/src/tools/rust-analyzer/crates/base-db/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/base-db/src/lib.rs
@@ -14,8 +14,9 @@ pub use crate::{
     input::{
         BuiltCrateData, BuiltDependency, Crate, CrateBuilder, CrateBuilderId, CrateDataBuilder,
         CrateDisplayName, CrateGraphBuilder, CrateName, CrateOrigin, CratesIdMap, CratesMap,
-        DependencyBuilder, Env, ExtraCrateData, LangCrateOrigin, ProcMacroPaths, ReleaseChannel,
-        SourceRoot, SourceRootId, TargetLayoutLoadResult, UniqueCrateData,
+        DependencyBuilder, Env, ExtraCrateData, LangCrateOrigin, ProcMacroLoadingError,
+        ProcMacroPaths, ReleaseChannel, SourceRoot, SourceRootId, TargetLayoutLoadResult,
+        UniqueCrateData,
     },
 };
 use dashmap::{DashMap, mapref::entry::Entry};
@@ -33,7 +34,7 @@ pub type FxIndexSet<T> = indexmap::IndexSet<T, rustc_hash::FxBuildHasher>;
 #[macro_export]
 macro_rules! impl_intern_key {
     ($id:ident, $loc:ident) => {
-        #[salsa_macros::interned(no_lifetime)]
+        #[salsa_macros::interned(no_lifetime, revisions = usize::MAX)]
         #[derive(PartialOrd, Ord)]
         pub struct $id {
             pub loc: $loc,
@@ -43,7 +44,7 @@ macro_rules! impl_intern_key {
         impl ::std::fmt::Debug for $id {
             fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
                 f.debug_tuple(stringify!($id))
-                    .field(&format_args!("{:04x}", self.0.as_u32()))
+                    .field(&format_args!("{:04x}", self.0.index()))
                     .finish()
             }
         }
@@ -167,7 +168,7 @@ impl Files {
     }
 }
 
-#[salsa_macros::interned(no_lifetime, debug, constructor=from_span)]
+#[salsa_macros::interned(no_lifetime, debug, constructor=from_span, revisions = usize::MAX)]
 #[derive(PartialOrd, Ord)]
 pub struct EditionedFileId {
     pub editioned_file_id: span::EditionedFileId,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs
index 85bd193223f..51612f341a1 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs
@@ -93,7 +93,7 @@ pub type TypeSource = InFile<TypePtr>;
 pub type LifetimePtr = AstPtr<ast::Lifetime>;
 pub type LifetimeSource = InFile<LifetimePtr>;
 
-#[derive(Debug, Eq, PartialEq)]
+#[derive(Debug, PartialEq, Eq)]
 pub struct ExpressionStore {
     pub exprs: Arena<Expr>,
     pub pats: Arena<Pat>,
@@ -114,7 +114,7 @@ pub struct ExpressionStore {
     ident_hygiene: FxHashMap<ExprOrPatId, HygieneId>,
 }
 
-#[derive(Debug, Eq, PartialEq, Default)]
+#[derive(Debug, Eq, Default)]
 pub struct ExpressionStoreSourceMap {
     // AST expressions can create patterns in destructuring assignments. Therefore, `ExprSource` can also map
     // to `PatId`, and `PatId` can also map to `ExprSource` (the other way around is unaffected).
@@ -127,19 +127,20 @@ pub struct ExpressionStoreSourceMap {
     label_map: FxHashMap<LabelSource, LabelId>,
     label_map_back: ArenaMap<LabelId, LabelSource>,
 
-    binding_definitions: FxHashMap<BindingId, SmallVec<[PatId; 4]>>,
-
-    /// We don't create explicit nodes for record fields (`S { record_field: 92 }`).
-    /// Instead, we use id of expression (`92`) to identify the field.
-    field_map_back: FxHashMap<ExprId, FieldSource>,
-    pat_field_map_back: FxHashMap<PatId, PatFieldSource>,
-
     types_map_back: ArenaMap<TypeRefId, TypeSource>,
     types_map: FxHashMap<TypeSource, TypeRefId>,
 
     lifetime_map_back: ArenaMap<LifetimeRefId, LifetimeSource>,
     lifetime_map: FxHashMap<LifetimeSource, LifetimeRefId>,
 
+    binding_definitions:
+        ArenaMap<BindingId, SmallVec<[PatId; 2 * size_of::<usize>() / size_of::<PatId>()]>>,
+
+    /// We don't create explicit nodes for record fields (`S { record_field: 92 }`).
+    /// Instead, we use id of expression (`92`) to identify the field.
+    field_map_back: FxHashMap<ExprId, FieldSource>,
+    pat_field_map_back: FxHashMap<PatId, PatFieldSource>,
+
     template_map: Option<Box<FormatTemplate>>,
 
     pub expansions: FxHashMap<InFile<MacroCallPtr>, MacroCallId>,
@@ -149,6 +150,43 @@ pub struct ExpressionStoreSourceMap {
     pub diagnostics: Vec<ExpressionStoreDiagnostics>,
 }
 
+impl PartialEq for ExpressionStoreSourceMap {
+    fn eq(&self, other: &Self) -> bool {
+        // we only need to compare one of the two mappings
+        // as the other is a reverse mapping and thus will compare
+        // the same as normal mapping
+        let Self {
+            expr_map: _,
+            expr_map_back,
+            pat_map: _,
+            pat_map_back,
+            label_map: _,
+            label_map_back,
+            types_map_back,
+            types_map: _,
+            lifetime_map_back,
+            lifetime_map: _,
+            // If this changed, our pattern data must have changed
+            binding_definitions: _,
+            // If this changed, our expression data must have changed
+            field_map_back: _,
+            // If this changed, our pattern data must have changed
+            pat_field_map_back: _,
+            template_map,
+            expansions,
+            diagnostics,
+        } = self;
+        *expr_map_back == other.expr_map_back
+            && *pat_map_back == other.pat_map_back
+            && *label_map_back == other.label_map_back
+            && *types_map_back == other.types_map_back
+            && *lifetime_map_back == other.lifetime_map_back
+            && *template_map == other.template_map
+            && *expansions == other.expansions
+            && *diagnostics == other.diagnostics
+    }
+}
+
 /// The body of an item (function, const etc.).
 #[derive(Debug, Eq, PartialEq, Default)]
 pub struct ExpressionStoreBuilder {
@@ -698,7 +736,7 @@ impl ExpressionStoreSourceMap {
     }
 
     pub fn patterns_for_binding(&self, binding: BindingId) -> &[PatId] {
-        self.binding_definitions.get(&binding).map_or(&[], Deref::deref)
+        self.binding_definitions.get(binding).map_or(&[], Deref::deref)
     }
 
     pub fn node_label(&self, node: InFile<&ast::Label>) -> Option<LabelId> {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/path.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/path.rs
index db83e73a0b9..19c7ce0ce04 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/path.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/path.rs
@@ -29,8 +29,8 @@ pub enum Path {
 // This type is being used a lot, make sure it doesn't grow unintentionally.
 #[cfg(target_arch = "x86_64")]
 const _: () = {
-    assert!(size_of::<Path>() == 16);
-    assert!(size_of::<Option<Path>>() == 16);
+    assert!(size_of::<Path>() == 24);
+    assert!(size_of::<Option<Path>>() == 24);
 };
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs
index a46711c67e8..2dd0b9bdb86 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs
@@ -535,7 +535,7 @@ fn foo() {
 
         let resolved = scopes.resolve_name_in_scope(expr_scope, &name_ref.as_name()).unwrap();
         let pat_src = source_map
-            .pat_syntax(*source_map.binding_definitions[&resolved.binding()].first().unwrap())
+            .pat_syntax(*source_map.binding_definitions[resolved.binding()].first().unwrap())
             .unwrap();
 
         let local_name = pat_src.value.syntax_node_ptr().to_node(file.syntax());
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs
index eb3b92d31f1..eacc3f3cedf 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/hir/type_ref.rs
@@ -149,7 +149,7 @@ pub enum TypeRef {
 }
 
 #[cfg(target_arch = "x86_64")]
-const _: () = assert!(size_of::<TypeRef>() == 16);
+const _: () = assert!(size_of::<TypeRef>() == 24);
 
 pub type TypeRefId = Idx<TypeRef>;
 
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs
index 777953d3f21..0013c2a2567 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_derive_macro.rs
@@ -746,3 +746,83 @@ struct Struct9<#[pointee] T, U>(T) where T: ?Sized;
             623..690: `derive(CoercePointee)` requires `T` to be marked `?Sized`"#]],
     );
 }
+
+#[test]
+fn union_derive() {
+    check_errors(
+        r#"
+//- minicore: clone, copy, default, fmt, hash, ord, eq, derive
+
+#[derive(Copy)]
+union Foo1 { _v: () }
+#[derive(Clone)]
+union Foo2 { _v: () }
+#[derive(Default)]
+union Foo3 { _v: () }
+#[derive(Debug)]
+union Foo4 { _v: () }
+#[derive(Hash)]
+union Foo5 { _v: () }
+#[derive(Ord)]
+union Foo6 { _v: () }
+#[derive(PartialOrd)]
+union Foo7 { _v: () }
+#[derive(Eq)]
+union Foo8 { _v: () }
+#[derive(PartialEq)]
+union Foo9 { _v: () }
+    "#,
+        expect![[r#"
+            78..118: this trait cannot be derived for unions
+            119..157: this trait cannot be derived for unions
+            158..195: this trait cannot be derived for unions
+            196..232: this trait cannot be derived for unions
+            233..276: this trait cannot be derived for unions
+            313..355: this trait cannot be derived for unions"#]],
+    );
+}
+
+#[test]
+fn default_enum_without_default_attr() {
+    check_errors(
+        r#"
+//- minicore: default, derive
+
+#[derive(Default)]
+enum Foo {
+    Bar,
+}
+    "#,
+        expect!["1..41: `#[derive(Default)]` on enum with no `#[default]`"],
+    );
+}
+
+#[test]
+fn generic_enum_default() {
+    check(
+        r#"
+//- minicore: default, derive
+
+#[derive(Default)]
+enum Foo<T> {
+    Bar(T),
+    #[default]
+    Baz,
+}
+"#,
+        expect![[r#"
+
+#[derive(Default)]
+enum Foo<T> {
+    Bar(T),
+    #[default]
+    Baz,
+}
+
+impl <T, > $crate::default::Default for Foo<T, > where {
+    fn default() -> Self {
+        Foo::Baz
+    }
+}"#]],
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs
index ba75dca3d3d..338851b715b 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/incremental.rs
@@ -172,7 +172,7 @@ fn no() {}
                 "ast_id_map_shim",
                 "parse_shim",
                 "real_span_map_shim",
-                "of_",
+                "EnumVariants::of_",
             ]
         "#]],
         expect![[r#"
@@ -181,7 +181,7 @@ fn no() {}
                 "ast_id_map_shim",
                 "file_item_tree_query",
                 "real_span_map_shim",
-                "of_",
+                "EnumVariants::of_",
             ]
         "#]],
     );
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs
index d135584a080..15e68ff95cd 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/derive_macro.rs
@@ -458,6 +458,7 @@ fn expand_simple_derive(
     invoc_span: Span,
     tt: &tt::TopSubtree,
     trait_path: tt::TopSubtree,
+    allow_unions: bool,
     make_trait_body: impl FnOnce(&BasicAdtInfo) -> tt::TopSubtree,
 ) -> ExpandResult<tt::TopSubtree> {
     let info = match parse_adt(db, tt, invoc_span) {
@@ -469,6 +470,12 @@ fn expand_simple_derive(
             );
         }
     };
+    if !allow_unions && matches!(info.shape, AdtShape::Union) {
+        return ExpandResult::new(
+            tt::TopSubtree::empty(tt::DelimSpan::from_single(invoc_span)),
+            ExpandError::other(invoc_span, "this trait cannot be derived for unions"),
+        );
+    }
     ExpandResult::ok(expand_simple_derive_with_parsed(
         invoc_span,
         info,
@@ -535,7 +542,14 @@ fn copy_expand(
     tt: &tt::TopSubtree,
 ) -> ExpandResult<tt::TopSubtree> {
     let krate = dollar_crate(span);
-    expand_simple_derive(db, span, tt, quote! {span => #krate::marker::Copy }, |_| quote! {span =>})
+    expand_simple_derive(
+        db,
+        span,
+        tt,
+        quote! {span => #krate::marker::Copy },
+        true,
+        |_| quote! {span =>},
+    )
 }
 
 fn clone_expand(
@@ -544,7 +558,7 @@ fn clone_expand(
     tt: &tt::TopSubtree,
 ) -> ExpandResult<tt::TopSubtree> {
     let krate = dollar_crate(span);
-    expand_simple_derive(db, span, tt, quote! {span => #krate::clone::Clone }, |adt| {
+    expand_simple_derive(db, span, tt, quote! {span => #krate::clone::Clone }, true, |adt| {
         if matches!(adt.shape, AdtShape::Union) {
             let star = tt::Punct { char: '*', spacing: ::tt::Spacing::Alone, span };
             return quote! {span =>
@@ -599,41 +613,63 @@ fn default_expand(
     tt: &tt::TopSubtree,
 ) -> ExpandResult<tt::TopSubtree> {
     let krate = &dollar_crate(span);
-    expand_simple_derive(db, span, tt, quote! {span => #krate::default::Default }, |adt| {
-        let body = match &adt.shape {
-            AdtShape::Struct(fields) => {
-                let name = &adt.name;
-                fields.as_pattern_map(
-                    quote!(span =>#name),
+    let adt = match parse_adt(db, tt, span) {
+        Ok(info) => info,
+        Err(e) => {
+            return ExpandResult::new(
+                tt::TopSubtree::empty(tt::DelimSpan { open: span, close: span }),
+                e,
+            );
+        }
+    };
+    let (body, constrain_to_trait) = match &adt.shape {
+        AdtShape::Struct(fields) => {
+            let name = &adt.name;
+            let body = fields.as_pattern_map(
+                quote!(span =>#name),
+                span,
+                |_| quote!(span =>#krate::default::Default::default()),
+            );
+            (body, true)
+        }
+        AdtShape::Enum { default_variant, variants } => {
+            if let Some(d) = default_variant {
+                let (name, fields) = &variants[*d];
+                let adt_name = &adt.name;
+                let body = fields.as_pattern_map(
+                    quote!(span =>#adt_name :: #name),
                     span,
                     |_| quote!(span =>#krate::default::Default::default()),
-                )
+                );
+                (body, false)
+            } else {
+                return ExpandResult::new(
+                    tt::TopSubtree::empty(tt::DelimSpan::from_single(span)),
+                    ExpandError::other(span, "`#[derive(Default)]` on enum with no `#[default]`"),
+                );
             }
-            AdtShape::Enum { default_variant, variants } => {
-                if let Some(d) = default_variant {
-                    let (name, fields) = &variants[*d];
-                    let adt_name = &adt.name;
-                    fields.as_pattern_map(
-                        quote!(span =>#adt_name :: #name),
-                        span,
-                        |_| quote!(span =>#krate::default::Default::default()),
-                    )
-                } else {
-                    // FIXME: Return expand error here
-                    quote!(span =>)
+        }
+        AdtShape::Union => {
+            return ExpandResult::new(
+                tt::TopSubtree::empty(tt::DelimSpan::from_single(span)),
+                ExpandError::other(span, "this trait cannot be derived for unions"),
+            );
+        }
+    };
+    ExpandResult::ok(expand_simple_derive_with_parsed(
+        span,
+        adt,
+        quote! {span => #krate::default::Default },
+        |_adt| {
+            quote! {span =>
+                fn default() -> Self {
+                    #body
                 }
             }
-            AdtShape::Union => {
-                // FIXME: Return expand error here
-                quote!(span =>)
-            }
-        };
-        quote! {span =>
-            fn default() -> Self {
-                #body
-            }
-        }
-    })
+        },
+        constrain_to_trait,
+        tt::TopSubtree::empty(tt::DelimSpan::from_single(span)),
+    ))
 }
 
 fn debug_expand(
@@ -642,7 +678,7 @@ fn debug_expand(
     tt: &tt::TopSubtree,
 ) -> ExpandResult<tt::TopSubtree> {
     let krate = &dollar_crate(span);
-    expand_simple_derive(db, span, tt, quote! {span => #krate::fmt::Debug }, |adt| {
+    expand_simple_derive(db, span, tt, quote! {span => #krate::fmt::Debug }, false, |adt| {
         let for_variant = |name: String, v: &VariantShape| match v {
             VariantShape::Struct(fields) => {
                 let for_fields = fields.iter().map(|it| {
@@ -697,10 +733,7 @@ fn debug_expand(
                     }
                 })
                 .collect(),
-            AdtShape::Union => {
-                // FIXME: Return expand error here
-                vec![]
-            }
+            AdtShape::Union => unreachable!(),
         };
         quote! {span =>
             fn fmt(&self, f: &mut #krate::fmt::Formatter) -> #krate::fmt::Result {
@@ -718,11 +751,7 @@ fn hash_expand(
     tt: &tt::TopSubtree,
 ) -> ExpandResult<tt::TopSubtree> {
     let krate = &dollar_crate(span);
-    expand_simple_derive(db, span, tt, quote! {span => #krate::hash::Hash }, |adt| {
-        if matches!(adt.shape, AdtShape::Union) {
-            // FIXME: Return expand error here
-            return quote! {span =>};
-        }
+    expand_simple_derive(db, span, tt, quote! {span => #krate::hash::Hash }, false, |adt| {
         if matches!(&adt.shape, AdtShape::Enum { variants, .. } if variants.is_empty()) {
             let star = tt::Punct { char: '*', spacing: ::tt::Spacing::Alone, span };
             return quote! {span =>
@@ -769,7 +798,14 @@ fn eq_expand(
     tt: &tt::TopSubtree,
 ) -> ExpandResult<tt::TopSubtree> {
     let krate = dollar_crate(span);
-    expand_simple_derive(db, span, tt, quote! {span => #krate::cmp::Eq }, |_| quote! {span =>})
+    expand_simple_derive(
+        db,
+        span,
+        tt,
+        quote! {span => #krate::cmp::Eq },
+        true,
+        |_| quote! {span =>},
+    )
 }
 
 fn partial_eq_expand(
@@ -778,11 +814,7 @@ fn partial_eq_expand(
     tt: &tt::TopSubtree,
 ) -> ExpandResult<tt::TopSubtree> {
     let krate = dollar_crate(span);
-    expand_simple_derive(db, span, tt, quote! {span => #krate::cmp::PartialEq }, |adt| {
-        if matches!(adt.shape, AdtShape::Union) {
-            // FIXME: Return expand error here
-            return quote! {span =>};
-        }
+    expand_simple_derive(db, span, tt, quote! {span => #krate::cmp::PartialEq }, false, |adt| {
         let name = &adt.name;
 
         let (self_patterns, other_patterns) = self_and_other_patterns(adt, name, span);
@@ -854,7 +886,7 @@ fn ord_expand(
     tt: &tt::TopSubtree,
 ) -> ExpandResult<tt::TopSubtree> {
     let krate = &dollar_crate(span);
-    expand_simple_derive(db, span, tt, quote! {span => #krate::cmp::Ord }, |adt| {
+    expand_simple_derive(db, span, tt, quote! {span => #krate::cmp::Ord }, false, |adt| {
         fn compare(
             krate: &tt::Ident,
             left: tt::TopSubtree,
@@ -873,10 +905,6 @@ fn ord_expand(
                 }
             }
         }
-        if matches!(adt.shape, AdtShape::Union) {
-            // FIXME: Return expand error here
-            return quote!(span =>);
-        }
         let (self_patterns, other_patterns) = self_and_other_patterns(adt, &adt.name, span);
         let arms = izip!(self_patterns, other_patterns, adt.shape.field_names(span)).map(
             |(pat1, pat2, fields)| {
@@ -916,7 +944,7 @@ fn partial_ord_expand(
     tt: &tt::TopSubtree,
 ) -> ExpandResult<tt::TopSubtree> {
     let krate = &dollar_crate(span);
-    expand_simple_derive(db, span, tt, quote! {span => #krate::cmp::PartialOrd }, |adt| {
+    expand_simple_derive(db, span, tt, quote! {span => #krate::cmp::PartialOrd }, false, |adt| {
         fn compare(
             krate: &tt::Ident,
             left: tt::TopSubtree,
@@ -935,10 +963,6 @@ fn partial_ord_expand(
                 }
             }
         }
-        if matches!(adt.shape, AdtShape::Union) {
-            // FIXME: Return expand error here
-            return quote!(span =>);
-        }
         let left = quote!(span =>#krate::intrinsics::discriminant_value(self));
         let right = quote!(span =>#krate::intrinsics::discriminant_value(other));
 
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs
index f9abe4f5566..800b40a9e7e 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs
@@ -7,6 +7,7 @@ use intern::{
     Symbol,
     sym::{self},
 };
+use itertools::Itertools;
 use mbe::{DelimiterKind, expect_fragment};
 use span::{Edition, FileId, Span};
 use stdx::format_to;
@@ -681,11 +682,19 @@ fn relative_file(
 }
 
 fn parse_string(tt: &tt::TopSubtree) -> Result<(Symbol, Span), ExpandError> {
-    let delimiter = tt.top_subtree().delimiter;
-    tt.iter()
-        .next()
-        .ok_or(delimiter.open.cover(delimiter.close))
-        .and_then(|tt| match tt {
+    let mut tt = TtElement::Subtree(tt.top_subtree(), tt.iter());
+    (|| {
+        // FIXME: We wrap expression fragments in parentheses which can break this expectation
+        // here
+        // Remove this once we handle none delims correctly
+        while let TtElement::Subtree(sub, tt_iter) = &mut tt
+            && let DelimiterKind::Parenthesis | DelimiterKind::Invisible = sub.delimiter.kind
+        {
+            tt =
+                tt_iter.exactly_one().map_err(|_| sub.delimiter.open.cover(sub.delimiter.close))?;
+        }
+
+        match tt {
             TtElement::Leaf(tt::Leaf::Literal(tt::Literal {
                 symbol: text,
                 span,
@@ -698,35 +707,11 @@ fn parse_string(tt: &tt::TopSubtree) -> Result<(Symbol, Span), ExpandError> {
                 kind: tt::LitKind::StrRaw(_),
                 suffix: _,
             })) => Ok((text.clone(), *span)),
-            // FIXME: We wrap expression fragments in parentheses which can break this expectation
-            // here
-            // Remove this once we handle none delims correctly
-            TtElement::Subtree(tt, mut tt_iter)
-                if tt.delimiter.kind == DelimiterKind::Parenthesis =>
-            {
-                tt_iter
-                    .next()
-                    .and_then(|tt| match tt {
-                        TtElement::Leaf(tt::Leaf::Literal(tt::Literal {
-                            symbol: text,
-                            span,
-                            kind: tt::LitKind::Str,
-                            suffix: _,
-                        })) => Some((unescape_symbol(text), *span)),
-                        TtElement::Leaf(tt::Leaf::Literal(tt::Literal {
-                            symbol: text,
-                            span,
-                            kind: tt::LitKind::StrRaw(_),
-                            suffix: _,
-                        })) => Some((text.clone(), *span)),
-                        _ => None,
-                    })
-                    .ok_or(delimiter.open.cover(delimiter.close))
-            }
             TtElement::Leaf(l) => Err(*l.span()),
             TtElement::Subtree(tt, _) => Err(tt.delimiter.open.cover(tt.delimiter.close)),
-        })
-        .map_err(|span| ExpandError::other(span, "expected string literal"))
+        }
+    })()
+    .map_err(|span| ExpandError::other(span, "expected string literal"))
 }
 
 fn include_expand(
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
index 7e9928c41ff..888c1405a6b 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
@@ -145,7 +145,7 @@ pub trait ExpandDatabase: RootQueryDb {
     fn syntax_context(&self, file: HirFileId, edition: Edition) -> SyntaxContext;
 }
 
-#[salsa_macros::interned(no_lifetime, id = span::SyntaxContext)]
+#[salsa_macros::interned(no_lifetime, id = span::SyntaxContext, revisions = usize::MAX)]
 pub struct SyntaxContextWrapper {
     pub data: SyntaxContext,
 }
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/files.rs b/src/tools/rust-analyzer/crates/hir-expand/src/files.rs
index a73a22370d2..6730b337d35 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/files.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/files.rs
@@ -315,11 +315,11 @@ impl<SN: Borrow<SyntaxNode>> InFile<SN> {
     }
 
     /// Falls back to the macro call range if the node cannot be mapped up fully.
-    pub fn original_file_range_with_macro_call_body(
+    pub fn original_file_range_with_macro_call_input(
         self,
         db: &dyn db::ExpandDatabase,
     ) -> FileRange {
-        self.borrow().map(SyntaxNode::text_range).original_node_file_range_with_macro_call_body(db)
+        self.borrow().map(SyntaxNode::text_range).original_node_file_range_with_macro_call_input(db)
     }
 
     pub fn original_syntax_node_rooted(
@@ -465,7 +465,7 @@ impl InFile<TextRange> {
         }
     }
 
-    pub fn original_node_file_range_with_macro_call_body(
+    pub fn original_node_file_range_with_macro_call_input(
         self,
         db: &dyn db::ExpandDatabase,
     ) -> FileRange {
@@ -476,7 +476,7 @@ impl InFile<TextRange> {
                     Some(it) => it,
                     _ => {
                         let loc = db.lookup_intern_macro_call(mac_file);
-                        loc.kind.original_call_range_with_body(db)
+                        loc.kind.original_call_range_with_input(db)
                     }
                 }
             }
@@ -497,6 +497,18 @@ impl InFile<TextRange> {
             }
         }
     }
+
+    pub fn original_node_file_range_rooted_opt(
+        self,
+        db: &dyn db::ExpandDatabase,
+    ) -> Option<FileRange> {
+        match self.file_id {
+            HirFileId::FileId(file_id) => Some(FileRange { file_id, range: self.value }),
+            HirFileId::MacroFile(mac_file) => {
+                map_node_range_up_rooted(db, &db.expansion_span_map(mac_file), self.value)
+            }
+        }
+    }
 }
 
 impl<N: AstNode> InFile<N> {
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
index 6ecac1463f5..ac61b220097 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
@@ -199,9 +199,9 @@ impl ExpandErrorKind {
             },
             &ExpandErrorKind::MissingProcMacroExpander(def_crate) => {
                 match db.proc_macros_for_crate(def_crate).as_ref().and_then(|it| it.get_error()) {
-                    Some((e, hard_err)) => RenderedExpandError {
-                        message: e.to_owned(),
-                        error: hard_err,
+                    Some(e) => RenderedExpandError {
+                        message: e.to_string(),
+                        error: e.is_hard_error(),
                         kind: RenderedExpandError::GENERAL_KIND,
                     },
                     None => RenderedExpandError {
@@ -688,8 +688,11 @@ impl MacroCallKind {
 
     /// Returns the original file range that best describes the location of this macro call.
     ///
-    /// Unlike `MacroCallKind::original_call_range`, this also spans the item of attributes and derives.
-    pub fn original_call_range_with_body(self, db: &dyn ExpandDatabase) -> FileRange {
+    /// This spans the entire macro call, including its input. That is for
+    /// - fn_like! {}, it spans the path and token tree
+    /// - #\[derive], it spans the `#[derive(...)]` attribute and the annotated item
+    /// - #\[attr], it spans the `#[attr(...)]` attribute and the annotated item
+    pub fn original_call_range_with_input(self, db: &dyn ExpandDatabase) -> FileRange {
         let mut kind = self;
         let file_id = loop {
             match kind.file_id() {
@@ -712,8 +715,8 @@ impl MacroCallKind {
     /// Returns the original file range that best describes the location of this macro call.
     ///
     /// Here we try to roughly match what rustc does to improve diagnostics: fn-like macros
-    /// get the whole `ast::MacroCall`, attribute macros get the attribute's range, and derives
-    /// get only the specific derive that is being referred to.
+    /// get the macro path (rustc shows the whole `ast::MacroCall`), attribute macros get the
+    /// attribute's range, and derives get only the specific derive that is being referred to.
     pub fn original_call_range(self, db: &dyn ExpandDatabase) -> FileRange {
         let mut kind = self;
         let file_id = loop {
@@ -726,7 +729,14 @@ impl MacroCallKind {
         };
 
         let range = match kind {
-            MacroCallKind::FnLike { ast_id, .. } => ast_id.to_ptr(db).text_range(),
+            MacroCallKind::FnLike { ast_id, .. } => {
+                let node = ast_id.to_node(db);
+                node.path()
+                    .unwrap()
+                    .syntax()
+                    .text_range()
+                    .cover(node.excl_token().unwrap().text_range())
+            }
             MacroCallKind::Derive { ast_id, derive_attr_index, .. } => {
                 // FIXME: should be the range of the macro name, not the whole derive
                 // FIXME: handle `cfg_attr`
@@ -1056,7 +1066,7 @@ impl ExpandTo {
 
 intern::impl_internable!(ModPath, attrs::AttrInput);
 
-#[salsa_macros::interned(no_lifetime, debug)]
+#[salsa_macros::interned(no_lifetime, debug, revisions = usize::MAX)]
 #[doc(alias = "MacroFileId")]
 pub struct MacroCallId {
     pub loc: MacroCallLoc,
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
index 217d991d110..679f61112ad 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
@@ -179,9 +179,10 @@ impl Name {
         self.symbol.as_str()
     }
 
+    #[inline]
     pub fn display<'a>(
         &'a self,
-        db: &dyn crate::db::ExpandDatabase,
+        db: &dyn salsa::Database,
         edition: Edition,
     ) -> impl fmt::Display + 'a {
         _ = db;
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs b/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs
index 6134c3a36b9..6431d46d39e 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/prettify_macro_expansion_.rs
@@ -46,7 +46,7 @@ pub fn prettify_macro_expansion(
                     } else if let Some(crate_name) = &macro_def_crate.extra_data(db).display_name {
                         make::tokens::ident(crate_name.crate_name().as_str())
                     } else {
-                        return dollar_crate.clone();
+                        dollar_crate.clone()
                     }
                 });
             if replacement.text() == "$crate" {
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs
index 1c8ebb6f535..f97d721dfa8 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/proc_macro.rs
@@ -4,7 +4,7 @@ use core::fmt;
 use std::any::Any;
 use std::{panic::RefUnwindSafe, sync};
 
-use base_db::{Crate, CrateBuilderId, CratesIdMap, Env};
+use base_db::{Crate, CrateBuilderId, CratesIdMap, Env, ProcMacroLoadingError};
 use intern::Symbol;
 use rustc_hash::FxHashMap;
 use span::Span;
@@ -53,8 +53,8 @@ pub enum ProcMacroExpansionError {
     System(String),
 }
 
-pub type ProcMacroLoadResult = Result<Vec<ProcMacro>, (String, bool)>;
-type StoredProcMacroLoadResult = Result<Box<[ProcMacro]>, (Box<str>, bool)>;
+pub type ProcMacroLoadResult = Result<Vec<ProcMacro>, ProcMacroLoadingError>;
+type StoredProcMacroLoadResult = Result<Box<[ProcMacro]>, ProcMacroLoadingError>;
 
 #[derive(Default, Debug)]
 pub struct ProcMacrosBuilder(FxHashMap<CrateBuilderId, Arc<CrateProcMacros>>);
@@ -77,9 +77,7 @@ impl ProcMacrosBuilder {
             proc_macros_crate,
             match proc_macro {
                 Ok(it) => Arc::new(CrateProcMacros(Ok(it.into_boxed_slice()))),
-                Err((e, hard_err)) => {
-                    Arc::new(CrateProcMacros(Err((e.into_boxed_str(), hard_err))))
-                }
+                Err(e) => Arc::new(CrateProcMacros(Err(e))),
             },
         );
     }
@@ -139,8 +137,8 @@ impl CrateProcMacros {
         )
     }
 
-    pub fn get_error(&self) -> Option<(&str, bool)> {
-        self.0.as_ref().err().map(|(e, hard_err)| (&**e, *hard_err))
+    pub fn get_error(&self) -> Option<&ProcMacroLoadingError> {
+        self.0.as_ref().err()
     }
 
     /// Fetch the [`CustomProcMacroExpander`]s and their corresponding names for the given crate.
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
index 1029969992c..5d3be07f3db 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
@@ -237,15 +237,6 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug {
 
     // Interned IDs for Chalk integration
     #[salsa::interned]
-    fn intern_type_or_const_param_id(
-        &self,
-        param_id: TypeOrConstParamId,
-    ) -> InternedTypeOrConstParamId;
-
-    #[salsa::interned]
-    fn intern_lifetime_param_id(&self, param_id: LifetimeParamId) -> InternedLifetimeParamId;
-
-    #[salsa::interned]
     fn intern_impl_trait_id(&self, id: ImplTraitId) -> InternedOpaqueTyId;
 
     #[salsa::interned]
@@ -282,9 +273,8 @@ pub trait HirDatabase: DefDatabase + std::fmt::Debug {
 
     #[salsa::invoke(crate::variance::variances_of)]
     #[salsa::cycle(
-        // cycle_fn = crate::variance::variances_of_cycle_fn,
-        // cycle_initial = crate::variance::variances_of_cycle_initial,
-        cycle_result = crate::variance::variances_of_cycle_initial,
+        cycle_fn = crate::variance::variances_of_cycle_fn,
+        cycle_initial = crate::variance::variances_of_cycle_initial,
     )]
     fn variances_of(&self, def: GenericDefId) -> Option<Arc<[crate::variance::Variance]>>;
 
@@ -329,9 +319,31 @@ fn hir_database_is_dyn_compatible() {
     fn _assert_dyn_compatible(_: &dyn HirDatabase) {}
 }
 
-impl_intern_key!(InternedTypeOrConstParamId, TypeOrConstParamId);
+#[salsa_macros::interned(no_lifetime, revisions = usize::MAX)]
+#[derive(PartialOrd, Ord)]
+pub struct InternedTypeOrConstParamId {
+    pub loc: TypeOrConstParamId,
+}
+impl ::std::fmt::Debug for InternedTypeOrConstParamId {
+    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+        f.debug_tuple(stringify!(InternedTypeOrConstParamId))
+            .field(&format_args!("{:04x}", self.0.index()))
+            .finish()
+    }
+}
 
-impl_intern_key!(InternedLifetimeParamId, LifetimeParamId);
+#[salsa_macros::interned(no_lifetime, revisions = usize::MAX)]
+#[derive(PartialOrd, Ord)]
+pub struct InternedLifetimeParamId {
+    pub loc: LifetimeParamId,
+}
+impl ::std::fmt::Debug for InternedLifetimeParamId {
+    fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
+        f.debug_tuple(stringify!(InternedLifetimeParamId))
+            .field(&format_args!("{:04x}", self.0.index()))
+            .finish()
+    }
+}
 
 impl_intern_key!(InternedConstParamId, ConstParamId);
 
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs
index 0bce32a6778..c3ab5aff3db 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs
@@ -25,7 +25,6 @@ use crate::{
     db::HirDatabase,
     display::{HirDisplay, HirDisplayError, HirFormatter},
     infer::BindingMode,
-    lang_items::is_box,
 };
 
 use self::pat_util::EnumerateAndAdjustIterator;
@@ -77,7 +76,7 @@ pub(crate) enum PatKind {
         subpatterns: Vec<FieldPat>,
     },
 
-    /// `box P`, `&P`, `&mut P`, etc.
+    /// `&P`, `&mut P`, etc.
     Deref {
         subpattern: Pat,
     },
@@ -406,7 +405,6 @@ impl HirDisplay for Pat {
             }
             PatKind::Deref { subpattern } => {
                 match self.ty.kind(Interner) {
-                    TyKind::Adt(adt, _) if is_box(f.db, adt.0) => write!(f, "box ")?,
                     &TyKind::Ref(mutbl, ..) => {
                         write!(f, "&{}", if mutbl == Mutability::Mut { "mut " } else { "" })?
                     }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
index 7cf22c64d0f..56fd12e1f2b 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
@@ -21,7 +21,7 @@ use crate::{
     inhabitedness::{is_enum_variant_uninhabited_from, is_ty_uninhabited_from},
 };
 
-use super::{FieldPat, Pat, PatKind, is_box};
+use super::{FieldPat, Pat, PatKind};
 
 use Constructor::*;
 
@@ -170,8 +170,6 @@ impl<'db> MatchCheckCtx<'db> {
             }
             PatKind::Deref { subpattern } => {
                 ctor = match pat.ty.kind(Interner) {
-                    // This is a box pattern.
-                    TyKind::Adt(adt, _) if is_box(self.db, adt.0) => Struct,
                     TyKind::Ref(..) => Ref,
                     _ => {
                         never!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, &pat.ty);
@@ -194,23 +192,6 @@ impl<'db> MatchCheckCtx<'db> {
                         ctor = Struct;
                         arity = substs.len(Interner);
                     }
-                    TyKind::Adt(adt, _) if is_box(self.db, adt.0) => {
-                        // The only legal patterns of type `Box` (outside `std`) are `_` and box
-                        // patterns. If we're here we can assume this is a box pattern.
-                        // FIXME(Nadrieril): A `Box` can in theory be matched either with `Box(_,
-                        // _)` or a box pattern. As a hack to avoid an ICE with the former, we
-                        // ignore other fields than the first one. This will trigger an error later
-                        // anyway.
-                        // See https://github.com/rust-lang/rust/issues/82772 ,
-                        // explanation: https://github.com/rust-lang/rust/pull/82789#issuecomment-796921977
-                        // The problem is that we can't know from the type whether we'll match
-                        // normally or through box-patterns. We'll have to figure out a proper
-                        // solution when we introduce generalized deref patterns. Also need to
-                        // prevent mixing of those two options.
-                        fields.retain(|ipat| ipat.idx == 0);
-                        ctor = Struct;
-                        arity = 1;
-                    }
                     &TyKind::Adt(AdtId(adt), _) => {
                         ctor = match pat.kind.as_ref() {
                             PatKind::Leaf { .. } if matches!(adt, hir_def::AdtId::UnionId(_)) => {
@@ -277,12 +258,6 @@ impl<'db> MatchCheckCtx<'db> {
                         })
                         .collect(),
                 },
-                TyKind::Adt(adt, _) if is_box(self.db, adt.0) => {
-                    // Without `box_patterns`, the only legal pattern of type `Box` is `_` (outside
-                    // of `std`). So this branch is only reachable when the feature is enabled and
-                    // the pattern is a box pattern.
-                    PatKind::Deref { subpattern: subpatterns.next().unwrap() }
-                }
                 TyKind::Adt(adt, substs) => {
                     let variant = Self::variant_id_for_adt(self.db, pat.ctor(), adt.0).unwrap();
                     let subpatterns = self
@@ -343,14 +318,8 @@ impl PatCx for MatchCheckCtx<'_> {
             Struct | Variant(_) | UnionField => match *ty.kind(Interner) {
                 TyKind::Tuple(arity, ..) => arity,
                 TyKind::Adt(AdtId(adt), ..) => {
-                    if is_box(self.db, adt) {
-                        // The only legal patterns of type `Box` (outside `std`) are `_` and box
-                        // patterns. If we're here we can assume this is a box pattern.
-                        1
-                    } else {
-                        let variant = Self::variant_id_for_adt(self.db, ctor, adt).unwrap();
-                        variant.fields(self.db).fields().len()
-                    }
+                    let variant = Self::variant_id_for_adt(self.db, ctor, adt).unwrap();
+                    variant.fields(self.db).fields().len()
                 }
                 _ => {
                     never!("Unexpected type for `Single` constructor: {:?}", ty);
@@ -383,29 +352,22 @@ impl PatCx for MatchCheckCtx<'_> {
                     tys.cloned().map(|ty| (ty, PrivateUninhabitedField(false))).collect()
                 }
                 TyKind::Ref(.., rty) => single(rty.clone()),
-                &TyKind::Adt(AdtId(adt), ref substs) => {
-                    if is_box(self.db, adt) {
-                        // The only legal patterns of type `Box` (outside `std`) are `_` and box
-                        // patterns. If we're here we can assume this is a box pattern.
-                        let subst_ty = substs.at(Interner, 0).assert_ty_ref(Interner).clone();
-                        single(subst_ty)
-                    } else {
-                        let variant = Self::variant_id_for_adt(self.db, ctor, adt).unwrap();
-
-                        let visibilities = LazyCell::new(|| self.db.field_visibilities(variant));
-
-                        self.list_variant_fields(ty, variant)
-                            .map(move |(fid, ty)| {
-                                let is_visible = || {
-                                    matches!(adt, hir_def::AdtId::EnumId(..))
-                                        || visibilities[fid].is_visible_from(self.db, self.module)
-                                };
-                                let is_uninhabited = self.is_uninhabited(&ty);
-                                let private_uninhabited = is_uninhabited && !is_visible();
-                                (ty, PrivateUninhabitedField(private_uninhabited))
-                            })
-                            .collect()
-                    }
+                &TyKind::Adt(AdtId(adt), ..) => {
+                    let variant = Self::variant_id_for_adt(self.db, ctor, adt).unwrap();
+
+                    let visibilities = LazyCell::new(|| self.db.field_visibilities(variant));
+
+                    self.list_variant_fields(ty, variant)
+                        .map(move |(fid, ty)| {
+                            let is_visible = || {
+                                matches!(adt, hir_def::AdtId::EnumId(..))
+                                    || visibilities[fid].is_visible_from(self.db, self.module)
+                            };
+                            let is_uninhabited = self.is_uninhabited(&ty);
+                            let private_uninhabited = is_uninhabited && !is_visible();
+                            (ty, PrivateUninhabitedField(private_uninhabited))
+                        })
+                        .collect()
                 }
                 ty_kind => {
                     never!("Unexpected type for `{:?}` constructor: {:?}", ctor, ty_kind);
@@ -527,6 +489,14 @@ impl PatCx for MatchCheckCtx<'_> {
     fn complexity_exceeded(&self) -> Result<(), Self::Error> {
         Err(())
     }
+
+    fn report_mixed_deref_pat_ctors(
+        &self,
+        _deref_pat: &DeconstructedPat<'_>,
+        _normal_pat: &DeconstructedPat<'_>,
+    ) {
+        // FIXME(deref_patterns): This could report an error comparable to the one in rustc.
+    }
 }
 
 impl fmt::Debug for MatchCheckCtx<'_> {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
index 507bab29208..810fe76f231 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
@@ -1432,10 +1432,10 @@ impl HirDisplay for Ty {
                 match f.closure_style {
                     ClosureStyle::Hide => return write!(f, "{TYPE_HINT_TRUNCATION}"),
                     ClosureStyle::ClosureWithId => {
-                        return write!(f, "{{closure#{:?}}}", id.0.as_u32());
+                        return write!(f, "{{closure#{:?}}}", id.0.index());
                     }
                     ClosureStyle::ClosureWithSubst => {
-                        write!(f, "{{closure#{:?}}}", id.0.as_u32())?;
+                        write!(f, "{{closure#{:?}}}", id.0.index())?;
                         return hir_fmt_generics(f, substs.as_slice(Interner), None, None);
                     }
                     _ => (),
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
index ce53198e966..e880438e3a7 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
@@ -460,19 +460,17 @@ pub struct InferenceResult {
     /// Whenever a tuple field expression access a tuple field, we allocate a tuple id in
     /// [`InferenceContext`] and store the tuples substitution there. This map is the reverse of
     /// that which allows us to resolve a [`TupleFieldId`]s type.
-    pub tuple_field_access_types: FxHashMap<TupleId, Substitution>,
+    tuple_field_access_types: FxHashMap<TupleId, Substitution>,
     /// During inference this field is empty and [`InferenceContext::diagnostics`] is filled instead.
-    pub diagnostics: Vec<InferenceDiagnostic>,
-    pub type_of_expr: ArenaMap<ExprId, Ty>,
+    diagnostics: Vec<InferenceDiagnostic>,
+    pub(crate) type_of_expr: ArenaMap<ExprId, Ty>,
     /// For each pattern record the type it resolves to.
     ///
     /// **Note**: When a pattern type is resolved it may still contain
     /// unresolved or missing subpatterns or subpatterns of mismatched types.
-    pub type_of_pat: ArenaMap<PatId, Ty>,
-    pub type_of_binding: ArenaMap<BindingId, Ty>,
-    pub type_of_rpit: ArenaMap<ImplTraitIdx, Ty>,
-    /// Type of the result of `.into_iter()` on the for. `ExprId` is the one of the whole for loop.
-    pub type_of_for_iterator: FxHashMap<ExprId, Ty>,
+    pub(crate) type_of_pat: ArenaMap<PatId, Ty>,
+    pub(crate) type_of_binding: ArenaMap<BindingId, Ty>,
+    pub(crate) type_of_rpit: ArenaMap<ImplTraitIdx, Ty>,
     type_mismatches: FxHashMap<ExprOrPatId, TypeMismatch>,
     /// Whether there are any type-mismatching errors in the result.
     // FIXME: This isn't as useful as initially thought due to us falling back placeholders to
@@ -483,7 +481,7 @@ pub struct InferenceResult {
     // FIXME: Move this into `InferenceContext`
     standard_types: InternedStandardTypes,
     /// Stores the types which were implicitly dereferenced in pattern binding modes.
-    pub pat_adjustments: FxHashMap<PatId, Vec<Ty>>,
+    pub(crate) pat_adjustments: FxHashMap<PatId, Vec<Ty>>,
     /// Stores the binding mode (`ref` in `let ref x = 2`) of bindings.
     ///
     /// This one is tied to the `PatId` instead of `BindingId`, because in some rare cases, a binding in an
@@ -497,12 +495,12 @@ pub struct InferenceResult {
     /// }
     /// ```
     /// the first `rest` has implicit `ref` binding mode, but the second `rest` binding mode is `move`.
-    pub binding_modes: ArenaMap<PatId, BindingMode>,
-    pub expr_adjustments: FxHashMap<ExprId, Box<[Adjustment]>>,
+    pub(crate) binding_modes: ArenaMap<PatId, BindingMode>,
+    pub(crate) expr_adjustments: FxHashMap<ExprId, Box<[Adjustment]>>,
     pub(crate) closure_info: FxHashMap<ClosureId, (Vec<CapturedItem>, FnTrait)>,
     // FIXME: remove this field
     pub mutated_bindings_in_closure: FxHashSet<BindingId>,
-    pub coercion_casts: FxHashSet<ExprId>,
+    pub(crate) coercion_casts: FxHashSet<ExprId>,
 }
 
 impl InferenceResult {
@@ -566,6 +564,26 @@ impl InferenceResult {
     pub fn is_erroneous(&self) -> bool {
         self.has_errors && self.type_of_expr.iter().count() == 0
     }
+
+    pub fn diagnostics(&self) -> &[InferenceDiagnostic] {
+        &self.diagnostics
+    }
+
+    pub fn tuple_field_access_type(&self, id: TupleId) -> &Substitution {
+        &self.tuple_field_access_types[&id]
+    }
+
+    pub fn pat_adjustment(&self, id: PatId) -> Option<&[Ty]> {
+        self.pat_adjustments.get(&id).map(|it| &**it)
+    }
+
+    pub fn expr_adjustment(&self, id: ExprId) -> Option<&[Adjustment]> {
+        self.expr_adjustments.get(&id).map(|it| &**it)
+    }
+
+    pub fn binding_mode(&self, id: PatId) -> Option<BindingMode> {
+        self.binding_modes.get(id).copied()
+    }
 }
 
 impl Index<ExprId> for InferenceResult {
@@ -772,7 +790,6 @@ impl<'db> InferenceContext<'db> {
             type_of_pat,
             type_of_binding,
             type_of_rpit,
-            type_of_for_iterator,
             type_mismatches,
             has_errors,
             standard_types: _,
@@ -832,11 +849,6 @@ impl<'db> InferenceContext<'db> {
             *has_errors = *has_errors || ty.contains_unknown();
         }
         type_of_rpit.shrink_to_fit();
-        for ty in type_of_for_iterator.values_mut() {
-            *ty = table.resolve_completely(ty.clone());
-            *has_errors = *has_errors || ty.contains_unknown();
-        }
-        type_of_for_iterator.shrink_to_fit();
 
         *has_errors |= !type_mismatches.is_empty();
 
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs
index 65a273cdf8d..c3029bf2b59 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs
@@ -1229,10 +1229,11 @@ impl InferenceContext<'_> {
                     self.select_from_expr(*expr);
                 }
             }
-            Expr::Let { pat: _, expr } => {
+            Expr::Let { pat, expr } => {
                 self.walk_expr(*expr);
-                let place = self.place_of_expr(*expr);
-                self.ref_expr(*expr, place);
+                if let Some(place) = self.place_of_expr(*expr) {
+                    self.consume_with_pat(place, *pat);
+                }
             }
             Expr::UnaryOp { expr, op: _ }
             | Expr::Array(Array::Repeat { initializer: expr, repeat: _ })
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
index d40d52c134d..d43c99fc282 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/expr.rs
@@ -731,9 +731,32 @@ impl InferenceContext<'_> {
                     &Pat::Expr(expr) => {
                         Some(self.infer_expr(expr, &Expectation::none(), ExprIsRead::No))
                     }
-                    Pat::Path(path) => Some(self.infer_expr_path(path, target.into(), tgt_expr)),
+                    Pat::Path(path) => {
+                        let resolver_guard =
+                            self.resolver.update_to_inner_scope(self.db, self.owner, tgt_expr);
+                        let resolution = self.resolver.resolve_path_in_value_ns_fully(
+                            self.db,
+                            path,
+                            self.body.pat_path_hygiene(target),
+                        );
+                        self.resolver.reset_to_guard(resolver_guard);
+
+                        if matches!(
+                            resolution,
+                            Some(
+                                ValueNs::ConstId(_)
+                                    | ValueNs::StructId(_)
+                                    | ValueNs::EnumVariantId(_)
+                            )
+                        ) {
+                            None
+                        } else {
+                            Some(self.infer_expr_path(path, target.into(), tgt_expr))
+                        }
+                    }
                     _ => None,
                 };
+                let is_destructuring_assignment = lhs_ty.is_none();
 
                 if let Some(lhs_ty) = lhs_ty {
                     self.write_pat_ty(target, lhs_ty.clone());
@@ -747,7 +770,15 @@ impl InferenceContext<'_> {
                     self.inside_assignment = false;
                     self.resolver.reset_to_guard(resolver_guard);
                 }
-                self.result.standard_types.unit.clone()
+                if is_destructuring_assignment && self.diverges.is_always() {
+                    // Ordinary assignments always return `()`, even when they diverge.
+                    // However, rustc lowers destructuring assignments into blocks, and blocks return `!` if they have no tail
+                    // expression and they diverge. Therefore, we have to do the same here, even though we don't lower destructuring
+                    // assignments into blocks.
+                    self.table.new_maybe_never_var()
+                } else {
+                    self.result.standard_types.unit.clone()
+                }
             }
             Expr::Range { lhs, rhs, range_type } => {
                 let lhs_ty =
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs
index 3fa2bfbd1b7..107da6a5af6 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout.rs
@@ -261,7 +261,7 @@ pub fn layout_of_ty_query(
         }
         // Potentially-wide pointers.
         TyKind::Ref(_, _, pointee) | TyKind::Raw(_, pointee) => {
-            let mut data_ptr = scalar_unit(dl, Primitive::Pointer(AddressSpace::DATA));
+            let mut data_ptr = scalar_unit(dl, Primitive::Pointer(AddressSpace::ZERO));
             if matches!(ty.kind(Interner), TyKind::Ref(..)) {
                 data_ptr.valid_range_mut().start = 1;
             }
@@ -285,7 +285,7 @@ pub fn layout_of_ty_query(
                     scalar_unit(dl, Primitive::Int(dl.ptr_sized_integer(), false))
                 }
                 TyKind::Dyn(..) => {
-                    let mut vtable = scalar_unit(dl, Primitive::Pointer(AddressSpace::DATA));
+                    let mut vtable = scalar_unit(dl, Primitive::Pointer(AddressSpace::ZERO));
                     vtable.valid_range_mut().start = 1;
                     vtable
                 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs
index e1e1c44996c..88c33ecccad 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs
@@ -2,7 +2,7 @@
 
 use base_db::Crate;
 use hir_def::layout::TargetDataLayout;
-use rustc_abi::{AlignFromBytesError, TargetDataLayoutErrors};
+use rustc_abi::{AlignFromBytesError, TargetDataLayoutErrors, AddressSpace};
 use triomphe::Arc;
 
 use crate::db::HirDatabase;
@@ -12,7 +12,7 @@ pub fn target_data_layout_query(
     krate: Crate,
 ) -> Result<Arc<TargetDataLayout>, Arc<str>> {
     match &krate.workspace_data(db).data_layout {
-        Ok(it) => match TargetDataLayout::parse_from_llvm_datalayout_string(it) {
+        Ok(it) => match TargetDataLayout::parse_from_llvm_datalayout_string(it, AddressSpace::ZERO) {
             Ok(it) => Ok(Arc::new(it)),
             Err(e) => {
                 Err(match e {
@@ -39,6 +39,7 @@ pub fn target_data_layout_query(
                         target,
                     } => format!(r#"inconsistent target specification: "data-layout" claims pointers are {pointer_size}-bit, while "target-pointer-width" is `{target}`"#),
                     TargetDataLayoutErrors::InvalidBitsSize { err } => err,
+                    TargetDataLayoutErrors::UnknownPointerSpecification { err } => format!(r#"use of unknown pointer specifer in "data-layout": {err}"#),
                 }.into())
             }
         },
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs
index 6936d8193eb..9d3d2044c43 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mapping.rs
@@ -13,7 +13,8 @@ use salsa::{
 
 use crate::{
     AssocTypeId, CallableDefId, ChalkTraitId, FnDefId, ForeignDefId, Interner, OpaqueTyId,
-    PlaceholderIndex, chalk_db, db::HirDatabase,
+    PlaceholderIndex, chalk_db,
+    db::{HirDatabase, InternedLifetimeParamId, InternedTypeOrConstParamId},
 };
 
 pub trait ToChalk {
@@ -125,30 +126,32 @@ pub fn from_assoc_type_id(id: AssocTypeId) -> TypeAliasId {
 pub fn from_placeholder_idx(db: &dyn HirDatabase, idx: PlaceholderIndex) -> TypeOrConstParamId {
     assert_eq!(idx.ui, chalk_ir::UniverseIndex::ROOT);
     // SAFETY: We cannot really encapsulate this unfortunately, so just hope this is sound.
-    let interned_id = FromId::from_id(unsafe { Id::from_u32(idx.idx.try_into().unwrap()) });
-    db.lookup_intern_type_or_const_param_id(interned_id)
+    let interned_id =
+        InternedTypeOrConstParamId::from_id(unsafe { Id::from_index(idx.idx.try_into().unwrap()) });
+    interned_id.loc(db)
 }
 
 pub fn to_placeholder_idx(db: &dyn HirDatabase, id: TypeOrConstParamId) -> PlaceholderIndex {
-    let interned_id = db.intern_type_or_const_param_id(id);
+    let interned_id = InternedTypeOrConstParamId::new(db, id);
     PlaceholderIndex {
         ui: chalk_ir::UniverseIndex::ROOT,
-        idx: interned_id.as_id().as_u32() as usize,
+        idx: interned_id.as_id().index() as usize,
     }
 }
 
 pub fn lt_from_placeholder_idx(db: &dyn HirDatabase, idx: PlaceholderIndex) -> LifetimeParamId {
     assert_eq!(idx.ui, chalk_ir::UniverseIndex::ROOT);
     // SAFETY: We cannot really encapsulate this unfortunately, so just hope this is sound.
-    let interned_id = FromId::from_id(unsafe { Id::from_u32(idx.idx.try_into().unwrap()) });
-    db.lookup_intern_lifetime_param_id(interned_id)
+    let interned_id =
+        InternedLifetimeParamId::from_id(unsafe { Id::from_index(idx.idx.try_into().unwrap()) });
+    interned_id.loc(db)
 }
 
 pub fn lt_to_placeholder_idx(db: &dyn HirDatabase, id: LifetimeParamId) -> PlaceholderIndex {
-    let interned_id = db.intern_lifetime_param_id(id);
+    let interned_id = InternedLifetimeParamId::new(db, id);
     PlaceholderIndex {
         ui: chalk_ir::UniverseIndex::ROOT,
-        idx: interned_id.as_id().as_u32() as usize,
+        idx: interned_id.as_id().index() as usize,
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
index 1ec55a82092..55fada14363 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs
@@ -630,7 +630,7 @@ impl Evaluator<'_> {
             Ok(target_data_layout) => target_data_layout,
             Err(e) => return Err(MirEvalError::TargetDataLayoutNotAvailable(e)),
         };
-        let cached_ptr_size = target_data_layout.pointer_size.bytes_usize();
+        let cached_ptr_size = target_data_layout.pointer_size().bytes_usize();
         Ok(Evaluator {
             target_data_layout,
             stack: vec![0],
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs
index d049c678e2d..b5de0e52f5b 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/test_db.rs
@@ -166,10 +166,10 @@ impl TestDB {
         self.events.lock().unwrap().take().unwrap()
     }
 
-    pub(crate) fn log_executed(&self, f: impl FnOnce()) -> Vec<String> {
+    pub(crate) fn log_executed(&self, f: impl FnOnce()) -> (Vec<String>, Vec<salsa::Event>) {
         let events = self.log(f);
-        events
-            .into_iter()
+        let executed = events
+            .iter()
             .filter_map(|e| match e.kind {
                 // This is pretty horrible, but `Debug` is the only way to inspect
                 // QueryDescriptor at the moment.
@@ -181,6 +181,7 @@ impl TestDB {
                 }
                 _ => None,
             })
-            .collect()
+            .collect();
+        (executed, events)
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs
index 7fb981752de..dbc68eeba1e 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs
@@ -446,7 +446,7 @@ fn main() {
 }
 
 #[test]
-fn let_binding_is_a_ref_capture() {
+fn let_binding_is_a_ref_capture_in_ref_binding() {
     check_closure_captures(
         r#"
 //- minicore:copy
@@ -454,12 +454,36 @@ struct S;
 fn main() {
     let mut s = S;
     let s_ref = &mut s;
+    let mut s2 = S;
+    let s_ref2 = &mut s2;
     let closure = || {
         if let ref cb = s_ref {
+        } else if let ref mut cb = s_ref2 {
         }
     };
 }
 "#,
-        expect!["83..135;49..54;112..117 ByRef(Shared) s_ref &'? &'? mut S"],
+        expect![[r#"
+            129..225;49..54;149..155 ByRef(Shared) s_ref &'? &'? mut S
+            129..225;93..99;188..198 ByRef(Mut { kind: Default }) s_ref2 &'? mut &'? mut S"#]],
+    );
+}
+
+#[test]
+fn let_binding_is_a_value_capture_in_binding() {
+    check_closure_captures(
+        r#"
+//- minicore:copy, option
+struct Box(i32);
+fn main() {
+    let b = Some(Box(0));
+    let closure = || {
+        if let Some(b) = b {
+            let _move = b;
+        }
+    };
+}
+"#,
+        expect!["73..149;37..38;103..104 ByValue b Option<Box>"],
     );
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs
index 0377ce95f19..3159499e867 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/incremental.rs
@@ -1,6 +1,7 @@
 use base_db::SourceDatabase;
 use expect_test::Expect;
 use hir_def::{DefWithBodyId, ModuleDefId};
+use salsa::EventKind;
 use test_fixture::WithFixture;
 
 use crate::{db::HirDatabase, test_db::TestDB};
@@ -567,11 +568,11 @@ fn main() {
                 "ast_id_map_shim",
                 "parse_shim",
                 "real_span_map_shim",
-                "query_with_diagnostics_",
+                "TraitItems::query_with_diagnostics_",
                 "body_shim",
                 "body_with_source_map_shim",
                 "attrs_shim",
-                "of_",
+                "ImplItems::of_",
                 "infer_shim",
                 "trait_signature_shim",
                 "trait_signature_with_source_map_shim",
@@ -596,8 +597,8 @@ fn main() {
                 "struct_signature_with_source_map_shim",
                 "generic_predicates_shim",
                 "value_ty_shim",
-                "firewall_",
-                "query_",
+                "VariantFields::firewall_",
+                "VariantFields::query_",
                 "lang_item",
                 "inherent_impls_in_crate_shim",
                 "impl_signature_shim",
@@ -674,11 +675,11 @@ fn main() {
                 "file_item_tree_query",
                 "real_span_map_shim",
                 "crate_local_def_map",
-                "query_with_diagnostics_",
+                "TraitItems::query_with_diagnostics_",
                 "body_with_source_map_shim",
                 "attrs_shim",
                 "body_shim",
-                "of_",
+                "ImplItems::of_",
                 "infer_shim",
                 "attrs_shim",
                 "trait_signature_with_source_map_shim",
@@ -697,7 +698,7 @@ fn main() {
                 "function_signature_with_source_map_shim",
                 "expr_scopes_shim",
                 "struct_signature_with_source_map_shim",
-                "query_",
+                "VariantFields::query_",
                 "inherent_impls_in_crate_shim",
                 "impl_signature_with_source_map_shim",
                 "impl_signature_shim",
@@ -718,10 +719,23 @@ fn execute_assert_events(
     required: &[(&str, usize)],
     expect: Expect,
 ) {
-    let events = db.log_executed(f);
-    for (event, count) in required {
-        let n = events.iter().filter(|it| it.contains(event)).count();
-        assert_eq!(n, *count, "Expected {event} to be executed {count} times, but only got {n}");
-    }
-    expect.assert_debug_eq(&events);
+    let (executed, events) = db.log_executed(f);
+    salsa::attach(db, || {
+        for (event, count) in required {
+            let n = executed.iter().filter(|it| it.contains(event)).count();
+            assert_eq!(
+                n,
+                *count,
+                "Expected {event} to be executed {count} times, but only got {n}:\n \
+             Executed: {executed:#?}\n \
+             Event log: {events:#?}",
+                events = events
+                    .iter()
+                    .filter(|event| !matches!(event.kind, EventKind::WillCheckCancellation))
+                    .map(|event| { format!("{:?}", event.kind) })
+                    .collect::<Vec<_>>(),
+            );
+        }
+        expect.assert_debug_eq(&executed);
+    });
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs
index 1ca4c9b2ad5..6a9135622de 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/never_type.rs
@@ -785,3 +785,31 @@ fn make_up_a_pointer<T>() -> *const T {
         "#]],
     )
 }
+
+#[test]
+fn diverging_destructuring_assignment() {
+    check_infer_with_mismatches(
+        r#"
+fn foo() {
+    let n = match 42 {
+        0 => _ = loop {},
+        _ => 0,
+    };
+}
+    "#,
+        expect![[r#"
+            9..84 '{     ...  }; }': ()
+            19..20 'n': i32
+            23..81 'match ...     }': i32
+            29..31 '42': i32
+            42..43 '0': i32
+            42..43 '0': i32
+            47..48 '_': !
+            47..58 '_ = loop {}': i32
+            51..58 'loop {}': !
+            56..58 '{}': ()
+            68..69 '_': i32
+            73..74 '0': i32
+        "#]],
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
index 43e8f3747ab..b154e598785 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/simple.rs
@@ -3751,7 +3751,7 @@ fn foo() {
     }
     let v: bool = true;
     m!();
- // ^^^^ i32
+ // ^^ i32
 }
         "#,
     );
@@ -3765,39 +3765,39 @@ fn foo() {
     let v: bool;
     macro_rules! m { () => { v } }
     m!();
- // ^^^^ bool
+ // ^^ bool
 
     let v: char;
     macro_rules! m { () => { v } }
     m!();
- // ^^^^ char
+ // ^^ char
 
     {
         let v: u8;
         macro_rules! m { () => { v } }
         m!();
-     // ^^^^ u8
+     // ^^ u8
 
         let v: i8;
         macro_rules! m { () => { v } }
         m!();
-     // ^^^^ i8
+     // ^^ i8
 
         let v: i16;
         macro_rules! m { () => { v } }
         m!();
-     // ^^^^ i16
+     // ^^ i16
 
         {
             let v: u32;
             macro_rules! m { () => { v } }
             m!();
-         // ^^^^ u32
+         // ^^ u32
 
             let v: u64;
             macro_rules! m { () => { v } }
             m!();
-         // ^^^^ u64
+         // ^^ u64
         }
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs
index 08a215fecf6..87d9df611bd 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/variance.rs
@@ -54,14 +54,14 @@ pub(crate) fn variances_of(db: &dyn HirDatabase, def: GenericDefId) -> Option<Ar
     variances.is_empty().not().then(|| Arc::from_iter(variances))
 }
 
-// pub(crate) fn variances_of_cycle_fn(
-//     _db: &dyn HirDatabase,
-//     _result: &Option<Arc<[Variance]>>,
-//     _count: u32,
-//     _def: GenericDefId,
-// ) -> salsa::CycleRecoveryAction<Option<Arc<[Variance]>>> {
-//     salsa::CycleRecoveryAction::Iterate
-// }
+pub(crate) fn variances_of_cycle_fn(
+    _db: &dyn HirDatabase,
+    _result: &Option<Arc<[Variance]>>,
+    _count: u32,
+    _def: GenericDefId,
+) -> salsa::CycleRecoveryAction<Option<Arc<[Variance]>>> {
+    salsa::CycleRecoveryAction::Iterate
+}
 
 pub(crate) fn variances_of_cycle_initial(
     db: &dyn HirDatabase,
@@ -965,7 +965,7 @@ struct S3<T>(S<T, T>);
 struct FixedPoint<T, U, V>(&'static FixedPoint<(), T, U>, V);
 "#,
             expect![[r#"
-                FixedPoint[T: bivariant, U: bivariant, V: bivariant]
+                FixedPoint[T: covariant, U: covariant, V: covariant]
             "#]],
         );
     }
diff --git a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
index aba2e032b32..c1e814ec223 100644
--- a/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/diagnostics.rs
@@ -36,16 +36,16 @@ pub use hir_ty::{
 };
 
 macro_rules! diagnostics {
-    ($($diag:ident $(<$lt:lifetime>)?,)*) => {
+    ($AnyDiagnostic:ident <$db:lifetime> -> $($diag:ident $(<$lt:lifetime>)?,)*) => {
         #[derive(Debug)]
-        pub enum AnyDiagnostic<'db> {$(
+        pub enum $AnyDiagnostic<$db> {$(
             $diag(Box<$diag $(<$lt>)?>),
         )*}
 
         $(
-            impl<'db> From<$diag $(<$lt>)?> for AnyDiagnostic<'db> {
-                fn from(d: $diag $(<$lt>)?) -> AnyDiagnostic<'db> {
-                    AnyDiagnostic::$diag(Box::new(d))
+            impl<$db> From<$diag $(<$lt>)?> for $AnyDiagnostic<$db> {
+                fn from(d: $diag $(<$lt>)?) -> $AnyDiagnostic<$db> {
+                    $AnyDiagnostic::$diag(Box::new(d))
                 }
             }
         )*
@@ -66,7 +66,7 @@ macro_rules! diagnostics {
 // }, ...
 // ]
 
-diagnostics![
+diagnostics![AnyDiagnostic<'db> ->
     AwaitOutsideOfAsync,
     BreakOutsideOfLoop,
     CastToUnsized<'db>,
diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index e8a18169712..5c6f622e6c3 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -1260,7 +1260,9 @@ impl TupleField {
     }
 
     pub fn ty<'db>(&self, db: &'db dyn HirDatabase) -> Type<'db> {
-        let ty = db.infer(self.owner).tuple_field_access_types[&self.tuple]
+        let ty = db
+            .infer(self.owner)
+            .tuple_field_access_type(self.tuple)
             .as_slice(Interner)
             .get(self.index as usize)
             .and_then(|arg| arg.ty(Interner))
@@ -1927,7 +1929,7 @@ impl DefWithBody {
         expr_store_diagnostics(db, acc, &source_map);
 
         let infer = db.infer(self.into());
-        for d in &infer.diagnostics {
+        for d in infer.diagnostics() {
             acc.extend(AnyDiagnostic::inference_diagnostic(
                 db,
                 self.into(),
diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
index f18ca7cb201..0662bfddcf8 100644
--- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
@@ -254,7 +254,7 @@ impl<'db> SourceAnalyzer<'db> {
         // expressions nor patterns).
         let expr_id = self.expr_id(expr.clone())?.as_expr()?;
         let infer = self.infer()?;
-        infer.expr_adjustments.get(&expr_id).map(|v| &**v)
+        infer.expr_adjustment(expr_id)
     }
 
     pub(crate) fn type_of_type(
@@ -286,7 +286,7 @@ impl<'db> SourceAnalyzer<'db> {
         let infer = self.infer()?;
         let coerced = expr_id
             .as_expr()
-            .and_then(|expr_id| infer.expr_adjustments.get(&expr_id))
+            .and_then(|expr_id| infer.expr_adjustment(expr_id))
             .and_then(|adjusts| adjusts.last().map(|adjust| adjust.target.clone()));
         let ty = infer[expr_id].clone();
         let mk_ty = |ty| Type::new_with_resolver(db, &self.resolver, ty);
@@ -302,12 +302,11 @@ impl<'db> SourceAnalyzer<'db> {
         let infer = self.infer()?;
         let coerced = match expr_or_pat_id {
             ExprOrPatId::ExprId(idx) => infer
-                .expr_adjustments
-                .get(&idx)
+                .expr_adjustment(idx)
                 .and_then(|adjusts| adjusts.last().cloned())
                 .map(|adjust| adjust.target),
             ExprOrPatId::PatId(idx) => {
-                infer.pat_adjustments.get(&idx).and_then(|adjusts| adjusts.last().cloned())
+                infer.pat_adjustment(idx).and_then(|adjusts| adjusts.last().cloned())
             }
         };
 
@@ -345,7 +344,7 @@ impl<'db> SourceAnalyzer<'db> {
     ) -> Option<BindingMode> {
         let id = self.pat_id(&pat.clone().into())?;
         let infer = self.infer()?;
-        infer.binding_modes.get(id.as_pat()?).map(|bm| match bm {
+        infer.binding_mode(id.as_pat()?).map(|bm| match bm {
             hir_ty::BindingMode::Move => BindingMode::Move,
             hir_ty::BindingMode::Ref(hir_ty::Mutability::Mut) => BindingMode::Ref(Mutability::Mut),
             hir_ty::BindingMode::Ref(hir_ty::Mutability::Not) => {
@@ -362,8 +361,7 @@ impl<'db> SourceAnalyzer<'db> {
         let infer = self.infer()?;
         Some(
             infer
-                .pat_adjustments
-                .get(&pat_id.as_pat()?)?
+                .pat_adjustment(pat_id.as_pat()?)?
                 .iter()
                 .map(|ty| Type::new_with_resolver(db, &self.resolver, ty.clone()))
                 .collect(),
@@ -736,7 +734,7 @@ impl<'db> SourceAnalyzer<'db> {
         let variant = self.infer()?.variant_resolution_for_pat(pat_id.as_pat()?)?;
         let variant_data = variant.fields(db);
         let field = FieldId { parent: variant, local_id: variant_data.field(&field_name)? };
-        let (adt, subst) = self.infer()?.type_of_pat.get(pat_id.as_pat()?)?.as_adt()?;
+        let (adt, subst) = self.infer()?[pat_id.as_pat()?].as_adt()?;
         let field_ty =
             db.field_types(variant).get(field.local_id)?.clone().substitute(Interner, subst);
         Some((
@@ -765,7 +763,8 @@ impl<'db> SourceAnalyzer<'db> {
             },
         };
 
-        let res = resolve_hir_path(db, &self.resolver, path, HygieneId::ROOT, Some(store))?;
+        let body_owner = self.resolver.body_owner();
+        let res = resolve_hir_value_path(db, &self.resolver, body_owner, path, HygieneId::ROOT)?;
         match res {
             PathResolution::Def(def) => Some(def),
             _ => None,
@@ -1249,7 +1248,7 @@ impl<'db> SourceAnalyzer<'db> {
         let infer = self.infer()?;
 
         let pat_id = self.pat_id(&pattern.clone().into())?.as_pat()?;
-        let substs = infer.type_of_pat[pat_id].as_adt()?.1;
+        let substs = infer[pat_id].as_adt()?.1;
 
         let (variant, missing_fields, _exhaustive) =
             record_pattern_missing_fields(db, infer, pat_id, &body[pat_id])?;
@@ -1785,8 +1784,8 @@ pub(crate) fn name_hygiene(db: &dyn HirDatabase, name: InFile<&SyntaxNode>) -> H
 }
 
 fn type_of_expr_including_adjust(infer: &InferenceResult, id: ExprId) -> Option<&Ty> {
-    match infer.expr_adjustments.get(&id).and_then(|adjustments| adjustments.last()) {
+    match infer.expr_adjustment(id).and_then(|adjustments| adjustments.last()) {
         Some(adjustment) => Some(&adjustment.target),
-        None => infer.type_of_expr.get(id),
+        None => Some(&infer[id]),
     }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs
index b71de5e00c6..c80b78fd970 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs
@@ -175,7 +175,7 @@ pub(crate) fn expand_rest_pattern(acc: &mut Assists, ctx: &AssistContext<'_>) ->
             // ast::TuplePat(it) => (),
             // FIXME
             // ast::SlicePat(it) => (),
-            _ => return None,
+            _ => None,
         }
     }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs
index 6316a8f0db2..603be4d6673 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/promote_local_to_const.rs
@@ -3,8 +3,7 @@ use ide_db::{assists::AssistId, defs::Definition};
 use stdx::to_upper_snake_case;
 use syntax::{
     AstNode,
-    ast::{self, HasName, make},
-    ted,
+    ast::{self, HasName, syntax_factory::SyntaxFactory},
 };
 
 use crate::{
@@ -69,15 +68,18 @@ pub(crate) fn promote_local_to_const(acc: &mut Assists, ctx: &AssistContext<'_>)
         "Promote local to constant",
         let_stmt.syntax().text_range(),
         |edit| {
+            let make = SyntaxFactory::with_mappings();
+            let mut editor = edit.make_editor(let_stmt.syntax());
             let name = to_upper_snake_case(&name.to_string());
             let usages = Definition::Local(local).usages(&ctx.sema).all();
             if let Some(usages) = usages.references.get(&ctx.file_id()) {
-                let name_ref = make::name_ref(&name);
+                let name_ref = make.name_ref(&name);
 
                 for usage in usages {
                     let Some(usage_name) = usage.name.as_name_ref().cloned() else { continue };
                     if let Some(record_field) = ast::RecordExprField::for_name_ref(&usage_name) {
-                        let name_expr = make::expr_path(make::path_from_text(&name));
+                        let path = make.ident_path(&name);
+                        let name_expr = make.expr_path(path);
                         utils::replace_record_field_expr(ctx, edit, record_field, name_expr);
                     } else {
                         let usage_range = usage.range;
@@ -86,15 +88,17 @@ pub(crate) fn promote_local_to_const(acc: &mut Assists, ctx: &AssistContext<'_>)
                 }
             }
 
-            let item = make::item_const(None, make::name(&name), make::ty(&ty), initializer)
-                .clone_for_update();
-            let let_stmt = edit.make_mut(let_stmt);
+            let item = make.item_const(None, make.name(&name), make.ty(&ty), initializer);
 
             if let Some((cap, name)) = ctx.config.snippet_cap.zip(item.name()) {
-                edit.add_tabstop_before(cap, name);
+                let tabstop = edit.make_tabstop_before(cap);
+                editor.add_annotation(name.syntax().clone(), tabstop);
             }
 
-            ted::replace(let_stmt.syntax(), item.syntax());
+            editor.replace(let_stmt.syntax(), item.syntax());
+
+            editor.add_mappings(make.finish_with_mappings());
+            edit.add_file_edits(ctx.vfs_file_id(), editor);
         },
     )
 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs
index e933bcc40db..62914ee7f38 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs
@@ -1,8 +1,5 @@
 use ide_db::syntax_helpers::suggest_name;
-use syntax::{
-    ast::{self, AstNode, make},
-    ted,
-};
+use syntax::ast::{self, AstNode, syntax_factory::SyntaxFactory};
 
 use crate::{AssistContext, AssistId, Assists};
 
@@ -60,21 +57,25 @@ pub(crate) fn replace_is_method_with_if_let_method(
                 message,
                 call_expr.syntax().text_range(),
                 |edit| {
-                    let call_expr = edit.make_mut(call_expr);
+                    let make = SyntaxFactory::with_mappings();
+                    let mut editor = edit.make_editor(call_expr.syntax());
 
-                    let var_pat = make::ident_pat(false, false, make::name(&var_name));
-                    let pat = make::tuple_struct_pat(make::ext::ident_path(text), [var_pat.into()]);
-                    let let_expr = make::expr_let(pat.into(), receiver).clone_for_update();
+                    let var_pat = make.ident_pat(false, false, make.name(&var_name));
+                    let pat = make.tuple_struct_pat(make.ident_path(text), [var_pat.into()]);
+                    let let_expr = make.expr_let(pat.into(), receiver);
 
                     if let Some(cap) = ctx.config.snippet_cap {
                         if let Some(ast::Pat::TupleStructPat(pat)) = let_expr.pat() {
                             if let Some(first_var) = pat.fields().next() {
-                                edit.add_placeholder_snippet(cap, first_var);
+                                let placeholder = edit.make_placeholder_snippet(cap);
+                                editor.add_annotation(first_var.syntax(), placeholder);
                             }
                         }
                     }
 
-                    ted::replace(call_expr.syntax(), let_expr.syntax());
+                    editor.replace(call_expr.syntax(), let_expr.syntax());
+                    editor.add_mappings(make.finish_with_mappings());
+                    edit.add_file_edits(ctx.vfs_file_id(), editor);
                 },
             )
         }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs
index 109269bd6e6..504e12f93df 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs
@@ -1,8 +1,7 @@
 use ide_db::assists::AssistId;
 use syntax::{
     AstNode, T,
-    ast::{self, make},
-    ted,
+    ast::{self, syntax_factory::SyntaxFactory},
 };
 
 use crate::{AssistContext, Assists};
@@ -37,8 +36,7 @@ pub(crate) fn toggle_macro_delimiter(acc: &mut Assists, ctx: &AssistContext<'_>)
         RCur,
     }
 
-    let makro = ctx.find_node_at_offset::<ast::MacroCall>()?.clone_for_update();
-    let makro_text_range = makro.syntax().text_range();
+    let makro = ctx.find_node_at_offset::<ast::MacroCall>()?;
 
     let cursor_offset = ctx.offset();
     let semicolon = makro.semicolon_token();
@@ -71,24 +69,28 @@ pub(crate) fn toggle_macro_delimiter(acc: &mut Assists, ctx: &AssistContext<'_>)
         },
         token_tree.syntax().text_range(),
         |builder| {
+            let make = SyntaxFactory::with_mappings();
+            let mut editor = builder.make_editor(makro.syntax());
+
             match token {
                 MacroDelims::LPar | MacroDelims::RPar => {
-                    ted::replace(ltoken, make::token(T!['{']));
-                    ted::replace(rtoken, make::token(T!['}']));
+                    editor.replace(ltoken, make.token(T!['{']));
+                    editor.replace(rtoken, make.token(T!['}']));
                     if let Some(sc) = semicolon {
-                        ted::remove(sc);
+                        editor.delete(sc);
                     }
                 }
                 MacroDelims::LBra | MacroDelims::RBra => {
-                    ted::replace(ltoken, make::token(T!['(']));
-                    ted::replace(rtoken, make::token(T![')']));
+                    editor.replace(ltoken, make.token(T!['(']));
+                    editor.replace(rtoken, make.token(T![')']));
                 }
                 MacroDelims::LCur | MacroDelims::RCur => {
-                    ted::replace(ltoken, make::token(T!['[']));
-                    ted::replace(rtoken, make::token(T![']']));
+                    editor.replace(ltoken, make.token(T!['[']));
+                    editor.replace(rtoken, make.token(T![']']));
                 }
             }
-            builder.replace(makro_text_range, makro.syntax().text());
+            editor.add_mappings(make.finish_with_mappings());
+            builder.add_file_edits(ctx.vfs_file_id(), editor);
         },
     )
 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_match_arm.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_match_arm.rs
index 5aedff5cc77..7b0f2dc65a7 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_match_arm.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unmerge_match_arm.rs
@@ -1,8 +1,7 @@
 use syntax::{
     Direction, SyntaxKind, T,
-    algo::neighbor,
-    ast::{self, AstNode, edit::IndentLevel, make},
-    ted::{self, Position},
+    ast::{self, AstNode, edit::IndentLevel, syntax_factory::SyntaxFactory},
+    syntax_editor::{Element, Position},
 };
 
 use crate::{AssistContext, AssistId, Assists};
@@ -33,7 +32,7 @@ use crate::{AssistContext, AssistId, Assists};
 // ```
 pub(crate) fn unmerge_match_arm(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
     let pipe_token = ctx.find_token_syntax_at_offset(T![|])?;
-    let or_pat = ast::OrPat::cast(pipe_token.parent()?)?.clone_for_update();
+    let or_pat = ast::OrPat::cast(pipe_token.parent()?)?;
     if or_pat.leading_pipe().is_some_and(|it| it == pipe_token) {
         return None;
     }
@@ -44,13 +43,14 @@ pub(crate) fn unmerge_match_arm(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
     // without `OrPat`.
 
     let new_parent = match_arm.syntax().parent()?;
-    let old_parent_range = new_parent.text_range();
 
     acc.add(
         AssistId::refactor_rewrite("unmerge_match_arm"),
         "Unmerge match arm",
         pipe_token.text_range(),
         |edit| {
+            let make = SyntaxFactory::with_mappings();
+            let mut editor = edit.make_editor(&new_parent);
             let pats_after = pipe_token
                 .siblings_with_tokens(Direction::Next)
                 .filter_map(|it| ast::Pat::cast(it.into_node()?))
@@ -59,11 +59,9 @@ pub(crate) fn unmerge_match_arm(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
             let new_pat = if pats_after.len() == 1 {
                 pats_after[0].clone()
             } else {
-                make::or_pat(pats_after, or_pat.leading_pipe().is_some()).into()
+                make.or_pat(pats_after, or_pat.leading_pipe().is_some()).into()
             };
-            let new_match_arm =
-                make::match_arm(new_pat, match_arm.guard(), match_arm_body).clone_for_update();
-
+            let new_match_arm = make.match_arm(new_pat, match_arm.guard(), match_arm_body);
             let mut pipe_index = pipe_token.index();
             if pipe_token
                 .prev_sibling_or_token()
@@ -71,10 +69,13 @@ pub(crate) fn unmerge_match_arm(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
             {
                 pipe_index -= 1;
             }
-            or_pat.syntax().splice_children(
-                pipe_index..or_pat.syntax().children_with_tokens().count(),
-                Vec::new(),
-            );
+            for child in or_pat
+                .syntax()
+                .children_with_tokens()
+                .skip_while(|child| child.index() < pipe_index)
+            {
+                editor.delete(child.syntax_element());
+            }
 
             let mut insert_after_old_arm = Vec::new();
 
@@ -86,33 +87,19 @@ pub(crate) fn unmerge_match_arm(acc: &mut Assists, ctx: &AssistContext<'_>) -> O
             //    body is a block, but we don't bother to check that.
             //  - Missing after the arm with arms after, if the arm body is a block. In this case
             //    we don't want to insert a comma at all.
-            let has_comma_after =
-                std::iter::successors(match_arm.syntax().last_child_or_token(), |it| {
-                    it.prev_sibling_or_token()
-                })
-                .map(|it| it.kind())
-                .find(|it| !it.is_trivia())
-                    == Some(T![,]);
-            let has_arms_after = neighbor(&match_arm, Direction::Next).is_some();
-            if !has_comma_after && !has_arms_after {
-                insert_after_old_arm.push(make::token(T![,]).into());
+            let has_comma_after = match_arm.comma_token().is_some();
+            if !has_comma_after && !match_arm.expr().unwrap().is_block_like() {
+                insert_after_old_arm.push(make.token(T![,]).into());
             }
 
             let indent = IndentLevel::from_node(match_arm.syntax());
-            insert_after_old_arm.push(make::tokens::whitespace(&format!("\n{indent}")).into());
+            insert_after_old_arm.push(make.whitespace(&format!("\n{indent}")).into());
 
             insert_after_old_arm.push(new_match_arm.syntax().clone().into());
 
-            ted::insert_all_raw(Position::after(match_arm.syntax()), insert_after_old_arm);
-
-            if has_comma_after {
-                ted::insert_raw(
-                    Position::last_child_of(new_match_arm.syntax()),
-                    make::token(T![,]),
-                );
-            }
-
-            edit.replace(old_parent_range, new_parent.to_string());
+            editor.insert_all(Position::after(match_arm.syntax()), insert_after_old_arm);
+            editor.add_mappings(make.finish_with_mappings());
+            edit.add_file_edits(ctx.vfs_file_id(), editor);
         },
     )
 }
@@ -258,7 +245,7 @@ fn main() {
     let x = X::A;
     let y = match x {
         X::A => 1i32,
-        X::B => 1i32
+        X::B => 1i32,
     };
 }
 "#,
@@ -276,7 +263,7 @@ enum X { A, B }
 fn main() {
     let x = X::A;
     match x {
-        X::A $0| X::B => {},
+        X::A $0| X::B => {}
     }
 }
 "#,
@@ -287,8 +274,8 @@ enum X { A, B }
 fn main() {
     let x = X::A;
     match x {
-        X::A => {},
-        X::B => {},
+        X::A => {}
+        X::B => {}
     }
 }
 "#,
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs
index e1b94673e77..5183566d136 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/wrap_unwrap_cfg_attr.rs
@@ -2,8 +2,7 @@ use ide_db::source_change::SourceChangeBuilder;
 use itertools::Itertools;
 use syntax::{
     NodeOrToken, SyntaxToken, T, TextRange, algo,
-    ast::{self, AstNode, make},
-    ted::{self, Position},
+    ast::{self, AstNode, make, syntax_factory::SyntaxFactory},
 };
 
 use crate::{AssistContext, AssistId, Assists};
@@ -173,40 +172,45 @@ fn wrap_derive(
         }
     }
     let handle_source_change = |edit: &mut SourceChangeBuilder| {
-        let new_derive = make::attr_outer(make::meta_token_tree(
-            make::ext::ident_path("derive"),
-            make::token_tree(T!['('], new_derive),
-        ))
-        .clone_for_update();
-        let meta = make::meta_token_tree(
-            make::ext::ident_path("cfg_attr"),
-            make::token_tree(
+        let make = SyntaxFactory::with_mappings();
+        let mut editor = edit.make_editor(attr.syntax());
+        let new_derive = make.attr_outer(
+            make.meta_token_tree(make.ident_path("derive"), make.token_tree(T!['('], new_derive)),
+        );
+        let meta = make.meta_token_tree(
+            make.ident_path("cfg_attr"),
+            make.token_tree(
                 T!['('],
                 vec![
-                    NodeOrToken::Token(make::token(T![,])),
-                    NodeOrToken::Token(make::tokens::whitespace(" ")),
-                    NodeOrToken::Token(make::tokens::ident("derive")),
-                    NodeOrToken::Node(make::token_tree(T!['('], cfg_derive_tokens)),
+                    NodeOrToken::Token(make.token(T![,])),
+                    NodeOrToken::Token(make.whitespace(" ")),
+                    NodeOrToken::Token(make.ident("derive")),
+                    NodeOrToken::Node(make.token_tree(T!['('], cfg_derive_tokens)),
                 ],
             ),
         );
-        // Remove the derive attribute
-        let edit_attr = edit.make_syntax_mut(attr.syntax().clone());
-
-        ted::replace(edit_attr, new_derive.syntax().clone());
-        let cfg_attr = make::attr_outer(meta).clone_for_update();
 
-        ted::insert_all_raw(
-            Position::after(new_derive.syntax().clone()),
-            vec![make::tokens::whitespace("\n").into(), cfg_attr.syntax().clone().into()],
+        let cfg_attr = make.attr_outer(meta);
+        editor.replace_with_many(
+            attr.syntax(),
+            vec![
+                new_derive.syntax().clone().into(),
+                make.whitespace("\n").into(),
+                cfg_attr.syntax().clone().into(),
+            ],
         );
+
         if let Some(snippet_cap) = ctx.config.snippet_cap {
             if let Some(first_meta) =
                 cfg_attr.meta().and_then(|meta| meta.token_tree()).and_then(|tt| tt.l_paren_token())
             {
-                edit.add_tabstop_after_token(snippet_cap, first_meta)
+                let tabstop = edit.make_tabstop_after(snippet_cap);
+                editor.add_annotation(first_meta, tabstop);
             }
         }
+
+        editor.add_mappings(make.finish_with_mappings());
+        edit.add_file_edits(ctx.vfs_file_id(), editor);
     };
 
     acc.add(
@@ -221,10 +225,10 @@ fn wrap_cfg_attr(acc: &mut Assists, ctx: &AssistContext<'_>, attr: ast::Attr) ->
     let range = attr.syntax().text_range();
     let path = attr.path()?;
     let handle_source_change = |edit: &mut SourceChangeBuilder| {
-        let mut raw_tokens = vec![
-            NodeOrToken::Token(make::token(T![,])),
-            NodeOrToken::Token(make::tokens::whitespace(" ")),
-        ];
+        let make = SyntaxFactory::with_mappings();
+        let mut editor = edit.make_editor(attr.syntax());
+        let mut raw_tokens =
+            vec![NodeOrToken::Token(make.token(T![,])), NodeOrToken::Token(make.whitespace(" "))];
         path.syntax().descendants_with_tokens().for_each(|it| {
             if let NodeOrToken::Token(token) = it {
                 raw_tokens.push(NodeOrToken::Token(token));
@@ -232,9 +236,9 @@ fn wrap_cfg_attr(acc: &mut Assists, ctx: &AssistContext<'_>, attr: ast::Attr) ->
         });
         if let Some(meta) = attr.meta() {
             if let (Some(eq), Some(expr)) = (meta.eq_token(), meta.expr()) {
-                raw_tokens.push(NodeOrToken::Token(make::tokens::whitespace(" ")));
+                raw_tokens.push(NodeOrToken::Token(make.whitespace(" ")));
                 raw_tokens.push(NodeOrToken::Token(eq));
-                raw_tokens.push(NodeOrToken::Token(make::tokens::whitespace(" ")));
+                raw_tokens.push(NodeOrToken::Token(make.whitespace(" ")));
 
                 expr.syntax().descendants_with_tokens().for_each(|it| {
                     if let NodeOrToken::Token(token) = it {
@@ -245,26 +249,24 @@ fn wrap_cfg_attr(acc: &mut Assists, ctx: &AssistContext<'_>, attr: ast::Attr) ->
                 raw_tokens.extend(tt.token_trees_and_tokens());
             }
         }
-        let meta = make::meta_token_tree(
-            make::ext::ident_path("cfg_attr"),
-            make::token_tree(T!['('], raw_tokens),
-        );
-        let cfg_attr = if attr.excl_token().is_some() {
-            make::attr_inner(meta)
-        } else {
-            make::attr_outer(meta)
-        }
-        .clone_for_update();
-        let attr_syntax = edit.make_syntax_mut(attr.syntax().clone());
-        ted::replace(attr_syntax, cfg_attr.syntax());
+        let meta =
+            make.meta_token_tree(make.ident_path("cfg_attr"), make.token_tree(T!['('], raw_tokens));
+        let cfg_attr =
+            if attr.excl_token().is_some() { make.attr_inner(meta) } else { make.attr_outer(meta) };
+
+        editor.replace(attr.syntax(), cfg_attr.syntax());
 
         if let Some(snippet_cap) = ctx.config.snippet_cap {
             if let Some(first_meta) =
                 cfg_attr.meta().and_then(|meta| meta.token_tree()).and_then(|tt| tt.l_paren_token())
             {
-                edit.add_tabstop_after_token(snippet_cap, first_meta)
+                let tabstop = edit.make_tabstop_after(snippet_cap);
+                editor.add_annotation(first_meta, tabstop);
             }
         }
+
+        editor.add_mappings(make.finish_with_mappings());
+        edit.add_file_edits(ctx.vfs_file_id(), editor);
     };
     acc.add(
         AssistId::refactor("wrap_unwrap_cfg_attr"),
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
index 1a91053f93c..87a4c2ef758 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
@@ -1,5 +1,7 @@
 //! Assorted functions shared by several assists.
 
+use std::slice;
+
 pub(crate) use gen_trait_fn_body::gen_trait_fn_body;
 use hir::{
     DisplayTarget, HasAttrs as HirHasAttrs, HirDisplay, InFile, ModuleDef, PathResolution,
@@ -912,7 +914,7 @@ fn handle_as_ref_str(
 ) -> Option<(ReferenceConversionType, bool)> {
     let str_type = hir::BuiltinType::str().ty(db);
 
-    ty.impls_trait(db, famous_defs.core_convert_AsRef()?, &[str_type.clone()])
+    ty.impls_trait(db, famous_defs.core_convert_AsRef()?, slice::from_ref(&str_type))
         .then_some((ReferenceConversionType::AsRefStr, could_deref_to_target(ty, &str_type, db)))
 }
 
@@ -924,7 +926,7 @@ fn handle_as_ref_slice(
     let type_argument = ty.type_arguments().next()?;
     let slice_type = hir::Type::new_slice(type_argument);
 
-    ty.impls_trait(db, famous_defs.core_convert_AsRef()?, &[slice_type.clone()]).then_some((
+    ty.impls_trait(db, famous_defs.core_convert_AsRef()?, slice::from_ref(&slice_type)).then_some((
         ReferenceConversionType::AsRefSlice,
         could_deref_to_target(ty, &slice_type, db),
     ))
@@ -937,10 +939,11 @@ fn handle_dereferenced(
 ) -> Option<(ReferenceConversionType, bool)> {
     let type_argument = ty.type_arguments().next()?;
 
-    ty.impls_trait(db, famous_defs.core_convert_AsRef()?, &[type_argument.clone()]).then_some((
-        ReferenceConversionType::Dereferenced,
-        could_deref_to_target(ty, &type_argument, db),
-    ))
+    ty.impls_trait(db, famous_defs.core_convert_AsRef()?, slice::from_ref(&type_argument))
+        .then_some((
+            ReferenceConversionType::Dereferenced,
+            could_deref_to_target(ty, &type_argument, db),
+        ))
 }
 
 fn handle_option_as_ref(
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs
index 411902f1117..46a36300459 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/attribute.rs
@@ -878,6 +878,7 @@ mod derive {
             expect![[r#"
                 de Clone              macro Clone
                 de Clone, Copy
+                de Debug              macro Debug
                 de Default          macro Default
                 de PartialEq      macro PartialEq
                 de PartialEq, Eq
@@ -900,6 +901,7 @@ mod derive {
             expect![[r#"
                 de Clone     macro Clone
                 de Clone, Copy
+                de Debug     macro Debug
                 de Default macro Default
                 de Eq
                 de Eq, PartialOrd, Ord
@@ -921,6 +923,7 @@ mod derive {
             expect![[r#"
                 de Clone     macro Clone
                 de Clone, Copy
+                de Debug     macro Debug
                 de Default macro Default
                 de Eq
                 de Eq, PartialOrd, Ord
@@ -942,6 +945,7 @@ mod derive {
             expect![[r#"
                 de Clone     macro Clone
                 de Clone, Copy
+                de Debug     macro Debug
                 de Default macro Default
                 de PartialOrd
                 de PartialOrd, Ord
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs b/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs
index 994150b1ac4..8e687385086 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/famous_defs.rs
@@ -106,6 +106,18 @@ impl FamousDefs<'_, '_> {
         self.find_trait("core:convert:AsRef")
     }
 
+    pub fn core_convert_AsMut(&self) -> Option<Trait> {
+        self.find_trait("core:convert:AsMut")
+    }
+
+    pub fn core_borrow_Borrow(&self) -> Option<Trait> {
+        self.find_trait("core:borrow:Borrow")
+    }
+
+    pub fn core_borrow_BorrowMut(&self) -> Option<Trait> {
+        self.find_trait("core:borrow:BorrowMut")
+    }
+
     pub fn core_ops_ControlFlow(&self) -> Option<Enum> {
         self.find_enum("core:ops:ControlFlow")
     }
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 de8a42979bb..f9eb44d03ab 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
@@ -4711,9 +4711,9 @@ The tracking issue for this feature is: [#133668]
         label: "const_trait_impl",
         description: r##"# `const_trait_impl`
 
-The tracking issue for this feature is: [#67792]
+The tracking issue for this feature is: [#143874]
 
-[#67792]: https://github.com/rust-lang/rust/issues/67792
+[#143874]: https://github.com/rust-lang/rust/issues/143874
 
 ------------------------
 "##,
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs b/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs
index 5356614dce5..e6618573e09 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs
@@ -272,5 +272,5 @@ fn crate_name(db: &RootDatabase, krate: Crate) -> Symbol {
         .display_name
         .as_deref()
         .cloned()
-        .unwrap_or_else(|| Symbol::integer(salsa::plumbing::AsId::as_id(&krate).as_u32() as usize))
+        .unwrap_or_else(|| Symbol::integer(salsa::plumbing::AsId::as_id(&krate).index() as usize))
 }
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
index 7d460f72492..4efb83ba323 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
@@ -317,7 +317,7 @@ impl Definition {
             };
             return match def {
                 Some(def) => SearchScope::file_range(
-                    def.as_ref().original_file_range_with_macro_call_body(db),
+                    def.as_ref().original_file_range_with_macro_call_input(db),
                 ),
                 None => SearchScope::single_file(file_id),
             };
@@ -332,7 +332,7 @@ impl Definition {
             };
             return match def {
                 Some(def) => SearchScope::file_range(
-                    def.as_ref().original_file_range_with_macro_call_body(db),
+                    def.as_ref().original_file_range_with_macro_call_input(db),
                 ),
                 None => SearchScope::single_file(file_id),
             };
@@ -341,7 +341,7 @@ impl Definition {
         if let Definition::SelfType(impl_) = self {
             return match impl_.source(db).map(|src| src.syntax().cloned()) {
                 Some(def) => SearchScope::file_range(
-                    def.as_ref().original_file_range_with_macro_call_body(db),
+                    def.as_ref().original_file_range_with_macro_call_input(db),
                 ),
                 None => SearchScope::single_file(file_id),
             };
@@ -360,7 +360,7 @@ impl Definition {
             };
             return match def {
                 Some(def) => SearchScope::file_range(
-                    def.as_ref().original_file_range_with_macro_call_body(db),
+                    def.as_ref().original_file_range_with_macro_call_input(db),
                 ),
                 None => SearchScope::single_file(file_id),
             };
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs
index 546512a6cf9..c39e00e178f 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/macro_error.rs
@@ -242,8 +242,8 @@ macro_rules! outer {
 
 fn f() {
     outer!();
-} //^^^^^^^^ error: leftover tokens
-  //^^^^^^^^ error: Syntax Error in Expansion: expected expression
+} //^^^^^^ error: leftover tokens
+  //^^^^^^ error: Syntax Error in Expansion: expected expression
 "#,
         )
     }
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs
index 8a5d82b48c0..7da799e0d49 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs
@@ -66,7 +66,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::MissingFields) -> Option<Vec<Ass
     let current_module =
         ctx.sema.scope(d.field_list_parent.to_node(&root).syntax()).map(|it| it.module());
     let range = InFile::new(d.file, d.field_list_parent.text_range())
-        .original_node_file_range_rooted(ctx.sema.db);
+        .original_node_file_range_rooted_opt(ctx.sema.db)?;
 
     let build_text_edit = |new_syntax: &SyntaxNode, old_syntax| {
         let edit = {
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
index 0928262d22f..1e80d02926d 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/moved_out_of_ref.rs
@@ -239,4 +239,22 @@ impl S {
 "#,
         )
     }
+
+    #[test]
+    fn regression_20155() {
+        check_diagnostics(
+            r#"
+//- minicore: copy, option
+struct Box(i32);
+fn test() {
+    let b = Some(Box(0));
+    || {
+        if let Some(b) = b {
+            let _move = b;
+        }
+    };
+}
+"#,
+        )
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs
index 4327b12dce7..fc2648efb40 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/trait_impl_redundant_assoc_item.rs
@@ -77,6 +77,7 @@ fn quickfix_for_redundant_assoc_item(
     redundant_item_def: String,
     range: TextRange,
 ) -> Option<Vec<Assist>> {
+    let file_id = d.file_id.file_id()?;
     let add_assoc_item_def = |builder: &mut SourceChangeBuilder| -> Option<()> {
         let db = ctx.sema.db;
         let root = db.parse_or_expand(d.file_id);
@@ -90,12 +91,14 @@ fn quickfix_for_redundant_assoc_item(
         let trait_def = d.trait_.source(db)?.value;
         let l_curly = trait_def.assoc_item_list()?.l_curly_token()?.text_range();
         let where_to_insert =
-            hir::InFile::new(d.file_id, l_curly).original_node_file_range_rooted(db).range;
+            hir::InFile::new(d.file_id, l_curly).original_node_file_range_rooted_opt(db)?;
+        if where_to_insert.file_id != file_id {
+            return None;
+        }
 
-        builder.insert(where_to_insert.end(), redundant_item_def);
+        builder.insert(where_to_insert.range.end(), redundant_item_def);
         Some(())
     };
-    let file_id = d.file_id.file_id()?;
     let mut source_change_builder = SourceChangeBuilder::new(file_id.file_id(ctx.sema.db));
     add_assoc_item_def(&mut source_change_builder)?;
 
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs
index 1f2d671249d..dcca85d4db3 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unresolved_method.rs
@@ -120,8 +120,7 @@ fn assoc_func_fix(
 
         let call = ast::MethodCallExpr::cast(expr.syntax().clone())?;
         let range = InFile::new(expr_ptr.file_id, call.syntax().text_range())
-            .original_node_file_range_rooted(db)
-            .range;
+            .original_node_file_range_rooted_opt(db)?;
 
         let receiver = call.receiver()?;
         let receiver_type = &ctx.sema.type_of_expr(&receiver)?.original;
@@ -174,18 +173,16 @@ fn assoc_func_fix(
 
         let assoc_func_call_expr_string = make::expr_call(assoc_func_path, args).to_string();
 
-        let file_id = ctx.sema.original_range_opt(call.receiver()?.syntax())?.file_id;
-
         Some(Assist {
             id: AssistId::quick_fix("method_call_to_assoc_func_call_fix"),
             label: Label::new(format!(
                 "Use associated func call instead: `{assoc_func_call_expr_string}`"
             )),
             group: None,
-            target: range,
+            target: range.range,
             source_change: Some(SourceChange::from_text_edit(
-                file_id.file_id(ctx.sema.db),
-                TextEdit::replace(range, assoc_func_call_expr_string),
+                range.file_id.file_id(ctx.sema.db),
+                TextEdit::replace(range.range, assoc_func_call_expr_string),
             )),
             command: None,
         })
@@ -300,7 +297,7 @@ macro_rules! m {
 }
 fn main() {
     m!(());
- // ^^^^^^ error: no method `foo` on type `()`
+ // ^^ error: no method `foo` on type `()`
 }
 "#,
         );
diff --git a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs
index 4b8d07a2533..7a0405939d1 100644
--- a/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/call_hierarchy.rs
@@ -592,7 +592,7 @@ macro_rules! call {
 "#,
             expect!["callee Function FileId(0) 22..37 30..36"],
             expect![[r#"
-                caller Function FileId(0) 38..52 : FileId(0):44..50
+                caller Function FileId(0) 38..43 : FileId(0):44..50
                 caller Function FileId(1) 130..136 130..136 : FileId(0):44..50
                 callee Function FileId(0) 38..52 44..50 : FileId(0):44..50"#]],
             expect![[]],
diff --git a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
index 2c983287d89..f58202a4213 100644
--- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
@@ -60,7 +60,7 @@ pub(crate) fn rewrite_links(
     let doc = Parser::new_with_broken_link_callback(markdown, MARKDOWN_OPTIONS, Some(&mut cb))
         .into_offset_iter();
 
-    let doc = map_links(doc, |target, title, range| {
+    let doc = map_links(doc, |target, title, range, link_type| {
         // This check is imperfect, there's some overlap between valid intra-doc links
         // and valid URLs so we choose to be too eager to try to resolve what might be
         // a URL.
@@ -78,7 +78,7 @@ pub(crate) fn rewrite_links(
                 .map(|(_, attr_id)| attr_id.is_inner_attr())
                 .unwrap_or(false);
             if let Some((target, title)) =
-                rewrite_intra_doc_link(db, definition, target, title, is_inner_doc)
+                rewrite_intra_doc_link(db, definition, target, title, is_inner_doc, link_type)
             {
                 (None, target, title)
             } else if let Some(target) = rewrite_url_link(db, definition, target) {
@@ -417,6 +417,7 @@ fn rewrite_intra_doc_link(
     target: &str,
     title: &str,
     is_inner_doc: bool,
+    link_type: LinkType,
 ) -> Option<(String, String)> {
     let (link, ns) = parse_intra_doc_link(target);
 
@@ -438,7 +439,21 @@ fn rewrite_intra_doc_link(
     url = url.join(&file).ok()?;
     url.set_fragment(frag);
 
-    Some((url.into(), strip_prefixes_suffixes(title).to_owned()))
+    // We want to strip the keyword prefix from the title, but only if the target is implicitly the same
+    // as the title.
+    let title = match link_type {
+        LinkType::Email
+        | LinkType::Autolink
+        | LinkType::Shortcut
+        | LinkType::Collapsed
+        | LinkType::Reference
+        | LinkType::Inline => title.to_owned(),
+        LinkType::ShortcutUnknown | LinkType::CollapsedUnknown | LinkType::ReferenceUnknown => {
+            strip_prefixes_suffixes(title).to_owned()
+        }
+    };
+
+    Some((url.into(), title))
 }
 
 /// Try to resolve path to local documentation via path-based links (i.e. `../gateway/struct.Shard.html`).
@@ -470,7 +485,7 @@ fn mod_path_of_def(db: &RootDatabase, def: Definition) -> Option<String> {
 /// Rewrites a markdown document, applying 'callback' to each link.
 fn map_links<'e>(
     events: impl Iterator<Item = (Event<'e>, Range<usize>)>,
-    callback: impl Fn(&str, &str, Range<usize>) -> (Option<LinkType>, String, String),
+    callback: impl Fn(&str, &str, Range<usize>, LinkType) -> (Option<LinkType>, String, String),
 ) -> impl Iterator<Item = Event<'e>> {
     let mut in_link = false;
     // holds the origin link target on start event and the rewritten one on end event
@@ -497,7 +512,7 @@ fn map_links<'e>(
         }
         Event::Text(s) if in_link => {
             let (link_type, link_target_s, link_name) =
-                callback(&end_link_target.take().unwrap(), &s, range);
+                callback(&end_link_target.take().unwrap(), &s, range, end_link_type.unwrap());
             end_link_target = Some(CowStr::Boxed(link_target_s.into()));
             if !matches!(end_link_type, Some(LinkType::Autolink)) {
                 end_link_type = link_type;
@@ -506,7 +521,7 @@ fn map_links<'e>(
         }
         Event::Code(s) if in_link => {
             let (link_type, link_target_s, link_name) =
-                callback(&end_link_target.take().unwrap(), &s, range);
+                callback(&end_link_target.take().unwrap(), &s, range, end_link_type.unwrap());
             end_link_target = Some(CowStr::Boxed(link_target_s.into()));
             if !matches!(end_link_type, Some(LinkType::Autolink)) {
                 end_link_type = link_type;
diff --git a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs
index 347da4e85b4..6820f99facf 100644
--- a/src/tools/rust-analyzer/crates/ide/src/file_structure.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/file_structure.rs
@@ -329,7 +329,7 @@ macro_rules! mcexp {
 #[deprecated]
 fn obsolete() {}
 
-#[deprecated(note = "for awhile")]
+#[deprecated(note = "for a while")]
 fn very_obsolete() {}
 
 // region: Some region name
@@ -608,8 +608,8 @@ fn let_statements() {
                     StructureNode {
                         parent: None,
                         label: "very_obsolete",
-                        navigation_range: 511..524,
-                        node_range: 473..529,
+                        navigation_range: 512..525,
+                        node_range: 473..530,
                         kind: SymbolKind(
                             Function,
                         ),
@@ -621,8 +621,8 @@ fn let_statements() {
                     StructureNode {
                         parent: None,
                         label: "Some region name",
-                        navigation_range: 531..558,
-                        node_range: 531..558,
+                        navigation_range: 532..559,
+                        node_range: 532..559,
                         kind: Region,
                         detail: None,
                         deprecated: false,
@@ -630,8 +630,8 @@ fn let_statements() {
                     StructureNode {
                         parent: None,
                         label: "m",
-                        navigation_range: 598..599,
-                        node_range: 573..636,
+                        navigation_range: 599..600,
+                        node_range: 574..637,
                         kind: SymbolKind(
                             Module,
                         ),
@@ -643,8 +643,8 @@ fn let_statements() {
                             22,
                         ),
                         label: "dontpanic",
-                        navigation_range: 573..593,
-                        node_range: 573..593,
+                        navigation_range: 574..594,
+                        node_range: 574..594,
                         kind: Region,
                         detail: None,
                         deprecated: false,
@@ -654,8 +654,8 @@ fn let_statements() {
                             22,
                         ),
                         label: "f",
-                        navigation_range: 605..606,
-                        node_range: 602..611,
+                        navigation_range: 606..607,
+                        node_range: 603..612,
                         kind: SymbolKind(
                             Function,
                         ),
@@ -669,8 +669,8 @@ fn let_statements() {
                             22,
                         ),
                         label: "g",
-                        navigation_range: 628..629,
-                        node_range: 612..634,
+                        navigation_range: 629..630,
+                        node_range: 613..635,
                         kind: SymbolKind(
                             Function,
                         ),
@@ -682,8 +682,8 @@ fn let_statements() {
                     StructureNode {
                         parent: None,
                         label: "extern \"C\"",
-                        navigation_range: 638..648,
-                        node_range: 638..651,
+                        navigation_range: 639..649,
+                        node_range: 639..652,
                         kind: ExternBlock,
                         detail: None,
                         deprecated: false,
@@ -691,8 +691,8 @@ fn let_statements() {
                     StructureNode {
                         parent: None,
                         label: "let_statements",
-                        navigation_range: 656..670,
-                        node_range: 653..813,
+                        navigation_range: 657..671,
+                        node_range: 654..814,
                         kind: SymbolKind(
                             Function,
                         ),
@@ -706,8 +706,8 @@ fn let_statements() {
                             27,
                         ),
                         label: "x",
-                        navigation_range: 683..684,
-                        node_range: 679..690,
+                        navigation_range: 684..685,
+                        node_range: 680..691,
                         kind: SymbolKind(
                             Local,
                         ),
@@ -719,8 +719,8 @@ fn let_statements() {
                             27,
                         ),
                         label: "mut y",
-                        navigation_range: 699..704,
-                        node_range: 695..709,
+                        navigation_range: 700..705,
+                        node_range: 696..710,
                         kind: SymbolKind(
                             Local,
                         ),
@@ -732,8 +732,8 @@ fn let_statements() {
                             27,
                         ),
                         label: "Foo { .. }",
-                        navigation_range: 718..740,
-                        node_range: 714..753,
+                        navigation_range: 719..741,
+                        node_range: 715..754,
                         kind: SymbolKind(
                             Local,
                         ),
@@ -745,8 +745,8 @@ fn let_statements() {
                             27,
                         ),
                         label: "_",
-                        navigation_range: 803..804,
-                        node_range: 799..811,
+                        navigation_range: 804..805,
+                        node_range: 800..812,
                         kind: SymbolKind(
                             Local,
                         ),
diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
index fd465f31d43..29fc68bb50f 100644
--- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
@@ -1082,7 +1082,7 @@ macro_rules! define_fn {
 }
 
   define_fn!();
-//^^^^^^^^^^^^^
+//^^^^^^^^^^
 fn bar() {
    $0foo();
 }
@@ -3228,7 +3228,7 @@ mod bar {
     use crate::m;
 
     m!();
- // ^^^^^
+ // ^^
 
     fn qux() {
         Foo$0;
@@ -3851,4 +3851,76 @@ fn main() {
 "#,
         );
     }
+
+    #[test]
+    fn goto_const_from_match_pat_with_tuple_struct() {
+        check(
+            r#"
+struct Tag(u8);
+struct Path {}
+
+const Path: u8 = 0;
+   // ^^^^
+fn main() {
+    match Tag(Path) {
+        Tag(Path$0) => {}
+        _ => {}
+    }
+}
+
+"#,
+        );
+    }
+
+    #[test]
+    fn goto_const_from_match_pat() {
+        check(
+            r#"
+type T1 = u8;
+const T1: u8 = 0;
+   // ^^
+fn main() {
+    let x = 0;
+    match x {
+        T1$0 => {}
+        _ => {}
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn goto_struct_from_match_pat() {
+        check(
+            r#"
+struct T1;
+    // ^^
+fn main() {
+    let x = 0;
+    match x {
+        T1$0 => {}
+        _ => {}
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn no_goto_trait_from_match_pat() {
+        check(
+            r#"
+trait T1 {}
+fn main() {
+    let x = 0;
+    match x {
+        T1$0 => {}
+     // ^^
+        _ => {}
+    }
+}
+"#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
index a281a491525..f63499aa0fd 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
@@ -10927,3 +10927,34 @@ fn main() {
         "#]],
     );
 }
+
+#[test]
+fn keyword_inside_link() {
+    check(
+        r#"
+enum Foo {
+    MacroExpansion,
+}
+
+/// I return a [macro expansion](Foo::MacroExpansion).
+fn bar$0() -> Foo {
+    Foo::MacroExpansion
+}
+    "#,
+        expect![[r#"
+            *bar*
+
+            ```rust
+            ra_test_fixture
+            ```
+
+            ```rust
+            fn bar() -> Foo
+            ```
+
+            ---
+
+            I return a [macro expansion](https://docs.rs/ra_test_fixture/*/ra_test_fixture/enum.Foo.html#variant.MacroExpansion).
+        "#]],
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs
index f2844a2eaa6..49b43fc37f2 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/adjustment.rs
@@ -109,50 +109,90 @@ pub(super) fn hints(
         }
         has_adjustments = true;
 
-        // FIXME: Add some nicer tooltips to each of these
-        let (text, coercion) = match kind {
+        let (text, coercion, detailed_tooltip) = match kind {
             Adjust::NeverToAny if config.adjustment_hints == AdjustmentHints::Always => {
                 allow_edit = false;
-                ("<never-to-any>", "never to any")
-            }
-            Adjust::Deref(None) => ("*", "dereference"),
-            Adjust::Deref(Some(OverloadedDeref(Mutability::Shared))) => {
-                ("*", "`Deref` dereference")
-            }
-            Adjust::Deref(Some(OverloadedDeref(Mutability::Mut))) => {
-                ("*", "`DerefMut` dereference")
-            }
-            Adjust::Borrow(AutoBorrow::Ref(Mutability::Shared)) => ("&", "borrow"),
-            Adjust::Borrow(AutoBorrow::Ref(Mutability::Mut)) => ("&mut ", "unique borrow"),
-            Adjust::Borrow(AutoBorrow::RawPtr(Mutability::Shared)) => {
-                ("&raw const ", "const pointer borrow")
-            }
-            Adjust::Borrow(AutoBorrow::RawPtr(Mutability::Mut)) => {
-                ("&raw mut ", "mut pointer borrow")
+                (
+                    "<never-to-any>",
+                    "never to any",
+                    "Coerces the never type `!` into any other type. This happens in code paths that never return, like after `panic!()` or `return`.",
+                )
             }
+            Adjust::Deref(None) => (
+                "*",
+                "dereference",
+                "Built-in dereference of a reference to access the underlying value. The compiler inserts `*` to get the value from `&T`.",
+            ),
+            Adjust::Deref(Some(OverloadedDeref(Mutability::Shared))) => (
+                "*",
+                "`Deref` dereference",
+                "Dereference via the `Deref` trait. Used for types like `Box<T>` or `Rc<T>` so they act like plain `T`.",
+            ),
+            Adjust::Deref(Some(OverloadedDeref(Mutability::Mut))) => (
+                "*",
+                "`DerefMut` dereference",
+                "Mutable dereference using the `DerefMut` trait. Enables smart pointers to give mutable access to their inner values.",
+            ),
+            Adjust::Borrow(AutoBorrow::Ref(Mutability::Shared)) => (
+                "&",
+                "shared borrow",
+                "Inserts `&` to create a shared reference. Lets you use a value without moving or cloning it.",
+            ),
+            Adjust::Borrow(AutoBorrow::Ref(Mutability::Mut)) => (
+                "&mut ",
+                "mutable borrow",
+                "Inserts `&mut` to create a unique, mutable reference. Lets you modify a value without taking ownership.",
+            ),
+            Adjust::Borrow(AutoBorrow::RawPtr(Mutability::Shared)) => (
+                "&raw const ",
+                "const raw pointer",
+                "Converts a reference to a raw const pointer `*const T`. Often used when working with FFI or unsafe code.",
+            ),
+            Adjust::Borrow(AutoBorrow::RawPtr(Mutability::Mut)) => (
+                "&raw mut ",
+                "mut raw pointer",
+                "Converts a mutable reference to a raw mutable pointer `*mut T`. Allows mutation in unsafe contexts.",
+            ),
             // some of these could be represented via `as` casts, but that's not too nice and
             // handling everything as a prefix expr makes the `(` and `)` insertion easier
             Adjust::Pointer(cast) if config.adjustment_hints == AdjustmentHints::Always => {
                 allow_edit = false;
                 match cast {
-                    PointerCast::ReifyFnPointer => {
-                        ("<fn-item-to-fn-pointer>", "fn item to fn pointer")
-                    }
+                    PointerCast::ReifyFnPointer => (
+                        "<fn-item-to-fn-pointer>",
+                        "fn item to fn pointer",
+                        "Converts a named function to a function pointer `fn()`. Useful when passing functions as values.",
+                    ),
                     PointerCast::UnsafeFnPointer => (
                         "<safe-fn-pointer-to-unsafe-fn-pointer>",
                         "safe fn pointer to unsafe fn pointer",
+                        "Coerces a safe function pointer to an unsafe one. Allows calling it in an unsafe context.",
+                    ),
+                    PointerCast::ClosureFnPointer(Safety::Unsafe) => (
+                        "<closure-to-unsafe-fn-pointer>",
+                        "closure to unsafe fn pointer",
+                        "Converts a non-capturing closure to an unsafe function pointer. Required for use in `extern` or unsafe APIs.",
+                    ),
+                    PointerCast::ClosureFnPointer(Safety::Safe) => (
+                        "<closure-to-fn-pointer>",
+                        "closure to fn pointer",
+                        "Converts a non-capturing closure to a function pointer. Lets closures behave like plain functions.",
+                    ),
+                    PointerCast::MutToConstPointer => (
+                        "<mut-ptr-to-const-ptr>",
+                        "mut ptr to const ptr",
+                        "Coerces `*mut T` to `*const T`. Safe because const pointers restrict what you can do.",
+                    ),
+                    PointerCast::ArrayToPointer => (
+                        "<array-ptr-to-element-ptr>",
+                        "array to pointer",
+                        "Converts an array to a pointer to its first element. Similar to how arrays decay to pointers in C.",
+                    ),
+                    PointerCast::Unsize => (
+                        "<unsize>",
+                        "unsize coercion",
+                        "Converts a sized type to an unsized one. Used for things like turning arrays into slices or concrete types into trait objects.",
                     ),
-                    PointerCast::ClosureFnPointer(Safety::Unsafe) => {
-                        ("<closure-to-unsafe-fn-pointer>", "closure to unsafe fn pointer")
-                    }
-                    PointerCast::ClosureFnPointer(Safety::Safe) => {
-                        ("<closure-to-fn-pointer>", "closure to fn pointer")
-                    }
-                    PointerCast::MutToConstPointer => {
-                        ("<mut-ptr-to-const-ptr>", "mut ptr to const ptr")
-                    }
-                    PointerCast::ArrayToPointer => ("<array-ptr-to-element-ptr>", ""),
-                    PointerCast::Unsize => ("<unsize>", "unsize"),
                 }
             }
             _ => continue,
@@ -162,9 +202,11 @@ pub(super) fn hints(
             linked_location: None,
             tooltip: Some(config.lazy_tooltip(|| {
                 InlayTooltip::Markdown(format!(
-                    "`{}` → `{}` ({coercion} coercion)",
+                    "`{}` → `{}`\n\n**{}**\n\n{}",
                     source.display(sema.db, display_target),
                     target.display(sema.db, display_target),
+                    coercion,
+                    detailed_tooltip
                 ))
             })),
         };
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs
index d2216e66ccb..05253b67948 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/closing_brace.rs
@@ -91,8 +91,6 @@ pub(super) fn hints(
         match_ast! {
             match parent {
                 ast::Fn(it) => {
-                    // FIXME: this could include parameters, but `HirDisplay` prints too much info
-                    // and doesn't respect the max length either, so the hints end up way too long
                     (format!("fn {}", it.name()?), it.name().map(name))
                 },
                 ast::Static(it) => (format!("static {}", it.name()?), it.name().map(name)),
diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs
index 4c7c597e684..7dc18141bdb 100644
--- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs
@@ -844,7 +844,7 @@ pub(crate) fn orig_range_with_focus_r(
                             // *should* contain the name
                             _ => {
                                 let kind = call_kind();
-                                let range = kind.clone().original_call_range_with_body(db);
+                                let range = kind.clone().original_call_range_with_input(db);
                                 //If the focus range is in the attribute/derive body, we
                                 // need to point the call site to the entire body, if not, fall back
                                 // to the name range of the attribute/derive call
diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs
index f48150b369f..9d1a5bae96f 100644
--- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs
@@ -351,7 +351,7 @@ pub(crate) fn runnable_fn(
     )
     .call_site();
 
-    let file_range = fn_source.syntax().original_file_range_with_macro_call_body(sema.db);
+    let file_range = fn_source.syntax().original_file_range_with_macro_call_input(sema.db);
     let update_test =
         UpdateTest::find_snapshot_macro(sema, &fn_source.file_syntax(sema.db), file_range);
 
@@ -425,7 +425,7 @@ pub(crate) fn runnable_impl(
 
     let impl_source = sema.source(*def)?;
     let impl_syntax = impl_source.syntax();
-    let file_range = impl_syntax.original_file_range_with_macro_call_body(sema.db);
+    let file_range = impl_syntax.original_file_range_with_macro_call_input(sema.db);
     let update_test =
         UpdateTest::find_snapshot_macro(sema, &impl_syntax.file_syntax(sema.db), file_range);
 
@@ -1241,10 +1241,10 @@ generate_main!();
                 [
                     "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 0..345, name: \"\", kind: Module })",
                     "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 282..312, focus_range: 286..291, name: \"tests\", kind: Module, description: \"mod tests\" })",
-                    "(Test, NavigationTarget { file_id: FileId(0), full_range: 298..310, name: \"foo_test\", kind: Function })",
-                    "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 313..326, name: \"tests2\", kind: Module, description: \"mod tests2\" }, true)",
-                    "(Test, NavigationTarget { file_id: FileId(0), full_range: 313..326, name: \"foo_test2\", kind: Function }, true)",
-                    "(Bin, NavigationTarget { file_id: FileId(0), full_range: 327..344, name: \"main\", kind: Function })",
+                    "(Test, NavigationTarget { file_id: FileId(0), full_range: 298..307, name: \"foo_test\", kind: Function })",
+                    "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 313..323, name: \"tests2\", kind: Module, description: \"mod tests2\" }, true)",
+                    "(Test, NavigationTarget { file_id: FileId(0), full_range: 313..323, name: \"foo_test2\", kind: Function }, true)",
+                    "(Bin, NavigationTarget { file_id: FileId(0), full_range: 327..341, name: \"main\", kind: Function })",
                 ]
             "#]],
         );
@@ -1272,10 +1272,10 @@ foo!();
 "#,
             expect![[r#"
                 [
-                    "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 210..217, name: \"foo_tests\", kind: Module, description: \"mod foo_tests\" }, true)",
-                    "(Test, NavigationTarget { file_id: FileId(0), full_range: 210..217, name: \"foo0\", kind: Function }, true)",
-                    "(Test, NavigationTarget { file_id: FileId(0), full_range: 210..217, name: \"foo1\", kind: Function }, true)",
-                    "(Test, NavigationTarget { file_id: FileId(0), full_range: 210..217, name: \"foo2\", kind: Function }, true)",
+                    "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 210..214, name: \"foo_tests\", kind: Module, description: \"mod foo_tests\" }, true)",
+                    "(Test, NavigationTarget { file_id: FileId(0), full_range: 210..214, name: \"foo0\", kind: Function }, true)",
+                    "(Test, NavigationTarget { file_id: FileId(0), full_range: 210..214, name: \"foo1\", kind: Function }, true)",
+                    "(Test, NavigationTarget { file_id: FileId(0), full_range: 210..214, name: \"foo2\", kind: Function }, true)",
                 ]
             "#]],
         );
diff --git a/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs b/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs
index 7985279679c..25deffe10eb 100644
--- a/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/view_crate_graph.rs
@@ -79,7 +79,7 @@ impl<'a> dot::Labeller<'a, Crate, Edge<'a>> for DotCrateGraph<'_> {
     }
 
     fn node_id(&'a self, n: &Crate) -> Id<'a> {
-        let id = n.as_id().as_u32();
+        let id = n.as_id().index();
         Id::new(format!("_{id:?}")).unwrap()
     }
 
diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
index 52f59679b58..26ee698af08 100644
--- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
@@ -11,7 +11,7 @@ use hir_expand::proc_macro::{
 };
 use ide_db::{
     ChangeWithProcMacros, FxHashMap, RootDatabase,
-    base_db::{CrateGraphBuilder, Env, SourceRoot, SourceRootId},
+    base_db::{CrateGraphBuilder, Env, ProcMacroLoadingError, SourceRoot, SourceRootId},
     prime_caches,
 };
 use itertools::Itertools;
@@ -69,6 +69,23 @@ pub fn load_workspace(
     extra_env: &FxHashMap<String, Option<String>>,
     load_config: &LoadCargoConfig,
 ) -> anyhow::Result<(RootDatabase, vfs::Vfs, Option<ProcMacroClient>)> {
+    let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::<u16>().ok());
+    let mut db = RootDatabase::new(lru_cap);
+
+    let (vfs, proc_macro_server) = load_workspace_into_db(ws, extra_env, load_config, &mut db)?;
+
+    Ok((db, vfs, proc_macro_server))
+}
+
+// This variant of `load_workspace` allows deferring the loading of rust-analyzer
+// into an existing database, which is useful in certain third-party scenarios,
+// now that `salsa` supports extending foreign databases (e.g. `RootDatabase`).
+pub fn load_workspace_into_db(
+    ws: ProjectWorkspace,
+    extra_env: &FxHashMap<String, Option<String>>,
+    load_config: &LoadCargoConfig,
+    db: &mut RootDatabase,
+) -> anyhow::Result<(vfs::Vfs, Option<ProcMacroClient>)> {
     let (sender, receiver) = unbounded();
     let mut vfs = vfs::Vfs::default();
     let mut loader = {
@@ -78,23 +95,27 @@ pub fn load_workspace(
 
     tracing::debug!(?load_config, "LoadCargoConfig");
     let proc_macro_server = match &load_config.with_proc_macro_server {
-        ProcMacroServerChoice::Sysroot => ws
-            .find_sysroot_proc_macro_srv()
-            .and_then(|it| ProcMacroClient::spawn(&it, extra_env).map_err(Into::into))
-            .map_err(|e| (e, true)),
+        ProcMacroServerChoice::Sysroot => ws.find_sysroot_proc_macro_srv().map(|it| {
+            it.and_then(|it| ProcMacroClient::spawn(&it, extra_env).map_err(Into::into)).map_err(
+                |e| ProcMacroLoadingError::ProcMacroSrvError(e.to_string().into_boxed_str()),
+            )
+        }),
         ProcMacroServerChoice::Explicit(path) => {
-            ProcMacroClient::spawn(path, extra_env).map_err(Into::into).map_err(|e| (e, true))
-        }
-        ProcMacroServerChoice::None => {
-            Err((anyhow::format_err!("proc macro server disabled"), false))
+            Some(ProcMacroClient::spawn(path, extra_env).map_err(|e| {
+                ProcMacroLoadingError::ProcMacroSrvError(e.to_string().into_boxed_str())
+            }))
         }
+        ProcMacroServerChoice::None => Some(Err(ProcMacroLoadingError::Disabled)),
     };
     match &proc_macro_server {
-        Ok(server) => {
-            tracing::info!(path=%server.server_path(), "Proc-macro server started")
+        Some(Ok(server)) => {
+            tracing::info!(manifest=%ws.manifest_or_root(), path=%server.server_path(), "Proc-macro server started")
         }
-        Err((e, _)) => {
-            tracing::info!(%e, "Failed to start proc-macro server")
+        Some(Err(e)) => {
+            tracing::info!(manifest=%ws.manifest_or_root(), %e, "Failed to start proc-macro server")
+        }
+        None => {
+            tracing::info!(manifest=%ws.manifest_or_root(), "No proc-macro server started")
         }
     }
 
@@ -111,22 +132,24 @@ pub fn load_workspace(
     );
     let proc_macros = {
         let proc_macro_server = match &proc_macro_server {
-            Ok(it) => Ok(it),
-            Err((e, hard_err)) => Err((e.to_string(), *hard_err)),
+            Some(Ok(it)) => Ok(it),
+            Some(Err(e)) => {
+                Err(ProcMacroLoadingError::ProcMacroSrvError(e.to_string().into_boxed_str()))
+            }
+            None => Err(ProcMacroLoadingError::ProcMacroSrvError(
+                "proc-macro-srv is not running, workspace is missing a sysroot".into(),
+            )),
         };
         proc_macros
             .into_iter()
             .map(|(crate_id, path)| {
                 (
                     crate_id,
-                    path.map_or_else(
-                        |e| Err((e, true)),
-                        |(_, path)| {
-                            proc_macro_server.as_ref().map_err(Clone::clone).and_then(
-                                |proc_macro_server| load_proc_macro(proc_macro_server, &path, &[]),
-                            )
-                        },
-                    ),
+                    path.map_or_else(Err, |(_, path)| {
+                        proc_macro_server.as_ref().map_err(Clone::clone).and_then(
+                            |proc_macro_server| load_proc_macro(proc_macro_server, &path, &[]),
+                        )
+                    }),
                 )
             })
             .collect()
@@ -139,18 +162,20 @@ pub fn load_workspace(
         version: 0,
     });
 
-    let db = load_crate_graph(
+    load_crate_graph_into_db(
         crate_graph,
         proc_macros,
         project_folders.source_root_config,
         &mut vfs,
         &receiver,
+        db,
     );
 
     if load_config.prefill_caches {
-        prime_caches::parallel_prime_caches(&db, 1, &|_| ());
+        prime_caches::parallel_prime_caches(db, 1, &|_| ());
     }
-    Ok((db, vfs, proc_macro_server.ok()))
+
+    Ok((vfs, proc_macro_server.and_then(Result::ok)))
 }
 
 #[derive(Default)]
@@ -391,11 +416,13 @@ pub fn load_proc_macro(
     path: &AbsPath,
     ignored_macros: &[Box<str>],
 ) -> ProcMacroLoadResult {
-    let res: Result<Vec<_>, String> = (|| {
+    let res: Result<Vec<_>, _> = (|| {
         let dylib = MacroDylib::new(path.to_path_buf());
-        let vec = server.load_dylib(dylib).map_err(|e| format!("{e}"))?;
+        let vec = server.load_dylib(dylib).map_err(|e| {
+            ProcMacroLoadingError::ProcMacroSrvError(format!("{e}").into_boxed_str())
+        })?;
         if vec.is_empty() {
-            return Err("proc macro library returned no proc macros".to_owned());
+            return Err(ProcMacroLoadingError::NoProcMacros);
         }
         Ok(vec
             .into_iter()
@@ -412,20 +439,19 @@ pub fn load_proc_macro(
         }
         Err(e) => {
             tracing::warn!("proc-macro loading for {path} failed: {e}");
-            Err((e, true))
+            Err(e)
         }
     }
 }
 
-fn load_crate_graph(
+fn load_crate_graph_into_db(
     crate_graph: CrateGraphBuilder,
     proc_macros: ProcMacrosBuilder,
     source_root_config: SourceRootConfig,
     vfs: &mut vfs::Vfs,
     receiver: &Receiver<vfs::loader::Message>,
-) -> RootDatabase {
-    let lru_cap = std::env::var("RA_LRU_CAP").ok().and_then(|it| it.parse::<u16>().ok());
-    let mut db = RootDatabase::new(lru_cap);
+    db: &mut RootDatabase,
+) {
     let mut analysis_change = ChangeWithProcMacros::default();
 
     db.enable_proc_attr_macros();
@@ -462,7 +488,6 @@ fn load_crate_graph(
     analysis_change.set_proc_macros(proc_macros);
 
     db.apply_change(analysis_change);
-    db
 }
 
 fn expander_to_proc_macro(
diff --git a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs
index e6c92dec681..bff9acd78fa 100644
--- a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs
@@ -11,8 +11,8 @@
 use std::ops;
 
 use rustc_literal_escaper::{
-    EscapeError, Mode, unescape_byte, unescape_byte_str, unescape_c_str, unescape_char,
-    unescape_str,
+    unescape_byte, unescape_byte_str, unescape_c_str, unescape_char, unescape_str, EscapeError,
+    Mode,
 };
 
 use crate::{
@@ -44,7 +44,9 @@ impl<'a> LexedStr<'a> {
 
         // Re-create the tokenizer from scratch every token because `GuardedStrPrefix` is one token in the lexer
         // but we want to split it to two in edition <2024.
-        while let Some(token) = rustc_lexer::tokenize(&text[conv.offset..]).next() {
+        while let Some(token) =
+            rustc_lexer::tokenize(&text[conv.offset..], rustc_lexer::FrontmatterAllowed::No).next()
+        {
             let token_text = &text[conv.offset..][..token.len as usize];
 
             conv.extend_token(&token.kind, token_text);
@@ -58,7 +60,7 @@ impl<'a> LexedStr<'a> {
             return None;
         }
 
-        let token = rustc_lexer::tokenize(text).next()?;
+        let token = rustc_lexer::tokenize(text, rustc_lexer::FrontmatterAllowed::No).next()?;
         if token.len as usize != text.len() {
             return None;
         }
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs
index dd576f23ae9..662f6257642 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl.rs
@@ -121,7 +121,7 @@ pub(super) fn literal_from_str<Span: Copy>(
     use proc_macro::bridge::LitKind;
     use rustc_lexer::{LiteralKind, Token, TokenKind};
 
-    let mut tokens = rustc_lexer::tokenize(s);
+    let mut tokens = rustc_lexer::tokenize(s, rustc_lexer::FrontmatterAllowed::No);
     let minus_or_lit = tokens.next().unwrap_or(Token { kind: TokenKind::Eof, len: 0 });
 
     let lit = if minus_or_lit.kind == TokenKind::Minus {
diff --git a/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs
index bbaa8f4f292..499caa622c4 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/build_dependencies.rs
@@ -312,7 +312,9 @@ impl WorkspaceBuildScripts {
                 match message {
                     Message::BuildScriptExecuted(mut message) => {
                         with_output_for(&message.package_id.repr, &mut |name, data| {
-                            progress(format!("running build-script: {name}"));
+                            progress(format!(
+                                "building compile-time-deps: build script {name} run"
+                            ));
                             let cfgs = {
                                 let mut acc = Vec::new();
                                 for cfg in &message.cfgs {
@@ -343,7 +345,9 @@ impl WorkspaceBuildScripts {
                     }
                     Message::CompilerArtifact(message) => {
                         with_output_for(&message.package_id.repr, &mut |name, data| {
-                            progress(format!("building proc-macros: {name}"));
+                            progress(format!(
+                                "building compile-time-deps: proc-macro {name} built"
+                            ));
                             if message.target.kind.contains(&cargo_metadata::TargetKind::ProcMacro)
                             {
                                 // Skip rmeta file
@@ -409,13 +413,6 @@ impl WorkspaceBuildScripts {
                     cmd.arg("--target-dir").arg(target_dir);
                 }
 
-                // --all-targets includes tests, benches and examples in addition to the
-                // default lib and bins. This is an independent concept from the --target
-                // flag below.
-                if config.all_targets {
-                    cmd.arg("--all-targets");
-                }
-
                 if let Some(target) = &config.target {
                     cmd.args(["--target", target]);
                 }
@@ -463,14 +460,26 @@ impl WorkspaceBuildScripts {
                     cmd.env("__CARGO_TEST_CHANNEL_OVERRIDE_DO_NOT_USE_THIS", "nightly");
                     cmd.arg("-Zunstable-options");
                     cmd.arg("--compile-time-deps");
-                } else if config.wrap_rustc_in_build_scripts {
-                    // Setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself. We use
-                    // that to compile only proc macros and build scripts during the initial
-                    // `cargo check`.
-                    // We don't need this if we are using `--compile-time-deps` flag.
-                    let myself = std::env::current_exe()?;
-                    cmd.env("RUSTC_WRAPPER", myself);
-                    cmd.env("RA_RUSTC_WRAPPER", "1");
+                    // we can pass this unconditionally, because we won't actually build the
+                    // binaries, and as such, this will succeed even on targets without libtest
+                    cmd.arg("--all-targets");
+                } else {
+                    // --all-targets includes tests, benches and examples in addition to the
+                    // default lib and bins. This is an independent concept from the --target
+                    // flag below.
+                    if config.all_targets {
+                        cmd.arg("--all-targets");
+                    }
+
+                    if config.wrap_rustc_in_build_scripts {
+                        // Setup RUSTC_WRAPPER to point to `rust-analyzer` binary itself. We use
+                        // that to compile only proc macros and build scripts during the initial
+                        // `cargo check`.
+                        // We don't need this if we are using `--compile-time-deps` flag.
+                        let myself = std::env::current_exe()?;
+                        cmd.env("RUSTC_WRAPPER", myself);
+                        cmd.env("RA_RUSTC_WRAPPER", "1");
+                    }
                 }
                 Ok(cmd)
             }
diff --git a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
index 4b34fc00711..9f19260d309 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
@@ -163,18 +163,18 @@ impl Sysroot {
         }
     }
 
-    pub fn discover_proc_macro_srv(&self) -> anyhow::Result<AbsPathBuf> {
-        let Some(root) = self.root() else {
-            return Err(anyhow::format_err!("no sysroot",));
-        };
-        ["libexec", "lib"]
-            .into_iter()
-            .map(|segment| root.join(segment).join("rust-analyzer-proc-macro-srv"))
-            .find_map(|server_path| probe_for_binary(server_path.into()))
-            .map(AbsPathBuf::assert)
-            .ok_or_else(|| {
-                anyhow::format_err!("cannot find proc-macro server in sysroot `{}`", root)
-            })
+    pub fn discover_proc_macro_srv(&self) -> Option<anyhow::Result<AbsPathBuf>> {
+        let root = self.root()?;
+        Some(
+            ["libexec", "lib"]
+                .into_iter()
+                .map(|segment| root.join(segment).join("rust-analyzer-proc-macro-srv"))
+                .find_map(|server_path| probe_for_binary(server_path.into()))
+                .map(AbsPathBuf::assert)
+                .ok_or_else(|| {
+                    anyhow::format_err!("cannot find proc-macro server in sysroot `{}`", root)
+                }),
+        )
     }
 
     fn assemble(
@@ -209,6 +209,7 @@ impl Sysroot {
     pub fn load_workspace(
         &self,
         sysroot_source_config: &RustSourceWorkspaceConfig,
+        no_deps: bool,
         current_dir: &AbsPath,
         progress: &dyn Fn(String),
     ) -> Option<RustLibSrcWorkspace> {
@@ -224,6 +225,7 @@ impl Sysroot {
                     &library_manifest,
                     current_dir,
                     cargo_config,
+                    no_deps,
                     progress,
                 ) {
                     Ok(loaded) => return Some(loaded),
@@ -318,6 +320,7 @@ impl Sysroot {
         library_manifest: &ManifestPath,
         current_dir: &AbsPath,
         cargo_config: &CargoMetadataConfig,
+        no_deps: bool,
         progress: &dyn Fn(String),
     ) -> Result<RustLibSrcWorkspace> {
         tracing::debug!("Loading library metadata: {library_manifest}");
@@ -333,7 +336,7 @@ impl Sysroot {
             current_dir,
             &cargo_config,
             self,
-            false,
+            no_deps,
             // Make sure we never attempt to write to the sysroot
             true,
             progress,
diff --git a/src/tools/rust-analyzer/crates/project-model/src/tests.rs b/src/tools/rust-analyzer/crates/project-model/src/tests.rs
index 4f11af2d06c..f229e9a650d 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs
@@ -240,7 +240,7 @@ fn smoke_test_real_sysroot_cargo() {
     let cwd = AbsPathBuf::assert_utf8(temp_dir().join("smoke_test_real_sysroot_cargo"));
     std::fs::create_dir_all(&cwd).unwrap();
     let loaded_sysroot =
-        sysroot.load_workspace(&RustSourceWorkspaceConfig::default_cargo(), &cwd, &|_| ());
+        sysroot.load_workspace(&RustSourceWorkspaceConfig::default_cargo(), false, &cwd, &|_| ());
     if let Some(loaded_sysroot) = loaded_sysroot {
         sysroot.set_workspace(loaded_sysroot);
     }
diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
index 5bc64df535c..43db84b4fa3 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
@@ -7,8 +7,8 @@ use std::{collections::VecDeque, fmt, fs, iter, ops::Deref, sync, thread};
 use anyhow::Context;
 use base_db::{
     CrateBuilderId, CrateDisplayName, CrateGraphBuilder, CrateName, CrateOrigin,
-    CrateWorkspaceData, DependencyBuilder, Env, LangCrateOrigin, ProcMacroPaths,
-    TargetLayoutLoadResult,
+    CrateWorkspaceData, DependencyBuilder, Env, LangCrateOrigin, ProcMacroLoadingError,
+    ProcMacroPaths, TargetLayoutLoadResult,
 };
 use cfg::{CfgAtom, CfgDiff, CfgOptions};
 use intern::{Symbol, sym};
@@ -391,6 +391,7 @@ impl ProjectWorkspace {
                         toolchain.clone(),
                         target_dir.clone(),
                     )),
+                    config.no_deps,
                     workspace_dir,
                     progress,
                 )
@@ -499,6 +500,7 @@ impl ProjectWorkspace {
                 if let Some(sysroot_project) = sysroot_project {
                     sysroot.load_workspace(
                         &RustSourceWorkspaceConfig::Json(*sysroot_project),
+                        config.no_deps,
                         project_root,
                         progress,
                     )
@@ -510,6 +512,7 @@ impl ProjectWorkspace {
                             toolchain.clone(),
                             target_dir,
                         )),
+                        config.no_deps,
                         project_root,
                         progress,
                     )
@@ -570,6 +573,7 @@ impl ProjectWorkspace {
                 toolchain.clone(),
                 target_dir.clone(),
             )),
+            config.no_deps,
             dir,
             &|_| (),
         );
@@ -744,7 +748,7 @@ impl ProjectWorkspace {
         }
     }
 
-    pub fn find_sysroot_proc_macro_srv(&self) -> anyhow::Result<AbsPathBuf> {
+    pub fn find_sysroot_proc_macro_srv(&self) -> Option<anyhow::Result<AbsPathBuf>> {
         self.sysroot.discover_proc_macro_srv()
     }
 
@@ -1641,11 +1645,11 @@ fn add_target_crate_root(
             Some((BuildScriptOutput { proc_macro_dylib_path, .. }, has_errors)) => {
                 match proc_macro_dylib_path {
                     Some(path) => Ok((cargo_name.to_owned(), path.clone())),
-                    None if has_errors => Err("failed to build proc-macro".to_owned()),
-                    None => Err("proc-macro crate build data is missing dylib path".to_owned()),
+                    None if has_errors => Err(ProcMacroLoadingError::FailedToBuild),
+                    None => Err(ProcMacroLoadingError::MissingDylibPath),
                 }
             }
-            None => Err("build scripts have not been built".to_owned()),
+            None => Err(ProcMacroLoadingError::NotYetBuilt),
         };
         proc_macros.insert(crate_id, proc_macro);
     }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs
index 740fcd81ea9..f97bf832442 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs
@@ -75,8 +75,12 @@ impl Tester {
         };
 
         let mut sysroot = Sysroot::discover(tmp_file.parent().unwrap(), &cargo_config.extra_env);
-        let loaded_sysroot =
-            sysroot.load_workspace(&RustSourceWorkspaceConfig::default_cargo(), &path, &|_| ());
+        let loaded_sysroot = sysroot.load_workspace(
+            &RustSourceWorkspaceConfig::default_cargo(),
+            false,
+            &path,
+            &|_| (),
+        );
         if let Some(loaded_sysroot) = loaded_sysroot {
             sysroot.set_workspace(loaded_sysroot);
         }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
index d258c5d8191..37f83f6dee6 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
@@ -25,7 +25,7 @@ impl flags::Scip {
         eprintln!("Generating SCIP start...");
         let now = Instant::now();
 
-        let no_progress = &|s| (eprintln!("rust-analyzer: Loading {s}"));
+        let no_progress = &|s| eprintln!("rust-analyzer: Loading {s}");
         let root =
             vfs::AbsPathBuf::assert_utf8(std::env::current_dir()?.join(&self.path)).normalize();
 
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
index e716d140752..51d4c29aa74 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -1526,7 +1526,7 @@ impl Config {
         CompletionConfig {
             enable_postfix_completions: self.completion_postfix_enable(source_root).to_owned(),
             enable_imports_on_the_fly: self.completion_autoimport_enable(source_root).to_owned()
-                && self.caps.completion_item_edit_resolve(),
+                && self.caps.has_completion_item_resolve_additionalTextEdits(),
             enable_self_on_the_fly: self.completion_autoself_enable(source_root).to_owned(),
             enable_auto_iter: *self.completion_autoIter_enable(source_root),
             enable_auto_await: *self.completion_autoAwait_enable(source_root),
@@ -2355,10 +2355,6 @@ impl Config {
             .and_then(|it| it.version.as_ref())
     }
 
-    pub fn client_is_helix(&self) -> bool {
-        self.client_info.as_ref().map(|it| it.name == "helix").unwrap_or_default()
-    }
-
     pub fn client_is_neovim(&self) -> bool {
         self.client_info.as_ref().map(|it| it.name == "Neovim").unwrap_or_default()
     }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs
index 0e418240db0..91d37bd7c9e 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs
@@ -6,6 +6,7 @@ use std::{fmt, io, process::Command, time::Duration};
 use cargo_metadata::PackageId;
 use crossbeam_channel::{Receiver, Sender, select_biased, unbounded};
 use ide_db::FxHashSet;
+use itertools::Itertools;
 use paths::{AbsPath, AbsPathBuf, Utf8PathBuf};
 use rustc_hash::FxHashMap;
 use serde::Deserialize as _;
@@ -379,7 +380,11 @@ impl FlycheckActor {
                             package_id = msg.package_id.repr,
                             "artifact received"
                         );
-                        self.report_progress(Progress::DidCheckCrate(msg.target.name));
+                        self.report_progress(Progress::DidCheckCrate(format!(
+                            "{} ({})",
+                            msg.target.name,
+                            msg.target.kind.iter().format_with(", ", |kind, f| f(&kind)),
+                        )));
                         let package_id = Arc::new(msg.package_id);
                         if self.diagnostics_cleared_for.insert(package_id.clone()) {
                             tracing::trace!(
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
index a870232d4a0..62a28a1a685 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
@@ -101,7 +101,7 @@ pub(crate) struct GlobalState {
     pub(crate) last_reported_status: lsp_ext::ServerStatusParams,
 
     // proc macros
-    pub(crate) proc_macro_clients: Arc<[anyhow::Result<ProcMacroClient>]>,
+    pub(crate) proc_macro_clients: Arc<[Option<anyhow::Result<ProcMacroClient>>]>,
     pub(crate) build_deps_changed: bool,
 
     // Flycheck
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs
index 40d05567fcc..aea116e647d 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/dispatch.rs
@@ -6,7 +6,7 @@ use std::{
 
 use ide_db::base_db::{
     DbPanicContext,
-    salsa::{self, Cancelled, UnexpectedCycle},
+    salsa::{self, Cancelled},
 };
 use lsp_server::{ExtractError, Response, ResponseError};
 use serde::{Serialize, de::DeserializeOwned};
@@ -350,9 +350,6 @@ where
             if let Some(panic_message) = panic_message {
                 message.push_str(": ");
                 message.push_str(panic_message);
-            } else if let Some(cycle) = panic.downcast_ref::<UnexpectedCycle>() {
-                tracing::error!("{cycle}");
-                message.push_str(": unexpected cycle");
             } else if let Ok(cancelled) = panic.downcast::<Cancelled>() {
                 tracing::error!("Cancellation propagated out of salsa! This is a bug");
                 return Err(HandlerCancelledError::Inner(*cancelled));
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/capabilities.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/capabilities.rs
index 04e31f37fd2..f94e7486ff8 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/capabilities.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/capabilities.rs
@@ -42,7 +42,7 @@ pub fn server_capabilities(config: &Config) -> ServerCapabilities {
         hover_provider: Some(HoverProviderCapability::Simple(true)),
         completion_provider: Some(CompletionOptions {
             resolve_provider: if config.client_is_neovim() {
-                config.completion_item_edit_resolve().then_some(true)
+                config.has_completion_item_resolve_additionalTextEdits().then_some(true)
             } else {
                 Some(config.caps().completions_resolve_provider())
             },
@@ -207,8 +207,8 @@ impl ClientCapabilities {
         serde_json::from_value(self.0.experimental.as_ref()?.get(index)?.clone()).ok()
     }
 
-    /// Parses client capabilities and returns all completion resolve capabilities rust-analyzer supports.
-    pub fn completion_item_edit_resolve(&self) -> bool {
+    #[allow(non_snake_case)]
+    pub fn has_completion_item_resolve_additionalTextEdits(&self) -> bool {
         (|| {
             Some(
                 self.0
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
index 0c0438c4b8f..00cf890510d 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
@@ -783,9 +783,14 @@ impl GlobalState {
                             DiscoverProjectParam::Path(it) => DiscoverArgument::Path(it),
                         };
 
-                        let handle =
-                            discover.spawn(arg, &std::env::current_dir().unwrap()).unwrap();
-                        self.discover_handle = Some(handle);
+                        let handle = discover.spawn(
+                            arg,
+                            &std::env::current_dir()
+                                .expect("Failed to get cwd during project discovery"),
+                        );
+                        self.discover_handle = Some(handle.unwrap_or_else(|e| {
+                            panic!("Failed to spawn project discovery command: {e}")
+                        }));
                     }
                 }
             }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
index 133d5a6cf7a..e798aa6a8a6 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
@@ -18,7 +18,7 @@ use std::{iter, mem};
 use hir::{ChangeWithProcMacros, ProcMacrosBuilder, db::DefDatabase};
 use ide_db::{
     FxHashMap,
-    base_db::{CrateGraphBuilder, ProcMacroPaths, salsa::Durability},
+    base_db::{CrateGraphBuilder, ProcMacroLoadingError, ProcMacroPaths, salsa::Durability},
 };
 use itertools::Itertools;
 use load_cargo::{ProjectFolders, load_proc_macro};
@@ -194,8 +194,7 @@ impl GlobalState {
                 format_to!(message, "{e}");
             });
 
-            let proc_macro_clients =
-                self.proc_macro_clients.iter().map(Some).chain(iter::repeat_with(|| None));
+            let proc_macro_clients = self.proc_macro_clients.iter().chain(iter::repeat(&None));
 
             for (ws, proc_macro_client) in self.workspaces.iter().zip(proc_macro_clients) {
                 if let ProjectWorkspaceKind::Cargo { error: Some(error), .. }
@@ -252,7 +251,8 @@ impl GlobalState {
                             message.push_str("\n\n");
                         }
                     }
-                    _ => (),
+                    // sysroot was explicitly not set so we didn't discover a server
+                    None => {}
                 }
             }
         }
@@ -419,14 +419,11 @@ impl GlobalState {
             };
 
             let mut builder = ProcMacrosBuilder::default();
-            let proc_macro_clients = proc_macro_clients
-                .iter()
-                .map(|res| res.as_ref().map_err(|e| e.to_string()))
-                .chain(iter::repeat_with(|| Err("proc-macro-srv is not running".into())));
+            let proc_macro_clients = proc_macro_clients.iter().chain(iter::repeat(&None));
             for (client, paths) in proc_macro_clients.zip(paths) {
                 for (crate_id, res) in paths.iter() {
                     let expansion_res = match client {
-                        Ok(client) => match res {
+                        Some(Ok(client)) => match res {
                             Ok((crate_name, path)) => {
                                 progress(format!("loading proc-macros: {path}"));
                                 let ignored_proc_macros = ignored_proc_macros
@@ -438,9 +435,14 @@ impl GlobalState {
 
                                 load_proc_macro(client, path, ignored_proc_macros)
                             }
-                            Err(e) => Err((e.clone(), true)),
+                            Err(e) => Err(e.clone()),
                         },
-                        Err(ref e) => Err((e.clone(), true)),
+                        Some(Err(e)) => Err(ProcMacroLoadingError::ProcMacroSrvError(
+                            e.to_string().into_boxed_str(),
+                        )),
+                        None => Err(ProcMacroLoadingError::ProcMacroSrvError(
+                            "proc-macro-srv is not running".into(),
+                        )),
                     };
                     builder.insert(*crate_id, expansion_res)
                 }
@@ -655,7 +657,10 @@ impl GlobalState {
             self.proc_macro_clients = Arc::from_iter(self.workspaces.iter().map(|ws| {
                 let path = match self.config.proc_macro_srv() {
                     Some(path) => path,
-                    None => ws.find_sysroot_proc_macro_srv()?,
+                    None => match ws.find_sysroot_proc_macro_srv()? {
+                        Ok(path) => path,
+                        Err(e) => return Some(Err(e)),
+                    },
                 };
 
                 let env: FxHashMap<_, _> = match &ws.kind {
@@ -682,14 +687,14 @@ impl GlobalState {
                 };
                 info!("Using proc-macro server at {path}");
 
-                ProcMacroClient::spawn(&path, &env).map_err(|err| {
+                Some(ProcMacroClient::spawn(&path, &env).map_err(|err| {
                     tracing::error!(
                         "Failed to run proc-macro server from path {path}, error: {err:?}",
                     );
                     anyhow::format_err!(
                         "Failed to run proc-macro server from path {path}, error: {err:?}",
                     )
-                })
+                }))
             }))
         }
 
@@ -753,14 +758,14 @@ impl GlobalState {
                 change.set_proc_macros(
                     crate_graph
                         .iter()
-                        .map(|id| (id, Err(("proc-macro has not been built yet".to_owned(), true))))
+                        .map(|id| (id, Err(ProcMacroLoadingError::NotYetBuilt)))
                         .collect(),
                 );
             } else {
                 change.set_proc_macros(
                     crate_graph
                         .iter()
-                        .map(|id| (id, Err(("proc-macro expansion is disabled".to_owned(), false))))
+                        .map(|id| (id, Err(ProcMacroLoadingError::Disabled)))
                         .collect(),
                 );
             }
diff --git a/src/tools/rust-analyzer/crates/span/src/ast_id.rs b/src/tools/rust-analyzer/crates/span/src/ast_id.rs
index 8e959711981..121d2e33243 100644
--- a/src/tools/rust-analyzer/crates/span/src/ast_id.rs
+++ b/src/tools/rust-analyzer/crates/span/src/ast_id.rs
@@ -107,9 +107,10 @@ impl fmt::Debug for ErasedFileAstId {
 }
 
 #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
+#[repr(u8)]
 enum ErasedFileAstIdKind {
     /// This needs to not change because it's depended upon by the proc macro server.
-    Fixup,
+    Fixup = 0,
     // The following are associated with `ErasedHasNameFileAstId`.
     Enum,
     Struct,
@@ -413,9 +414,9 @@ impl ErasedAstIdNextIndexMap {
 }
 
 macro_rules! register_enum_ast_id {
-    (impl AstIdNode for $($ident:ident),+ ) => {
+    (impl $AstIdNode:ident for $($ident:ident),+ ) => {
         $(
-            impl AstIdNode for ast::$ident {}
+            impl $AstIdNode for ast::$ident {}
         )+
     };
 }
@@ -426,9 +427,9 @@ register_enum_ast_id! {
 }
 
 macro_rules! register_has_name_ast_id {
-    (impl AstIdNode for $($ident:ident = $name_method:ident),+ ) => {
+    (impl $AstIdNode:ident for $($ident:ident = $name_method:ident),+ ) => {
         $(
-            impl AstIdNode for ast::$ident {}
+            impl $AstIdNode for ast::$ident {}
         )+
 
         fn has_name_ast_id(node: &SyntaxNode, index_map: &mut ErasedAstIdNextIndexMap) -> Option<ErasedFileAstId> {
@@ -472,9 +473,9 @@ register_has_name_ast_id! {
 }
 
 macro_rules! register_assoc_item_ast_id {
-    (impl AstIdNode for $($ident:ident = $name_callback:expr),+ ) => {
+    (impl $AstIdNode:ident for $($ident:ident = $name_callback:expr),+ ) => {
         $(
-            impl AstIdNode for ast::$ident {}
+            impl $AstIdNode for ast::$ident {}
         )+
 
         fn assoc_item_ast_id(
diff --git a/src/tools/rust-analyzer/crates/span/src/hygiene.rs b/src/tools/rust-analyzer/crates/span/src/hygiene.rs
index 7bb88ac3658..aef3fbf0517 100644
--- a/src/tools/rust-analyzer/crates/span/src/hygiene.rs
+++ b/src/tools/rust-analyzer/crates/span/src/hygiene.rs
@@ -97,6 +97,7 @@ const _: () = {
         const LOCATION: salsa::plumbing::Location =
             salsa::plumbing::Location { file: file!(), line: line!() };
         const DEBUG_NAME: &'static str = "SyntaxContextData";
+        const REVISIONS: std::num::NonZeroUsize = std::num::NonZeroUsize::MAX;
         type Fields<'a> = SyntaxContextData;
         type Struct<'a> = SyntaxContext;
     }
@@ -108,7 +109,9 @@ const _: () = {
             static CACHE: zalsa_::IngredientCache<zalsa_struct_::IngredientImpl<SyntaxContext>> =
                 zalsa_::IngredientCache::new();
             CACHE.get_or_create(db.zalsa(), || {
-                db.zalsa().add_or_lookup_jar_by_type::<zalsa_struct_::JarImpl<SyntaxContext>>()
+                db.zalsa()
+                    .lookup_jar_by_type::<zalsa_struct_::JarImpl<SyntaxContext>>()
+                    .get_or_create()
             })
         }
     }
@@ -130,9 +133,12 @@ const _: () = {
         type MemoIngredientMap = salsa::plumbing::MemoIngredientSingletonIndex;
 
         fn lookup_or_create_ingredient_index(
-            aux: &salsa::plumbing::Zalsa,
+            zalsa: &salsa::plumbing::Zalsa,
         ) -> salsa::plumbing::IngredientIndices {
-            aux.add_or_lookup_jar_by_type::<zalsa_struct_::JarImpl<SyntaxContext>>().into()
+            zalsa
+                .lookup_jar_by_type::<zalsa_struct_::JarImpl<SyntaxContext>>()
+                .get_or_create()
+                .into()
         }
 
         #[inline]
@@ -326,14 +332,14 @@ impl<'db> SyntaxContext {
             None
         } else {
             // SAFETY: By our invariant, this is either a root (which we verified it's not) or a valid `salsa::Id`.
-            unsafe { Some(salsa::Id::from_u32(self.0)) }
+            unsafe { Some(salsa::Id::from_index(self.0)) }
         }
     }
 
     #[inline]
     fn from_salsa_id(id: salsa::Id) -> Self {
         // SAFETY: This comes from a Salsa ID.
-        unsafe { Self::from_u32(id.as_u32()) }
+        unsafe { Self::from_u32(id.index()) }
     }
 
     #[inline]
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
index 955aadaa25d..309332873cb 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
@@ -842,9 +842,10 @@ pub fn ref_pat(pat: ast::Pat) -> ast::RefPat {
 }
 
 pub fn match_arm(pat: ast::Pat, guard: Option<ast::MatchGuard>, expr: ast::Expr) -> ast::MatchArm {
+    let comma_str = if expr.is_block_like() { "" } else { "," };
     return match guard {
-        Some(guard) => from_text(&format!("{pat} {guard} => {expr}")),
-        None => from_text(&format!("{pat} => {expr}")),
+        Some(guard) => from_text(&format!("{pat} {guard} => {expr}{comma_str}")),
+        None => from_text(&format!("{pat} => {expr}{comma_str}")),
     };
 
     fn from_text(text: &str) -> ast::MatchArm {
@@ -877,7 +878,7 @@ pub fn match_arm_list(arms: impl IntoIterator<Item = ast::MatchArm>) -> ast::Mat
     let arms_str = arms.into_iter().fold(String::new(), |mut acc, arm| {
         let needs_comma =
             arm.comma_token().is_none() && arm.expr().is_none_or(|it| !it.is_block_like());
-        let comma = if needs_comma { "," } else { "" };
+        let comma = if needs_comma && arm.comma_token().is_none() { "," } else { "" };
         let arm = arm.syntax();
         format_to_acc!(acc, "    {arm}{comma}\n")
     });
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs
index 429e51ba362..17cc5f9c057 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs
@@ -1212,6 +1212,43 @@ impl SyntaxFactory {
         ast
     }
 
+    pub fn attr_outer(&self, meta: ast::Meta) -> ast::Attr {
+        let ast = make::attr_outer(meta.clone()).clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_node(meta.syntax().clone(), ast.meta().unwrap().syntax().clone());
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
+    pub fn attr_inner(&self, meta: ast::Meta) -> ast::Attr {
+        let ast = make::attr_inner(meta.clone()).clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_node(meta.syntax().clone(), ast.meta().unwrap().syntax().clone());
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
+    pub fn meta_token_tree(&self, path: ast::Path, tt: ast::TokenTree) -> ast::Meta {
+        let ast = make::meta_token_tree(path.clone(), tt.clone()).clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_node(path.syntax().clone(), ast.path().unwrap().syntax().clone());
+            builder.map_node(tt.syntax().clone(), ast.token_tree().unwrap().syntax().clone());
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
     pub fn token_tree(
         &self,
         delimiter: SyntaxKind,
@@ -1242,6 +1279,10 @@ impl SyntaxFactory {
     pub fn whitespace(&self, text: &str) -> SyntaxToken {
         make::tokens::whitespace(text)
     }
+
+    pub fn ident(&self, text: &str) -> SyntaxToken {
+        make::tokens::ident(text)
+    }
 }
 
 // `ext` constructors
diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs
index 31caf618be9..3fa584850f7 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs
@@ -435,7 +435,7 @@ mod tests {
             _ => {
                 let var_name = 2 + 2;
                 (var_name, true)
-            }"#]];
+            },"#]];
         expect.assert_eq(&edit.new_root.to_string());
 
         assert_eq!(edit.find_annotation(placeholder_snippet).len(), 2);
diff --git a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs
index 1d821e96e55..e830c6a7cf6 100644
--- a/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs
+++ b/src/tools/rust-analyzer/crates/test-utils/src/fixture.rs
@@ -435,14 +435,16 @@ impl MiniCore {
                 continue;
             }
 
-            let mut active_line_region = false;
-            let mut inactive_line_region = false;
+            let mut active_line_region = 0;
+            let mut inactive_line_region = 0;
             if let Some(idx) = trimmed.find("// :!") {
-                inactive_line_region = true;
-                inactive_regions.push(&trimmed[idx + "// :!".len()..]);
+                let regions = trimmed[idx + "// :!".len()..].split(", ");
+                inactive_line_region += regions.clone().count();
+                inactive_regions.extend(regions);
             } else if let Some(idx) = trimmed.find("// :") {
-                active_line_region = true;
-                active_regions.push(&trimmed[idx + "// :".len()..]);
+                let regions = trimmed[idx + "// :".len()..].split(", ");
+                active_line_region += regions.clone().count();
+                active_regions.extend(regions);
             }
 
             let mut keep = true;
@@ -462,11 +464,11 @@ impl MiniCore {
             if keep {
                 buf.push_str(line);
             }
-            if active_line_region {
-                active_regions.pop().unwrap();
+            if active_line_region > 0 {
+                active_regions.drain(active_regions.len() - active_line_region..);
             }
-            if inactive_line_region {
-                inactive_regions.pop().unwrap();
+            if inactive_line_region > 0 {
+                inactive_regions.drain(inactive_regions.len() - active_line_region..);
             }
         }
 
diff --git a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
index d48063fb86f..e5d8f78d948 100644
--- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
+++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
@@ -11,10 +11,13 @@
 //!     add:
 //!     asm:
 //!     assert:
+//!     as_mut: sized
 //!     as_ref: sized
 //!     async_fn: fn, tuple, future, copy
 //!     bool_impl: option, fn
 //!     builtin_impls:
+//!     borrow: sized
+//!     borrow_mut: borrow
 //!     cell: copy, drop
 //!     clone: sized
 //!     coerce_pointee: derive, sized, unsize, coerce_unsized, dispatch_from_dyn
@@ -228,8 +231,11 @@ pub mod hash {
     }
 
     // region:derive
-    #[rustc_builtin_macro]
-    pub macro Hash($item:item) {}
+    pub(crate) mod derive {
+        #[rustc_builtin_macro]
+        pub macro Hash($item:item) {}
+    }
+    pub use derive::Hash;
     // endregion:derive
 }
 // endregion:hash
@@ -377,11 +383,30 @@ pub mod convert {
         fn as_ref(&self) -> &T;
     }
     // endregion:as_ref
+    // region:as_mut
+    pub trait AsMut<T: crate::marker::PointeeSized>: crate::marker::PointeeSized {
+        fn as_mut(&mut self) -> &mut T;
+    }
+    // endregion:as_mut
     // region:infallible
     pub enum Infallible {}
     // endregion:infallible
 }
 
+pub mod borrow {
+    // region:borrow
+    pub trait Borrow<Borrowed: ?Sized> {
+        fn borrow(&self) -> &Borrowed;
+    }
+    // endregion:borrow
+
+    // region:borrow_mut
+    pub trait BorrowMut<Borrowed: ?Sized>: Borrow<Borrowed> {
+        fn borrow_mut(&mut self) -> &mut Borrowed;
+    }
+    // endregion:borrow_mut
+}
+
 pub mod mem {
     // region:manually_drop
     use crate::marker::PointeeSized;
@@ -986,8 +1011,7 @@ pub mod ops {
     }
 
     #[lang = "add_assign"]
-    #[const_trait]
-    pub trait AddAssign<Rhs = Self> {
+    pub const trait AddAssign<Rhs = Self> {
         fn add_assign(&mut self, rhs: Rhs);
     }
 
@@ -1264,8 +1288,11 @@ pub mod fmt {
     }
 
     // region:derive
-    #[rustc_builtin_macro]
-    pub macro Debug($item:item) {}
+    pub(crate) mod derive {
+        #[rustc_builtin_macro]
+        pub macro Debug($item:item) {}
+    }
+    pub use derive::Debug;
     // endregion:derive
 
     // region:builtin_impls
@@ -1931,6 +1958,8 @@ pub mod prelude {
             panic,                                   // :panic
             result::Result::{self, Err, Ok},         // :result
             str::FromStr,                            // :str
+            fmt::derive::Debug,                      // :fmt, derive
+            hash::derive::Hash,                      // :hash, derive
         };
     }
 
diff --git a/src/tools/rust-analyzer/crates/tt/src/iter.rs b/src/tools/rust-analyzer/crates/tt/src/iter.rs
index 0418c00174b..3246156f1cb 100644
--- a/src/tools/rust-analyzer/crates/tt/src/iter.rs
+++ b/src/tools/rust-analyzer/crates/tt/src/iter.rs
@@ -211,6 +211,7 @@ impl<'a, S: Copy> TtIter<'a, S> {
     }
 }
 
+#[derive(Clone)]
 pub enum TtElement<'a, S> {
     Leaf(&'a Leaf<S>),
     Subtree(&'a Subtree<S>, TtIter<'a, S>),
diff --git a/src/tools/rust-analyzer/crates/tt/src/lib.rs b/src/tools/rust-analyzer/crates/tt/src/lib.rs
index 14574a6456b..44123385c8c 100644
--- a/src/tools/rust-analyzer/crates/tt/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/tt/src/lib.rs
@@ -579,7 +579,7 @@ where
 {
     use rustc_lexer::LiteralKind;
 
-    let token = rustc_lexer::tokenize(text).next_tuple();
+    let token = rustc_lexer::tokenize(text, rustc_lexer::FrontmatterAllowed::No).next_tuple();
     let Some((rustc_lexer::Token {
         kind: rustc_lexer::TokenKind::Literal { kind, suffix_start },
         ..
diff --git a/src/tools/rust-analyzer/crates/vfs/src/file_set.rs b/src/tools/rust-analyzer/crates/vfs/src/file_set.rs
index 1228e2e1774..0c41ede5b53 100644
--- a/src/tools/rust-analyzer/crates/vfs/src/file_set.rs
+++ b/src/tools/rust-analyzer/crates/vfs/src/file_set.rs
@@ -5,8 +5,8 @@
 use std::fmt;
 
 use fst::{IntoStreamer, Streamer};
-use nohash_hasher::IntMap;
-use rustc_hash::FxHashMap;
+use indexmap::IndexMap;
+use rustc_hash::{FxBuildHasher, FxHashMap};
 
 use crate::{AnchoredPath, FileId, Vfs, VfsPath};
 
@@ -14,7 +14,7 @@ use crate::{AnchoredPath, FileId, Vfs, VfsPath};
 #[derive(Default, Clone, Eq, PartialEq)]
 pub struct FileSet {
     files: FxHashMap<VfsPath, FileId>,
-    paths: IntMap<FileId, VfsPath>,
+    paths: IndexMap<FileId, VfsPath, FxBuildHasher>,
 }
 
 impl FileSet {
diff --git a/src/tools/rust-analyzer/docs/book/src/contributing/lsp-extensions.md b/src/tools/rust-analyzer/docs/book/src/contributing/lsp-extensions.md
index 1ada1cb24c2..8c06f33a9f7 100644
--- a/src/tools/rust-analyzer/docs/book/src/contributing/lsp-extensions.md
+++ b/src/tools/rust-analyzer/docs/book/src/contributing/lsp-extensions.md
@@ -694,24 +694,6 @@ interface CancelFlycheckParams {}
 
 Cancels all running flycheck processes.
 
-## Syntax Tree
-
-**Method:** `rust-analyzer/syntaxTree`
-
-**Request:**
-
-```typescript
-interface SyntaxTreeParams {
-    textDocument: TextDocumentIdentifier,
-    range?: Range,
-}
-```
-
-**Response:** `string`
-
-Returns textual representation of a parse tree for the file/selected region.
-Primarily for debugging, but very useful for all people working on rust-analyzer itself.
-
 ## View Syntax Tree
 
 **Method:** `rust-analyzer/viewSyntaxTree`
diff --git a/src/tools/rustbook/Cargo.lock b/src/tools/rustbook/Cargo.lock
index 050ddf47bae..27798d6aeb0 100644
--- a/src/tools/rustbook/Cargo.lock
+++ b/src/tools/rustbook/Cargo.lock
@@ -885,9 +885,9 @@ dependencies = [
 
 [[package]]
 name = "mdbook"
-version = "0.4.51"
+version = "0.4.52"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a87e65420ab45ca9c1b8cdf698f95b710cc826d373fa550f0f7fad82beac9328"
+checksum = "93c284d2855916af7c5919cf9ad897cfc77d3c2db6f55429c7cfb769182030ec"
 dependencies = [
  "ammonia",
  "anyhow",
diff --git a/src/tools/rustbook/Cargo.toml b/src/tools/rustbook/Cargo.toml
index 69c0cfaf5c9..c7c6e39f157 100644
--- a/src/tools/rustbook/Cargo.toml
+++ b/src/tools/rustbook/Cargo.toml
@@ -15,6 +15,6 @@ mdbook-i18n-helpers = "0.3.3"
 mdbook-spec = { path = "../../doc/reference/mdbook-spec" }
 
 [dependencies.mdbook]
-version = "0.4.51"
+version = "0.4.52"
 default-features = false
 features = ["search"]
diff --git a/src/tools/rustdoc-gui-test/src/main.rs b/src/tools/rustdoc-gui-test/src/main.rs
index 6461f38f527..5b86bea8932 100644
--- a/src/tools/rustdoc-gui-test/src/main.rs
+++ b/src/tools/rustdoc-gui-test/src/main.rs
@@ -1,63 +1,15 @@
+use std::env;
 use std::path::{Path, PathBuf};
 use std::process::Command;
 use std::sync::Arc;
-use std::{env, fs};
 
+use build_helper::npm;
 use build_helper::util::try_run;
 use compiletest::directives::TestProps;
 use config::Config;
 
 mod config;
 
-fn get_browser_ui_test_version_inner(npm: &Path, global: bool) -> Option<String> {
-    let mut command = Command::new(&npm);
-    command.arg("list").arg("--parseable").arg("--long").arg("--depth=0");
-    if global {
-        command.arg("--global");
-    }
-    let lines = match command.output() {
-        Ok(output) => String::from_utf8_lossy(&output.stdout).into_owned(),
-        Err(e) => {
-            eprintln!(
-                "path to npm can be wrong, provided path: {npm:?}. Try to set npm path \
-            in bootstrap.toml in [build.npm]",
-            );
-            panic!("{:?}", e)
-        }
-    };
-    lines
-        .lines()
-        .find_map(|l| l.rsplit(':').next()?.strip_prefix("browser-ui-test@"))
-        .map(|v| v.to_owned())
-}
-
-fn get_browser_ui_test_version(npm: &Path) -> Option<String> {
-    get_browser_ui_test_version_inner(npm, false)
-        .or_else(|| get_browser_ui_test_version_inner(npm, true))
-}
-
-fn compare_browser_ui_test_version(installed_version: &str, src: &Path) {
-    match fs::read_to_string(
-        src.join("src/ci/docker/host-x86_64/x86_64-gnu-tools/browser-ui-test.version"),
-    ) {
-        Ok(v) => {
-            if v.trim() != installed_version {
-                eprintln!(
-                    "⚠️ Installed version of browser-ui-test (`{}`) is different than the \
-                     one used in the CI (`{}`)",
-                    installed_version, v
-                );
-                eprintln!(
-                    "You can install this version using `npm update browser-ui-test` or by using \
-                     `npm install browser-ui-test@{}`",
-                    v,
-                );
-            }
-        }
-        Err(e) => eprintln!("Couldn't find the CI browser-ui-test version: {:?}", e),
-    }
-}
-
 fn find_librs<P: AsRef<Path>>(path: P) -> Option<PathBuf> {
     for entry in walkdir::WalkDir::new(path) {
         let entry = entry.ok()?;
@@ -71,27 +23,6 @@ fn find_librs<P: AsRef<Path>>(path: P) -> Option<PathBuf> {
 fn main() -> Result<(), ()> {
     let config = Arc::new(Config::from_args(env::args().collect()));
 
-    // The goal here is to check if the necessary packages are installed, and if not, we
-    // panic.
-    match get_browser_ui_test_version(&config.npm) {
-        Some(version) => {
-            // We also check the version currently used in CI and emit a warning if it's not the
-            // same one.
-            compare_browser_ui_test_version(&version, &config.rust_src);
-        }
-        None => {
-            eprintln!(
-                r#"
-error: rustdoc-gui test suite cannot be run because npm `browser-ui-test` dependency is missing.
-
-If you want to install the `browser-ui-test` dependency, run `npm install browser-ui-test`
-"#,
-            );
-
-            panic!("Cannot run rustdoc-gui tests");
-        }
-    }
-
     let src_path = config.rust_src.join("tests/rustdoc-gui/src");
     for entry in src_path.read_dir().expect("read_dir call failed") {
         if let Ok(entry) = entry {
@@ -112,11 +43,7 @@ If you want to install the `browser-ui-test` dependency, run `npm install browse
                 .current_dir(path);
 
             if let Some(librs) = find_librs(entry.path()) {
-                let compiletest_c = compiletest::common::Config {
-                    edition: None,
-                    mode: compiletest::common::Mode::Rustdoc,
-                    ..Default::default()
-                };
+                let compiletest_c = compiletest::common::Config::incomplete_for_rustdoc_gui_test();
 
                 let test_props = TestProps::from_file(
                     &camino::Utf8PathBuf::try_from(librs).unwrap(),
@@ -138,16 +65,12 @@ If you want to install the `browser-ui-test` dependency, run `npm install browse
         }
     }
 
-    let mut command = Command::new(&config.nodejs);
+    // FIXME(binarycat): once we get package.json in version control, this should be updated to install via that instead
+    let local_node_modules =
+        npm::install_one(&config.out_dir, &config.npm, "browser-ui-test", "0.21.1")
+            .expect("unable to install browser-ui-test");
 
-    if let Ok(current_dir) = env::current_dir() {
-        let local_node_modules = current_dir.join("node_modules");
-        if local_node_modules.exists() {
-            // Link the local node_modules if exists.
-            // This is useful when we run rustdoc-gui-test from outside of the source root.
-            env::set_var("NODE_PATH", local_node_modules);
-        }
-    }
+    let mut command = Command::new(&config.nodejs);
 
     command
         .arg(config.rust_src.join("src/tools/rustdoc-gui/tester.js"))
@@ -158,6 +81,12 @@ If you want to install the `browser-ui-test` dependency, run `npm install browse
         .arg("--tests-folder")
         .arg(config.rust_src.join("tests/rustdoc-gui"));
 
+    if local_node_modules.exists() {
+        // Link the local node_modules if exists.
+        // This is useful when we run rustdoc-gui-test from outside of the source root.
+        command.env("NODE_PATH", local_node_modules);
+    }
+
     for file in &config.goml_files {
         command.arg("--file").arg(file);
     }
diff --git a/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs
index 1a3897b51cb..7084639aca9 100644
--- a/src/tools/rustfmt/src/items.rs
+++ b/src/tools/rustfmt/src/items.rs
@@ -1172,6 +1172,7 @@ pub(crate) fn format_trait(
         unreachable!();
     };
     let ast::Trait {
+        constness,
         is_auto,
         safety,
         ident,
@@ -1182,7 +1183,8 @@ pub(crate) fn format_trait(
 
     let mut result = String::with_capacity(128);
     let header = format!(
-        "{}{}{}trait ",
+        "{}{}{}{}trait ",
+        format_constness(constness),
         format_visibility(context, &item.vis),
         format_safety(safety),
         format_auto(is_auto),
diff --git a/src/tools/suggest-tests/Cargo.toml b/src/tools/suggest-tests/Cargo.toml
deleted file mode 100644
index d6f86078d7e..00000000000
--- a/src/tools/suggest-tests/Cargo.toml
+++ /dev/null
@@ -1,8 +0,0 @@
-[package]
-name = "suggest-tests"
-version = "0.1.0"
-edition = "2021"
-
-[dependencies]
-glob = "0.3.0"
-build_helper = { version = "0.1.0", path = "../../build_helper" }
diff --git a/src/tools/suggest-tests/src/dynamic_suggestions.rs b/src/tools/suggest-tests/src/dynamic_suggestions.rs
deleted file mode 100644
index f09720f1c91..00000000000
--- a/src/tools/suggest-tests/src/dynamic_suggestions.rs
+++ /dev/null
@@ -1,32 +0,0 @@
-use std::path::Path;
-
-use crate::Suggestion;
-
-type DynamicSuggestion = fn(&Path) -> Vec<Suggestion>;
-
-pub(crate) const DYNAMIC_SUGGESTIONS: &[DynamicSuggestion] = &[
-    |path: &Path| -> Vec<Suggestion> {
-        if path.starts_with("compiler/") || path.starts_with("library/") {
-            let path = path.components().take(2).collect::<Vec<_>>();
-
-            vec![Suggestion::with_single_path(
-                "test",
-                None,
-                &format!(
-                    "{}/{}",
-                    path[0].as_os_str().to_str().unwrap(),
-                    path[1].as_os_str().to_str().unwrap()
-                ),
-            )]
-        } else {
-            Vec::new()
-        }
-    },
-    |path: &Path| -> Vec<Suggestion> {
-        if path.starts_with("compiler/rustc_pattern_analysis") {
-            vec![Suggestion::new("test", None, &["tests/ui", "--test-args", "pattern"])]
-        } else {
-            Vec::new()
-        }
-    },
-];
diff --git a/src/tools/suggest-tests/src/lib.rs b/src/tools/suggest-tests/src/lib.rs
deleted file mode 100644
index cc1288c6b72..00000000000
--- a/src/tools/suggest-tests/src/lib.rs
+++ /dev/null
@@ -1,96 +0,0 @@
-use std::fmt::{self, Display};
-use std::path::Path;
-
-use dynamic_suggestions::DYNAMIC_SUGGESTIONS;
-use glob::Pattern;
-use static_suggestions::static_suggestions;
-
-mod dynamic_suggestions;
-mod static_suggestions;
-
-#[cfg(test)]
-mod tests;
-
-macro_rules! sug {
-    ($cmd:expr) => {
-        Suggestion::new($cmd, None, &[])
-    };
-
-    ($cmd:expr, $paths:expr) => {
-        Suggestion::new($cmd, None, $paths.as_slice())
-    };
-
-    ($cmd:expr, $stage:expr, $paths:expr) => {
-        Suggestion::new($cmd, Some($stage), $paths.as_slice())
-    };
-}
-
-pub(crate) use sug;
-
-pub fn get_suggestions<T: AsRef<str>>(modified_files: &[T]) -> Vec<Suggestion> {
-    let mut suggestions = Vec::new();
-
-    // static suggestions
-    for (globs, sugs) in static_suggestions().iter() {
-        let globs = globs
-            .iter()
-            .map(|glob| Pattern::new(glob).expect("Found invalid glob pattern!"))
-            .collect::<Vec<_>>();
-        let matches_some_glob = |file: &str| globs.iter().any(|glob| glob.matches(file));
-
-        if modified_files.iter().map(AsRef::as_ref).any(matches_some_glob) {
-            suggestions.extend_from_slice(sugs);
-        }
-    }
-
-    // dynamic suggestions
-    for sug in DYNAMIC_SUGGESTIONS {
-        for file in modified_files {
-            let sugs = sug(Path::new(file.as_ref()));
-
-            suggestions.extend_from_slice(&sugs);
-        }
-    }
-
-    suggestions.sort();
-    suggestions.dedup();
-
-    suggestions
-}
-
-#[derive(Clone, PartialOrd, Ord, PartialEq, Eq, Debug)]
-pub struct Suggestion {
-    pub cmd: String,
-    pub stage: Option<u32>,
-    pub paths: Vec<String>,
-}
-
-impl Suggestion {
-    pub fn new(cmd: &str, stage: Option<u32>, paths: &[&str]) -> Self {
-        Self { cmd: cmd.to_owned(), stage, paths: paths.iter().map(|p| p.to_string()).collect() }
-    }
-
-    pub fn with_single_path(cmd: &str, stage: Option<u32>, path: &str) -> Self {
-        Self::new(cmd, stage, &[path])
-    }
-}
-
-impl Display for Suggestion {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
-        write!(f, "{} ", self.cmd)?;
-
-        for path in &self.paths {
-            write!(f, "{} ", path)?;
-        }
-
-        if let Some(stage) = self.stage {
-            write!(f, "{}", stage)?;
-        } else {
-            // write a sentinel value here (in place of a stage) to be consumed
-            // by the shim in bootstrap, it will be read and ignored.
-            write!(f, "N/A")?;
-        }
-
-        Ok(())
-    }
-}
diff --git a/src/tools/suggest-tests/src/main.rs b/src/tools/suggest-tests/src/main.rs
deleted file mode 100644
index d84f8e9fa1b..00000000000
--- a/src/tools/suggest-tests/src/main.rs
+++ /dev/null
@@ -1,40 +0,0 @@
-use std::process::ExitCode;
-
-use build_helper::git::{GitConfig, get_git_modified_files};
-use suggest_tests::get_suggestions;
-
-fn main() -> ExitCode {
-    let modified_files = get_git_modified_files(
-        &GitConfig {
-            nightly_branch: &env("SUGGEST_TESTS_NIGHTLY_BRANCH"),
-            git_merge_commit_email: &env("SUGGEST_TESTS_MERGE_COMMIT_EMAIL"),
-        },
-        None,
-        &Vec::new(),
-    );
-    let modified_files = match modified_files {
-        Ok(files) => files,
-        Err(err) => {
-            eprintln!("Could not get modified files from git: \"{err}\"");
-            return ExitCode::FAILURE;
-        }
-    };
-
-    let suggestions = get_suggestions(&modified_files);
-
-    for sug in &suggestions {
-        println!("{sug}");
-    }
-
-    ExitCode::SUCCESS
-}
-
-fn env(key: &str) -> String {
-    match std::env::var(key) {
-        Ok(var) => var,
-        Err(err) => {
-            eprintln!("suggest-tests: failed to read environment variable {key}: {err}");
-            std::process::exit(1);
-        }
-    }
-}
diff --git a/src/tools/suggest-tests/src/static_suggestions.rs b/src/tools/suggest-tests/src/static_suggestions.rs
deleted file mode 100644
index d363d583b54..00000000000
--- a/src/tools/suggest-tests/src/static_suggestions.rs
+++ /dev/null
@@ -1,40 +0,0 @@
-use std::sync::OnceLock;
-
-use crate::{Suggestion, sug};
-
-// FIXME: perhaps this could use `std::lazy` when it is stabilized
-macro_rules! static_suggestions {
-    ($( [ $( $glob:expr ),* $(,)? ] => [ $( $suggestion:expr ),* $(,)? ] ),* $(,)? ) => {
-        pub(crate) fn static_suggestions() -> &'static [(Vec<&'static str>, Vec<Suggestion>)]
-        {
-            static S: OnceLock<Vec<(Vec<&'static str>, Vec<Suggestion>)>> = OnceLock::new();
-            S.get_or_init(|| vec![ $( (vec![ $($glob),* ], vec![ $($suggestion),* ]) ),*])
-        }
-    }
-}
-
-static_suggestions! {
-    ["*.md"] => [
-        sug!("test", 0, ["linkchecker"]),
-    ],
-
-    ["compiler/*"] => [
-        sug!("check"),
-        sug!("test", 1, ["tests/ui", "tests/run-make"]),
-    ],
-
-    ["compiler/rustc_mir_transform/*"] => [
-        sug!("test", 1, ["mir-opt"]),
-    ],
-
-    [
-        "compiler/rustc_mir_transform/src/coverage/*",
-        "compiler/rustc_codegen_llvm/src/coverageinfo/*",
-    ] => [
-        sug!("test", 1, ["coverage"]),
-    ],
-
-    ["src/librustdoc/*"] => [
-        sug!("test", 1, ["rustdoc"]),
-    ],
-}
diff --git a/src/tools/suggest-tests/src/tests.rs b/src/tools/suggest-tests/src/tests.rs
deleted file mode 100644
index b4149136fa3..00000000000
--- a/src/tools/suggest-tests/src/tests.rs
+++ /dev/null
@@ -1,21 +0,0 @@
-macro_rules! sugg_test {
-    ( $( $name:ident: $paths:expr => $suggestions:expr ),* ) => {
-        $(
-            #[test]
-            fn $name() {
-                let suggestions = crate::get_suggestions(&$paths).into_iter().map(|s| s.to_string()).collect::<Vec<_>>();
-                assert_eq!(suggestions, $suggestions);
-            }
-        )*
-    };
-}
-
-sugg_test! {
-    test_error_code_docs: ["compiler/rustc_error_codes/src/error_codes/E0000.md"] =>
-        ["check N/A", "test compiler/rustc_error_codes N/A", "test linkchecker 0", "test tests/ui tests/run-make 1"],
-
-    test_rustdoc: ["src/librustdoc/src/lib.rs"] => ["test rustdoc 1"],
-
-    test_rustdoc_and_libstd: ["src/librustdoc/src/lib.rs", "library/std/src/lib.rs"] =>
-        ["test library/std N/A", "test rustdoc 1"]
-}
diff --git a/src/tools/test-float-parse/src/lib.rs b/src/tools/test-float-parse/src/lib.rs
index 0bd4878f9a6..1321a3c3354 100644
--- a/src/tools/test-float-parse/src/lib.rs
+++ b/src/tools/test-float-parse/src/lib.rs
@@ -340,7 +340,7 @@ fn launch_tests(tests: &mut [TestInfo], cfg: &Config) -> Duration {
     for test in tests.iter_mut() {
         test.progress = Some(ui::Progress::new(test, &mut all_progress_bars));
         ui::set_panic_hook(&all_progress_bars);
-        ((test.launch)(test, cfg));
+        (test.launch)(test, cfg);
     }
 
     start.elapsed()
diff --git a/src/tools/tidy/Cargo.toml b/src/tools/tidy/Cargo.toml
index 4835c220210..d995106ae02 100644
--- a/src/tools/tidy/Cargo.toml
+++ b/src/tools/tidy/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "tidy"
 version = "0.1.0"
-edition = "2021"
+edition = "2024"
 autobins = false
 
 [dependencies]
diff --git a/src/tools/tidy/src/alphabetical.rs b/src/tools/tidy/src/alphabetical.rs
index 141083290c6..9ddce725106 100644
--- a/src/tools/tidy/src/alphabetical.rs
+++ b/src/tools/tidy/src/alphabetical.rs
@@ -103,7 +103,7 @@ fn check_section<'a>(
 
         let prev_line_trimmed_lowercase = prev_line.trim_start_matches(' ');
 
-        if version_sort(&trimmed_line, &prev_line_trimmed_lowercase).is_lt() {
+        if version_sort(trimmed_line, prev_line_trimmed_lowercase).is_lt() {
             tidy_error_ext!(err, bad, "{file}:{}: line not in alphabetical order", idx + 1);
         }
 
diff --git a/src/tools/tidy/src/bins.rs b/src/tools/tidy/src/bins.rs
index 9b78ba75a05..a18f549844b 100644
--- a/src/tools/tidy/src/bins.rs
+++ b/src/tools/tidy/src/bins.rs
@@ -75,7 +75,7 @@ mod os_impl {
                         return ReadOnlyFs;
                     }
 
-                    panic!("unable to create temporary file `{:?}`: {:?}", path, e);
+                    panic!("unable to create temporary file `{path:?}`: {e:?}");
                 }
             }
         }
@@ -83,12 +83,7 @@ mod os_impl {
         for &source_dir in sources {
             match check_dir(source_dir) {
                 Unsupported => return false,
-                ReadOnlyFs => {
-                    return match check_dir(output) {
-                        Supported => true,
-                        _ => false,
-                    };
-                }
+                ReadOnlyFs => return matches!(check_dir(output), Supported),
                 _ => {}
             }
         }
@@ -139,7 +134,7 @@ mod os_impl {
                     return;
                 }
 
-                if t!(is_executable(&file), file) {
+                if t!(is_executable(file), file) {
                     let rel_path = file.strip_prefix(path).unwrap();
                     let git_friendly_path = rel_path.to_str().unwrap().replace("\\", "/");
 
diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs
index bf813d2131e..f43f5eae9a5 100644
--- a/src/tools/tidy/src/deps.rs
+++ b/src/tools/tidy/src/deps.rs
@@ -624,7 +624,7 @@ fn check_proc_macro_dep_list(root: &Path, cargo: &Path, bless: bool, bad: &mut b
     let is_proc_macro_pkg = |pkg: &Package| pkg.targets.iter().any(|target| target.is_proc_macro());
 
     let mut proc_macro_deps = HashSet::new();
-    for pkg in metadata.packages.iter().filter(|pkg| is_proc_macro_pkg(*pkg)) {
+    for pkg in metadata.packages.iter().filter(|pkg| is_proc_macro_pkg(pkg)) {
         deps_of(&metadata, &pkg.id, &mut proc_macro_deps);
     }
     // Remove the proc-macro crates themselves
diff --git a/src/tools/tidy/src/error_codes.rs b/src/tools/tidy/src/error_codes.rs
index bb61412f678..65aa89fe801 100644
--- a/src/tools/tidy/src/error_codes.rs
+++ b/src/tools/tidy/src/error_codes.rs
@@ -119,8 +119,7 @@ fn extract_error_codes(root_path: &Path, errors: &mut Vec<String>) -> Vec<String
             let Some(split_line) = split_line else {
                 errors.push(format!(
                     "{path}:{line_index}: Expected a line with the format `Eabcd: abcd, \
-                    but got \"{}\" without a `:` delimiter",
-                    line,
+                    but got \"{line}\" without a `:` delimiter",
                 ));
                 continue;
             };
@@ -129,10 +128,8 @@ fn extract_error_codes(root_path: &Path, errors: &mut Vec<String>) -> Vec<String
 
             // If this is a duplicate of another error code, emit a fatal error.
             if error_codes.contains(&err_code) {
-                errors.push(format!(
-                    "{path}:{line_index}: Found duplicate error code: `{}`",
-                    err_code
-                ));
+                errors
+                    .push(format!("{path}:{line_index}: Found duplicate error code: `{err_code}`"));
                 continue;
             }
 
@@ -145,8 +142,7 @@ fn extract_error_codes(root_path: &Path, errors: &mut Vec<String>) -> Vec<String
             let Some(rest) = rest else {
                 errors.push(format!(
                     "{path}:{line_index}: Expected a line with the format `Eabcd: abcd, \
-                    but got \"{}\" without a `,` delimiter",
-                    line,
+                    but got \"{line}\" without a `,` delimiter",
                 ));
                 continue;
             };
@@ -209,7 +205,7 @@ fn check_error_codes_docs(
         }
 
         let (found_code_example, found_proper_doctest, emit_ignore_warning, no_longer_emitted) =
-            check_explanation_has_doctest(&contents, &err_code);
+            check_explanation_has_doctest(contents, err_code);
 
         if emit_ignore_warning {
             verbose_print!(
@@ -232,7 +228,7 @@ fn check_error_codes_docs(
             return;
         }
 
-        let test_ignored = IGNORE_DOCTEST_CHECK.contains(&&err_code);
+        let test_ignored = IGNORE_DOCTEST_CHECK.contains(&err_code);
 
         // Check that the explanation has a doctest, and if it shouldn't, that it doesn't
         if !found_proper_doctest && !test_ignored {
@@ -300,7 +296,7 @@ fn check_error_codes_tests(
     let tests_path = root_path.join(Path::new(ERROR_TESTS_PATH));
 
     for code in error_codes {
-        let test_path = tests_path.join(format!("{}.stderr", code));
+        let test_path = tests_path.join(format!("{code}.stderr"));
 
         if !test_path.exists() && !IGNORE_UI_TEST_CHECK.contains(&code.as_str()) {
             verbose_print!(
@@ -388,7 +384,7 @@ fn check_error_codes_used(
 
                     if !error_codes.contains(&error_code) {
                         // This error code isn't properly defined, we must error.
-                        errors.push(format!("Error code `{}` is used in the compiler but not defined and documented in `compiler/rustc_error_codes/src/lib.rs`.", error_code));
+                        errors.push(format!("Error code `{error_code}` is used in the compiler but not defined and documented in `compiler/rustc_error_codes/src/lib.rs`."));
                         continue;
                     }
 
diff --git a/src/tools/tidy/src/ext_tool_checks.rs b/src/tools/tidy/src/ext_tool_checks.rs
index d2da63a9703..381ea44fd46 100644
--- a/src/tools/tidy/src/ext_tool_checks.rs
+++ b/src/tools/tidy/src/ext_tool_checks.rs
@@ -20,8 +20,11 @@
 use std::ffi::OsStr;
 use std::path::{Path, PathBuf};
 use std::process::Command;
+use std::str::FromStr;
 use std::{fmt, fs, io};
 
+use crate::CiInfo;
+
 const MIN_PY_REV: (u32, u32) = (3, 9);
 const MIN_PY_REV_STR: &str = "≥3.9";
 
@@ -36,15 +39,19 @@ const RUFF_CONFIG_PATH: &[&str] = &["src", "tools", "tidy", "config", "ruff.toml
 const RUFF_CACHE_PATH: &[&str] = &["cache", "ruff_cache"];
 const PIP_REQ_PATH: &[&str] = &["src", "tools", "tidy", "config", "requirements.txt"];
 
+// this must be kept in sync with with .github/workflows/spellcheck.yml
+const SPELLCHECK_DIRS: &[&str] = &["compiler", "library", "src/bootstrap", "src/librustdoc"];
+
 pub fn check(
     root_path: &Path,
     outdir: &Path,
+    ci_info: &CiInfo,
     bless: bool,
     extra_checks: Option<&str>,
     pos_args: &[String],
     bad: &mut bool,
 ) {
-    if let Err(e) = check_impl(root_path, outdir, bless, extra_checks, pos_args) {
+    if let Err(e) = check_impl(root_path, outdir, ci_info, bless, extra_checks, pos_args) {
         tidy_error!(bad, "{e}");
     }
 }
@@ -52,34 +59,55 @@ pub fn check(
 fn check_impl(
     root_path: &Path,
     outdir: &Path,
+    ci_info: &CiInfo,
     bless: bool,
     extra_checks: Option<&str>,
     pos_args: &[String],
 ) -> Result<(), Error> {
-    let show_diff = std::env::var("TIDY_PRINT_DIFF")
-        .map_or(false, |v| v.eq_ignore_ascii_case("true") || v == "1");
+    let show_diff =
+        std::env::var("TIDY_PRINT_DIFF").is_ok_and(|v| v.eq_ignore_ascii_case("true") || v == "1");
 
     // Split comma-separated args up
     let lint_args = match extra_checks {
-        Some(s) => s.strip_prefix("--extra-checks=").unwrap().split(',').collect(),
+        Some(s) => s
+            .strip_prefix("--extra-checks=")
+            .unwrap()
+            .split(',')
+            .map(|s| {
+                if s == "spellcheck:fix" {
+                    eprintln!("warning: `spellcheck:fix` is no longer valid, use `--extra-checks=spellcheck --bless`");
+                }
+                (ExtraCheckArg::from_str(s), s)
+            })
+            .filter_map(|(res, src)| match res {
+                Ok(arg) => {
+                    if arg.is_inactive_auto(ci_info) {
+                        None
+                    } else {
+                        Some(arg)
+                    }
+                }
+                Err(err) => {
+                    // only warn because before bad extra checks would be silently ignored.
+                    eprintln!("warning: bad extra check argument {src:?}: {err:?}");
+                    None
+                }
+            })
+            .collect(),
         None => vec![],
     };
 
-    if lint_args.contains(&"spellcheck:fix") {
-        return Err(Error::Generic(
-            "`spellcheck:fix` is no longer valid, use `--extra=check=spellcheck --bless`"
-                .to_string(),
-        ));
+    macro_rules! extra_check {
+        ($lang:ident, $kind:ident) => {
+            lint_args.iter().any(|arg| arg.matches(ExtraCheckLang::$lang, ExtraCheckKind::$kind))
+        };
     }
 
-    let python_all = lint_args.contains(&"py");
-    let python_lint = lint_args.contains(&"py:lint") || python_all;
-    let python_fmt = lint_args.contains(&"py:fmt") || python_all;
-    let shell_all = lint_args.contains(&"shell");
-    let shell_lint = lint_args.contains(&"shell:lint") || shell_all;
-    let cpp_all = lint_args.contains(&"cpp");
-    let cpp_fmt = lint_args.contains(&"cpp:fmt") || cpp_all;
-    let spellcheck = lint_args.contains(&"spellcheck");
+    let python_lint = extra_check!(Py, Lint);
+    let python_fmt = extra_check!(Py, Fmt);
+    let shell_lint = extra_check!(Shell, Lint);
+    let cpp_fmt = extra_check!(Cpp, Fmt);
+    let spellcheck = extra_check!(Spellcheck, None);
 
     let mut py_path = None;
 
@@ -113,7 +141,7 @@ fn check_impl(
             );
         }
         // Rethrow error
-        let _ = res?;
+        res?;
     }
 
     if python_fmt {
@@ -145,7 +173,7 @@ fn check_impl(
         }
 
         // Rethrow error
-        let _ = res?;
+        res?;
     }
 
     if cpp_fmt {
@@ -216,7 +244,7 @@ fn check_impl(
             }
         }
         // Rethrow error
-        let _ = res?;
+        res?;
     }
 
     if shell_lint {
@@ -234,15 +262,9 @@ fn check_impl(
 
     if spellcheck {
         let config_path = root_path.join("typos.toml");
-        // sync target files with .github/workflows/spellcheck.yml
-        let mut args = vec![
-            "-c",
-            config_path.as_os_str().to_str().unwrap(),
-            "./compiler",
-            "./library",
-            "./src/bootstrap",
-            "./src/librustdoc",
-        ];
+        let mut args = vec!["-c", config_path.as_os_str().to_str().unwrap()];
+
+        args.extend_from_slice(SPELLCHECK_DIRS);
 
         if bless {
             eprintln!("spellcheck files and fix");
@@ -264,8 +286,8 @@ fn run_ruff(
     file_args: &[&OsStr],
     ruff_args: &[&OsStr],
 ) -> Result<(), Error> {
-    let mut cfg_args_ruff = cfg_args.into_iter().copied().collect::<Vec<_>>();
-    let mut file_args_ruff = file_args.into_iter().copied().collect::<Vec<_>>();
+    let mut cfg_args_ruff = cfg_args.to_vec();
+    let mut file_args_ruff = file_args.to_vec();
 
     let mut cfg_path = root_path.to_owned();
     cfg_path.extend(RUFF_CONFIG_PATH);
@@ -283,7 +305,7 @@ fn run_ruff(
         file_args_ruff.push(root_path.as_os_str());
     }
 
-    let mut args: Vec<&OsStr> = ruff_args.into_iter().copied().collect();
+    let mut args: Vec<&OsStr> = ruff_args.to_vec();
     args.extend(merge_args(&cfg_args_ruff, &file_args_ruff));
     py_runner(py_path, true, None, "ruff", &args)
 }
@@ -638,3 +660,136 @@ impl From<io::Error> for Error {
         Self::Io(value)
     }
 }
+
+#[derive(Debug)]
+enum ExtraCheckParseError {
+    #[allow(dead_code, reason = "shown through Debug")]
+    UnknownKind(String),
+    #[allow(dead_code)]
+    UnknownLang(String),
+    UnsupportedKindForLang,
+    /// Too many `:`
+    TooManyParts,
+    /// Tried to parse the empty string
+    Empty,
+    /// `auto` specified without lang part.
+    AutoRequiresLang,
+}
+
+struct ExtraCheckArg {
+    auto: bool,
+    lang: ExtraCheckLang,
+    /// None = run all extra checks for the given lang
+    kind: Option<ExtraCheckKind>,
+}
+
+impl ExtraCheckArg {
+    fn matches(&self, lang: ExtraCheckLang, kind: ExtraCheckKind) -> bool {
+        self.lang == lang && self.kind.map(|k| k == kind).unwrap_or(true)
+    }
+
+    /// Returns `true` if this is an auto arg and the relevant files are not modified.
+    fn is_inactive_auto(&self, ci_info: &CiInfo) -> bool {
+        if !self.auto {
+            return false;
+        }
+        let ext = match self.lang {
+            ExtraCheckLang::Py => ".py",
+            ExtraCheckLang::Cpp => ".cpp",
+            ExtraCheckLang::Shell => ".sh",
+            ExtraCheckLang::Spellcheck => {
+                return !crate::files_modified(ci_info, |s| {
+                    SPELLCHECK_DIRS.iter().any(|dir| Path::new(s).starts_with(dir))
+                });
+            }
+        };
+        !crate::files_modified(ci_info, |s| s.ends_with(ext))
+    }
+
+    fn has_supported_kind(&self) -> bool {
+        let Some(kind) = self.kind else {
+            // "run all extra checks" mode is supported for all languages.
+            return true;
+        };
+        use ExtraCheckKind::*;
+        let supported_kinds: &[_] = match self.lang {
+            ExtraCheckLang::Py => &[Fmt, Lint],
+            ExtraCheckLang::Cpp => &[Fmt],
+            ExtraCheckLang::Shell => &[Lint],
+            ExtraCheckLang::Spellcheck => &[],
+        };
+        supported_kinds.contains(&kind)
+    }
+}
+
+impl FromStr for ExtraCheckArg {
+    type Err = ExtraCheckParseError;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        let mut auto = false;
+        let mut parts = s.split(':');
+        let Some(mut first) = parts.next() else {
+            return Err(ExtraCheckParseError::Empty);
+        };
+        if first == "auto" {
+            let Some(part) = parts.next() else {
+                return Err(ExtraCheckParseError::AutoRequiresLang);
+            };
+            auto = true;
+            first = part;
+        }
+        let second = parts.next();
+        if parts.next().is_some() {
+            return Err(ExtraCheckParseError::TooManyParts);
+        }
+        let arg = Self { auto, lang: first.parse()?, kind: second.map(|s| s.parse()).transpose()? };
+        if !arg.has_supported_kind() {
+            return Err(ExtraCheckParseError::UnsupportedKindForLang);
+        }
+
+        Ok(arg)
+    }
+}
+
+#[derive(PartialEq, Copy, Clone)]
+enum ExtraCheckLang {
+    Py,
+    Shell,
+    Cpp,
+    Spellcheck,
+}
+
+impl FromStr for ExtraCheckLang {
+    type Err = ExtraCheckParseError;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        Ok(match s {
+            "py" => Self::Py,
+            "shell" => Self::Shell,
+            "cpp" => Self::Cpp,
+            "spellcheck" => Self::Spellcheck,
+            _ => return Err(ExtraCheckParseError::UnknownLang(s.to_string())),
+        })
+    }
+}
+
+#[derive(PartialEq, Copy, Clone)]
+enum ExtraCheckKind {
+    Lint,
+    Fmt,
+    /// Never parsed, but used as a placeholder for
+    /// langs that never have a specific kind.
+    None,
+}
+
+impl FromStr for ExtraCheckKind {
+    type Err = ExtraCheckParseError;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        Ok(match s {
+            "lint" => Self::Lint,
+            "fmt" => Self::Fmt,
+            _ => return Err(ExtraCheckParseError::UnknownKind(s.to_string())),
+        })
+    }
+}
diff --git a/src/tools/tidy/src/extdeps.rs b/src/tools/tidy/src/extdeps.rs
index 55f937aeacf..bc217a55cc1 100644
--- a/src/tools/tidy/src/extdeps.rs
+++ b/src/tools/tidy/src/extdeps.rs
@@ -41,7 +41,7 @@ pub fn check(root: &Path, bad: &mut bool) {
             let source = line.split_once('=').unwrap().1.trim();
 
             // Ensure source is allowed.
-            if !ALLOWED_SOURCES.contains(&&*source) {
+            if !ALLOWED_SOURCES.contains(&source) {
                 tidy_error!(bad, "invalid source: {}", source);
             }
         }
diff --git a/src/tools/tidy/src/features.rs b/src/tools/tidy/src/features.rs
index 6093e7fd263..e83b47e1380 100644
--- a/src/tools/tidy/src/features.rs
+++ b/src/tools/tidy/src/features.rs
@@ -125,8 +125,8 @@ pub fn check(
                 let gate_test_str = "gate-test-";
 
                 let feature_name = match line.find(gate_test_str) {
-                    // NB: the `splitn` always succeeds, even if the delimiter is not present.
-                    Some(i) => line[i + gate_test_str.len()..].splitn(2, ' ').next().unwrap(),
+                    // `split` always contains at least 1 element, even if the delimiter is not present.
+                    Some(i) => line[i + gate_test_str.len()..].split(' ').next().unwrap(),
                     None => continue,
                 };
                 match features.get_mut(feature_name) {
@@ -135,16 +135,14 @@ pub fn check(
                             err(&format!(
                                 "The file is already marked as gate test \
                                       through its name, no need for a \
-                                      'gate-test-{}' comment",
-                                feature_name
+                                      'gate-test-{feature_name}' comment"
                             ));
                         }
                         f.has_gate_test = true;
                     }
                     None => {
                         err(&format!(
-                            "gate-test test found referencing a nonexistent feature '{}'",
-                            feature_name
+                            "gate-test test found referencing a nonexistent feature '{feature_name}'"
                         ));
                     }
                 }
@@ -170,8 +168,7 @@ pub fn check(
         );
         println!(
             "Hint: If you already have such a test and don't want to rename it,\
-                \n      you can also add a // gate-test-{} line to the test file.",
-            name
+                \n      you can also add a // gate-test-{name} line to the test file."
         );
     }
 
@@ -231,7 +228,7 @@ pub fn check(
 fn get_version_and_channel(src_path: &Path) -> (Version, String) {
     let version_str = t!(std::fs::read_to_string(src_path.join("version")));
     let version_str = version_str.trim();
-    let version = t!(std::str::FromStr::from_str(&version_str).map_err(|e| format!("{e:?}")));
+    let version = t!(std::str::FromStr::from_str(version_str).map_err(|e| format!("{e:?}")));
     let channel_str = t!(std::fs::read_to_string(src_path.join("ci").join("channel")));
     (version, channel_str.trim().to_owned())
 }
@@ -468,7 +465,7 @@ fn get_and_check_lib_features(
     map_lib_features(base_src_path, &mut |res, file, line| match res {
         Ok((name, f)) => {
             let mut check_features = |f: &Feature, list: &Features, display: &str| {
-                if let Some(ref s) = list.get(name) {
+                if let Some(s) = list.get(name) {
                     if f.tracking_issue != s.tracking_issue && f.level != Status::Accepted {
                         tidy_error!(
                             bad,
@@ -483,7 +480,7 @@ fn get_and_check_lib_features(
                     }
                 }
             };
-            check_features(&f, &lang_features, "corresponding lang feature");
+            check_features(&f, lang_features, "corresponding lang feature");
             check_features(&f, &lib_features, "previous");
             lib_features.insert(name.to_owned(), f);
         }
@@ -543,7 +540,7 @@ fn map_lib_features(
                     continue;
                 }
 
-                if let Some((ref name, ref mut f)) = becoming_feature {
+                if let Some((name, ref mut f)) = becoming_feature {
                     if f.tracking_issue.is_none() {
                         f.tracking_issue = find_attr_val(line, "issue").and_then(handle_issue_none);
                     }
diff --git a/src/tools/tidy/src/features/version.rs b/src/tools/tidy/src/features/version.rs
index 6a902e80f8e..0e0629a48e2 100644
--- a/src/tools/tidy/src/features/version.rs
+++ b/src/tools/tidy/src/features/version.rs
@@ -20,7 +20,7 @@ impl fmt::Display for Version {
             Version::Explicit { parts } => {
                 f.pad(&format!("{}.{}.{}", parts[0], parts[1], parts[2]))
             }
-            Version::CurrentPlaceholder => f.pad(&format!("CURRENT")),
+            Version::CurrentPlaceholder => f.pad("CURRENT"),
         }
     }
 }
diff --git a/src/tools/tidy/src/features/version/tests.rs b/src/tools/tidy/src/features/version/tests.rs
index 7701dce2df1..453ba40586e 100644
--- a/src/tools/tidy/src/features/version/tests.rs
+++ b/src/tools/tidy/src/features/version/tests.rs
@@ -33,6 +33,6 @@ fn test_to_string() {
 
     assert_eq!(v_1_0_0.to_string(), "1.0.0");
     assert_eq!(v_1_32_1.to_string(), "1.32.1");
-    assert_eq!(format!("{:<8}", v_1_32_1), "1.32.1  ");
-    assert_eq!(format!("{:>8}", v_1_32_1), "  1.32.1");
+    assert_eq!(format!("{v_1_32_1:<8}"), "1.32.1  ");
+    assert_eq!(format!("{v_1_32_1:>8}"), "  1.32.1");
 }
diff --git a/src/tools/tidy/src/filenames.rs b/src/tools/tidy/src/filenames.rs
new file mode 100644
index 00000000000..53115f4eaa4
--- /dev/null
+++ b/src/tools/tidy/src/filenames.rs
@@ -0,0 +1,40 @@
+//! Tidy check to ensure that there are no filenames containing forbidden characters
+//! checked into the source tree by accident:
+//! - Non-UTF8 filenames
+//! - Control characters such as CR or TAB
+//! - Filenames containing ":" as they are not supported on Windows
+//!
+//! Only files added to git are checked, as it may be acceptable to have temporary
+//! invalid filenames in the local directory during development.
+
+use std::path::Path;
+use std::process::Command;
+
+pub fn check(root_path: &Path, bad: &mut bool) {
+    let stat_output = Command::new("git")
+        .arg("-C")
+        .arg(root_path)
+        .args(["ls-files", "-z"])
+        .output()
+        .unwrap()
+        .stdout;
+    for filename in stat_output.split(|&b| b == 0) {
+        match str::from_utf8(filename) {
+            Err(_) => tidy_error!(
+                bad,
+                r#"non-UTF8 file names are not supported: "{}""#,
+                String::from_utf8_lossy(filename),
+            ),
+            Ok(name) if name.chars().any(|c| c.is_control()) => tidy_error!(
+                bad,
+                r#"control characters are not supported in file names: "{}""#,
+                String::from_utf8_lossy(filename),
+            ),
+            Ok(name) if name.contains(':') => tidy_error!(
+                bad,
+                r#"":" is not supported in file names because of Windows compatibility: "{name}""#,
+            ),
+            _ => (),
+        }
+    }
+}
diff --git a/src/tools/tidy/src/fluent_alphabetical.rs b/src/tools/tidy/src/fluent_alphabetical.rs
index 6f154b92eff..48d14a37514 100644
--- a/src/tools/tidy/src/fluent_alphabetical.rs
+++ b/src/tools/tidy/src/fluent_alphabetical.rs
@@ -13,8 +13,8 @@ fn message() -> &'static Regex {
     static_regex!(r#"(?m)^([a-zA-Z0-9_]+)\s*=\s*"#)
 }
 
-fn filter_fluent(path: &Path) -> bool {
-    if let Some(ext) = path.extension() { ext.to_str() != Some("ftl") } else { true }
+fn is_fluent(path: &Path) -> bool {
+    path.extension().is_some_and(|ext| ext == "flt")
 }
 
 fn check_alphabetic(
@@ -92,7 +92,7 @@ pub fn check(path: &Path, bless: bool, bad: &mut bool) {
     let mut all_defined_msgs = HashMap::new();
     walk(
         path,
-        |path, is_dir| filter_dirs(path) || (!is_dir && filter_fluent(path)),
+        |path, is_dir| filter_dirs(path) || (!is_dir && !is_fluent(path)),
         &mut |ent, contents| {
             if bless {
                 let sorted = sort_messages(
@@ -104,7 +104,7 @@ pub fn check(path: &Path, bless: bool, bad: &mut bool) {
                 if sorted != contents {
                     let mut f =
                         OpenOptions::new().write(true).truncate(true).open(ent.path()).unwrap();
-                    f.write(sorted.as_bytes()).unwrap();
+                    f.write_all(sorted.as_bytes()).unwrap();
                 }
             } else {
                 check_alphabetic(
diff --git a/src/tools/tidy/src/fluent_period.rs b/src/tools/tidy/src/fluent_period.rs
index 6a136e5aec6..85c1ef6166a 100644
--- a/src/tools/tidy/src/fluent_period.rs
+++ b/src/tools/tidy/src/fluent_period.rs
@@ -37,7 +37,7 @@ fn check_period(filename: &str, contents: &str, bad: &mut bool) {
                 if let Some(PatternElement::TextElement { value }) = pat.elements.last() {
                     // We don't care about ellipses.
                     if value.ends_with(".") && !value.ends_with("...") {
-                        let ll = find_line(contents, *value);
+                        let ll = find_line(contents, value);
                         let name = m.id.name;
                         tidy_error!(bad, "{filename}:{ll}: message `{name}` ends in a period");
                     }
@@ -52,7 +52,7 @@ fn check_period(filename: &str, contents: &str, bad: &mut bool) {
 
                 if let Some(PatternElement::TextElement { value }) = attr.value.elements.last() {
                     if value.ends_with(".") && !value.ends_with("...") {
-                        let ll = find_line(contents, *value);
+                        let ll = find_line(contents, value);
                         let name = attr.id.name;
                         tidy_error!(bad, "{filename}:{ll}: attr `{name}` ends in a period");
                     }
diff --git a/src/tools/tidy/src/fluent_used.rs b/src/tools/tidy/src/fluent_used.rs
index ab56b5f0b0e..12fafd9a7ff 100644
--- a/src/tools/tidy/src/fluent_used.rs
+++ b/src/tools/tidy/src/fluent_used.rs
@@ -13,8 +13,8 @@ fn filter_used_messages(
     // we don't just check messages never appear in Rust files,
     // because messages can be used as parts of other fluent messages in Fluent files,
     // so we do checking messages appear only once in all Rust and Fluent files.
-    let mut matches = static_regex!(r"\w+").find_iter(contents);
-    while let Some(name) = matches.next() {
+    let matches = static_regex!(r"\w+").find_iter(contents);
+    for name in matches {
         if let Some((name, filename)) = msgs_not_appeared_yet.remove_entry(name.as_str()) {
             // if one msg appears for the first time,
             // remove it from `msgs_not_appeared_yet` and insert it into `msgs_appeared_only_once`.
diff --git a/src/tools/tidy/src/gcc_submodule.rs b/src/tools/tidy/src/gcc_submodule.rs
index 952ebe9e0cf..5d726c3ea48 100644
--- a/src/tools/tidy/src/gcc_submodule.rs
+++ b/src/tools/tidy/src/gcc_submodule.rs
@@ -7,7 +7,9 @@ use std::process::Command;
 pub fn check(root_path: &Path, compiler_path: &Path, bad: &mut bool) {
     let cg_gcc_version_path = compiler_path.join("rustc_codegen_gcc/libgccjit.version");
     let cg_gcc_version = std::fs::read_to_string(&cg_gcc_version_path)
-        .expect(&format!("Cannot read GCC version from {}", cg_gcc_version_path.display()))
+        .unwrap_or_else(|_| {
+            panic!("Cannot read GCC version from {}", cg_gcc_version_path.display())
+        })
         .trim()
         .to_string();
 
@@ -27,14 +29,13 @@ pub fn check(root_path: &Path, compiler_path: &Path, bad: &mut bool) {
     //  e607be166673a8de9fc07f6f02c60426e556c5f2 src/gcc (master-e607be166673a8de9fc07f6f02c60426e556c5f2.e607be)
     // +e607be166673a8de9fc07f6f02c60426e556c5f2 src/gcc (master-e607be166673a8de9fc07f6f02c60426e556c5f2.e607be)
     let git_output = String::from_utf8_lossy(&git_output.stdout)
-        .trim()
         .split_whitespace()
         .next()
         .unwrap_or_default()
         .to_string();
 
     // The SHA can start with + if the submodule is modified or - if it is not checked out.
-    let gcc_submodule_sha = git_output.trim_start_matches(&['+', '-']);
+    let gcc_submodule_sha = git_output.trim_start_matches(['+', '-']);
     if gcc_submodule_sha != cg_gcc_version {
         *bad = true;
         eprintln!(
diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt
index cac4dba2b49..8b57db23d01 100644
--- a/src/tools/tidy/src/issues.txt
+++ b/src/tools/tidy/src/issues.txt
@@ -2338,7 +2338,6 @@ ui/issues/issue-50415.rs
 ui/issues/issue-50442.rs
 ui/issues/issue-50471.rs
 ui/issues/issue-50518.rs
-ui/issues/issue-50571.rs
 ui/issues/issue-50581.rs
 ui/issues/issue-50582.rs
 ui/issues/issue-50585.rs
diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs
index 237737f0f16..ade4055b5bd 100644
--- a/src/tools/tidy/src/lib.rs
+++ b/src/tools/tidy/src/lib.rs
@@ -124,6 +124,40 @@ pub fn git_diff<S: AsRef<OsStr>>(base_commit: &str, extra_arg: S) -> Option<Stri
     Some(String::from_utf8_lossy(&output.stdout).into())
 }
 
+/// Returns true if any modified file matches the predicate, if we are in CI, or if unable to list modified files.
+pub fn files_modified(ci_info: &CiInfo, pred: impl Fn(&str) -> bool) -> bool {
+    if CiEnv::is_ci() {
+        // assume everything is modified on CI because we really don't want false positives there.
+        return true;
+    }
+    let Some(base_commit) = &ci_info.base_commit else {
+        eprintln!("No base commit, assuming all files are modified");
+        return true;
+    };
+    match crate::git_diff(&base_commit, "--name-status") {
+        Some(output) => {
+            let modified_files = output.lines().filter_map(|ln| {
+                let (status, name) = ln
+                    .trim_end()
+                    .split_once('\t')
+                    .expect("bad format from `git diff --name-status`");
+                if status == "M" { Some(name) } else { None }
+            });
+            for modified_file in modified_files {
+                if pred(modified_file) {
+                    return true;
+                }
+            }
+            false
+        }
+        None => {
+            eprintln!("warning: failed to run `git diff` to check for changes");
+            eprintln!("warning: assuming all files are modified");
+            true
+        }
+    }
+}
+
 pub mod alphabetical;
 pub mod bins;
 pub mod debug_artifacts;
@@ -133,6 +167,7 @@ pub mod error_codes;
 pub mod ext_tool_checks;
 pub mod extdeps;
 pub mod features;
+pub mod filenames;
 pub mod fluent_alphabetical;
 pub mod fluent_period;
 mod fluent_used;
diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs
index ef6ff5c9277..0f1116a632e 100644
--- a/src/tools/tidy/src/main.rs
+++ b/src/tools/tidy/src/main.rs
@@ -15,11 +15,12 @@ use std::{env, process};
 use tidy::*;
 
 fn main() {
-    // Running Cargo will read the libstd Cargo.toml
+    // Enable nightly, because Cargo will read the libstd Cargo.toml
     // which uses the unstable `public-dependency` feature.
-    //
-    // `setenv` might not be thread safe, so run it before using multiple threads.
-    env::set_var("RUSTC_BOOTSTRAP", "1");
+    // SAFETY: no other threads have been spawned
+    unsafe {
+        env::set_var("RUSTC_BOOTSTRAP", "1");
+    }
 
     let root_path: PathBuf = env::args_os().nth(1).expect("need path to root of repo").into();
     let cargo: PathBuf = env::args_os().nth(2).expect("need path to cargo").into();
@@ -154,6 +155,8 @@ fn main() {
 
         check!(triagebot, &root_path);
 
+        check!(filenames, &root_path);
+
         let collected = {
             drain_handles(&mut handles);
 
@@ -173,7 +176,15 @@ fn main() {
         };
         check!(unstable_book, &src_path, collected);
 
-        check!(ext_tool_checks, &root_path, &output_directory, bless, extra_checks, pos_args);
+        check!(
+            ext_tool_checks,
+            &root_path,
+            &output_directory,
+            &ci_info,
+            bless,
+            extra_checks,
+            pos_args
+        );
     });
 
     if bad.load(Ordering::Relaxed) {
diff --git a/src/tools/tidy/src/mir_opt_tests.rs b/src/tools/tidy/src/mir_opt_tests.rs
index bb0d8150be4..1efe71b1687 100644
--- a/src/tools/tidy/src/mir_opt_tests.rs
+++ b/src/tools/tidy/src/mir_opt_tests.rs
@@ -49,7 +49,7 @@ fn check_unused_files(path: &Path, bless: bool, bad: &mut bool) {
 }
 
 fn check_dash_files(path: &Path, bless: bool, bad: &mut bool) {
-    for file in walkdir::WalkDir::new(&path.join("mir-opt"))
+    for file in walkdir::WalkDir::new(path.join("mir-opt"))
         .into_iter()
         .filter_map(Result::ok)
         .filter(|e| e.file_type().is_file())
diff --git a/src/tools/tidy/src/pal.rs b/src/tools/tidy/src/pal.rs
index 9b915e0f737..b7d4a331891 100644
--- a/src/tools/tidy/src/pal.rs
+++ b/src/tools/tidy/src/pal.rs
@@ -86,7 +86,7 @@ pub fn check(path: &Path, bad: &mut bool) {
             return;
         }
 
-        check_cfgs(contents, &file, bad, &mut saw_target_arch, &mut saw_cfg_bang);
+        check_cfgs(contents, file, bad, &mut saw_target_arch, &mut saw_cfg_bang);
     });
 
     assert!(saw_target_arch);
diff --git a/src/tools/tidy/src/rustdoc_gui_tests.rs b/src/tools/tidy/src/rustdoc_gui_tests.rs
index 91776bc989e..3b995f219d2 100644
--- a/src/tools/tidy/src/rustdoc_gui_tests.rs
+++ b/src/tools/tidy/src/rustdoc_gui_tests.rs
@@ -5,7 +5,7 @@ use std::path::Path;
 pub fn check(path: &Path, bad: &mut bool) {
     crate::walk::walk(
         &path.join("rustdoc-gui"),
-        |p, is_dir| !is_dir && p.extension().map_or(true, |e| e != "goml"),
+        |p, is_dir| !is_dir && p.extension().is_none_or(|e| e != "goml"),
         &mut |entry, content| {
             for line in content.lines() {
                 if !line.starts_with("// ") {
diff --git a/src/tools/tidy/src/rustdoc_js.rs b/src/tools/tidy/src/rustdoc_js.rs
index 5e924544f0d..5737fcbafc0 100644
--- a/src/tools/tidy/src/rustdoc_js.rs
+++ b/src/tools/tidy/src/rustdoc_js.rs
@@ -88,7 +88,7 @@ pub fn check(librustdoc_path: &Path, tools_path: &Path, src_path: &Path, bad: &m
     let mut files_to_check = Vec::new();
     walk_no_read(
         &[&librustdoc_path.join("html/static/js")],
-        |path, is_dir| is_dir || !path.extension().is_some_and(|ext| ext == OsStr::new("js")),
+        |path, is_dir| is_dir || path.extension().is_none_or(|ext| ext != OsStr::new("js")),
         &mut |path: &DirEntry| {
             files_to_check.push(path.path().into());
         },
diff --git a/src/tools/tidy/src/rustdoc_json.rs b/src/tools/tidy/src/rustdoc_json.rs
index dfbb35d69f1..722e1ebd0ca 100644
--- a/src/tools/tidy/src/rustdoc_json.rs
+++ b/src/tools/tidy/src/rustdoc_json.rs
@@ -14,25 +14,13 @@ pub fn check(src_path: &Path, ci_info: &crate::CiInfo, bad: &mut bool) {
     };
 
     // First we check that `src/rustdoc-json-types` was modified.
-    match crate::git_diff(&base_commit, "--name-status") {
-        Some(output) => {
-            if !output
-                .lines()
-                .any(|line| line.starts_with("M") && line.contains(RUSTDOC_JSON_TYPES))
-            {
-                // `rustdoc-json-types` was not modified so nothing more to check here.
-                println!("`rustdoc-json-types` was not modified.");
-                return;
-            }
-        }
-        None => {
-            *bad = true;
-            eprintln!("error: failed to run `git diff` in rustdoc_json check");
-            return;
-        }
+    if !crate::files_modified(ci_info, |p| p == RUSTDOC_JSON_TYPES) {
+        // `rustdoc-json-types` was not modified so nothing more to check here.
+        println!("`rustdoc-json-types` was not modified.");
+        return;
     }
     // Then we check that if `FORMAT_VERSION` was updated, the `Latest feature:` was also updated.
-    match crate::git_diff(&base_commit, src_path.join("rustdoc-json-types")) {
+    match crate::git_diff(base_commit, src_path.join("rustdoc-json-types")) {
         Some(output) => {
             let mut format_version_updated = false;
             let mut latest_feature_comment_updated = false;
diff --git a/src/tools/tidy/src/rustdoc_templates.rs b/src/tools/tidy/src/rustdoc_templates.rs
index 2173dbf7e74..dca3e8d9d25 100644
--- a/src/tools/tidy/src/rustdoc_templates.rs
+++ b/src/tools/tidy/src/rustdoc_templates.rs
@@ -14,7 +14,7 @@ const TAGS: &[(&str, &str)] = &[("{#", "#}"), ("{%", "%}"), ("{{", "}}")];
 pub fn check(librustdoc_path: &Path, bad: &mut bool) {
     walk(
         &librustdoc_path.join("html/templates"),
-        |path, is_dir| is_dir || !path.extension().is_some_and(|ext| ext == OsStr::new("html")),
+        |path, is_dir| is_dir || path.extension().is_none_or(|ext| ext != OsStr::new("html")),
         &mut |path: &DirEntry, file_content: &str| {
             let mut lines = file_content.lines().enumerate().peekable();
 
@@ -23,7 +23,7 @@ pub fn check(librustdoc_path: &Path, bad: &mut bool) {
                 if let Some(need_next_line_check) = TAGS.iter().find_map(|(tag, end_tag)| {
                     // We first check if the line ends with a jinja tag.
                     if !line.ends_with(end_tag) {
-                        return None;
+                        None
                     // Then we check if this a comment tag.
                     } else if *tag != "{#" {
                         return Some(false);
diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs
index 2237eac200d..8dde4618ce5 100644
--- a/src/tools/tidy/src/style.rs
+++ b/src/tools/tidy/src/style.rs
@@ -94,10 +94,9 @@ fn generate_problems<'a>(
     letter_digit: &'a FxHashMap<char, char>,
 ) -> impl Iterator<Item = u32> + 'a {
     consts.iter().flat_map(move |const_value| {
-        let problem =
-            letter_digit.iter().fold(format!("{:X}", const_value), |acc, (key, value)| {
-                acc.replace(&value.to_string(), &key.to_string())
-            });
+        let problem = letter_digit.iter().fold(format!("{const_value:X}"), |acc, (key, value)| {
+            acc.replace(&value.to_string(), &key.to_string())
+        });
         let indexes: Vec<usize> = problem
             .chars()
             .enumerate()
@@ -341,7 +340,7 @@ fn is_unexplained_ignore(extension: &str, line: &str) -> bool {
 
 pub fn check(path: &Path, bad: &mut bool) {
     fn skip(path: &Path, is_dir: bool) -> bool {
-        if path.file_name().map_or(false, |name| name.to_string_lossy().starts_with(".#")) {
+        if path.file_name().is_some_and(|name| name.to_string_lossy().starts_with(".#")) {
             // vim or emacs temporary file
             return true;
         }
@@ -358,12 +357,12 @@ pub fn check(path: &Path, bad: &mut bool) {
         let extensions = ["rs", "py", "js", "sh", "c", "cpp", "h", "md", "css", "ftl", "goml"];
 
         // NB: don't skip paths without extensions (or else we'll skip all directories and will only check top level files)
-        if path.extension().map_or(true, |ext| !extensions.iter().any(|e| ext == OsStr::new(e))) {
+        if path.extension().is_none_or(|ext| !extensions.iter().any(|e| ext == OsStr::new(e))) {
             return true;
         }
 
         // We only check CSS files in rustdoc.
-        path.extension().map_or(false, |e| e == "css") && !is_in(path, "src", "librustdoc")
+        path.extension().is_some_and(|e| e == "css") && !is_in(path, "src", "librustdoc")
     }
 
     // This creates a RegexSet as regex contains performance optimizations to be able to deal with these over
@@ -435,7 +434,7 @@ pub fn check(path: &Path, bad: &mut bool) {
             mut skip_copyright,
             mut skip_dbg,
             mut skip_odd_backticks,
-        ] = contains_ignore_directives(&path_str, can_contain, &contents, CONFIGURABLE_CHECKS);
+        ] = contains_ignore_directives(&path_str, can_contain, contents, CONFIGURABLE_CHECKS);
         let mut leading_new_lines = false;
         let mut trailing_new_lines = 0;
         let mut lines = 0;
diff --git a/src/tools/tidy/src/target_specific_tests.rs b/src/tools/tidy/src/target_specific_tests.rs
index a66ccd37070..1a6fd3eaf2d 100644
--- a/src/tools/tidy/src/target_specific_tests.rs
+++ b/src/tools/tidy/src/target_specific_tests.rs
@@ -30,10 +30,9 @@ pub fn check(tests_path: &Path, bad: &mut bool) {
                         comp_vec.push(component);
                     }
                 }
-            } else if directive.starts_with(COMPILE_FLAGS_HEADER) {
-                let compile_flags = &directive[COMPILE_FLAGS_HEADER.len()..];
+            } else if let Some(compile_flags) = directive.strip_prefix(COMPILE_FLAGS_HEADER) {
                 if let Some((_, v)) = compile_flags.split_once("--target") {
-                    let v = v.trim_start_matches(|c| c == ' ' || c == '=');
+                    let v = v.trim_start_matches([' ', '=']);
                     let v = if v == "{{target}}" { Some((v, v)) } else { v.split_once("-") };
                     if let Some((arch, _)) = v {
                         let info = header_map.entry(revision).or_insert(RevisionInfo::default());
@@ -57,15 +56,13 @@ pub fn check(tests_path: &Path, bad: &mut bool) {
                 (None, None) => {}
                 (Some(_), None) => {
                     eprintln!(
-                        "{}: revision {} should specify `{}` as it has `--target` set",
-                        file, rev, LLVM_COMPONENTS_HEADER
+                        "{file}: revision {rev} should specify `{LLVM_COMPONENTS_HEADER}` as it has `--target` set"
                     );
                     *bad = true;
                 }
                 (None, Some(_)) => {
                     eprintln!(
-                        "{}: revision {} should not specify `{}` as it doesn't need `--target`",
-                        file, rev, LLVM_COMPONENTS_HEADER
+                        "{file}: revision {rev} should not specify `{LLVM_COMPONENTS_HEADER}` as it doesn't need `--target`"
                     );
                     *bad = true;
                 }
diff --git a/src/tools/tidy/src/tests_revision_unpaired_stdout_stderr.rs b/src/tools/tidy/src/tests_revision_unpaired_stdout_stderr.rs
index ee92d302db3..02412b6f190 100644
--- a/src/tools/tidy/src/tests_revision_unpaired_stdout_stderr.rs
+++ b/src/tools/tidy/src/tests_revision_unpaired_stdout_stderr.rs
@@ -38,7 +38,7 @@ pub fn check(tests_path: impl AsRef<Path>, bad: &mut bool) {
 
             let sibling_path = sibling.path();
 
-            let Some(ext) = sibling_path.extension().map(OsStr::to_str).flatten() else {
+            let Some(ext) = sibling_path.extension().and_then(OsStr::to_str) else {
                 continue;
             };
 
@@ -84,7 +84,7 @@ pub fn check(tests_path: impl AsRef<Path>, bad: &mut bool) {
                 }
             });
 
-            let Some(test_name) = test.file_stem().map(OsStr::to_str).flatten() else {
+            let Some(test_name) = test.file_stem().and_then(OsStr::to_str) else {
                 continue;
             };
 
@@ -102,9 +102,9 @@ pub fn check(tests_path: impl AsRef<Path>, bad: &mut bool) {
         // of the form: `test-name.revision.compare_mode.extension`, but our only concern is
         // `test-name.revision` and `extension`.
         for sibling in files_under_inspection.iter().filter(|f| {
-            f.extension().map(OsStr::to_str).flatten().is_some_and(|ext| EXTENSIONS.contains(&ext))
+            f.extension().and_then(OsStr::to_str).is_some_and(|ext| EXTENSIONS.contains(&ext))
         }) {
-            let Some(filename) = sibling.file_name().map(OsStr::to_str).flatten() else {
+            let Some(filename) = sibling.file_name().and_then(OsStr::to_str) else {
                 continue;
             };
 
@@ -131,10 +131,10 @@ pub fn check(tests_path: impl AsRef<Path>, bad: &mut bool) {
                 }
                 [_, _] => return,
                 [_, found_revision, .., extension] => {
-                    if !IGNORES.contains(&found_revision)
+                    if !IGNORES.contains(found_revision)
                         && !expected_revisions.contains(*found_revision)
                         // This is from `//@ stderr-per-bitwidth`
-                        && !(*extension == "stderr" && ["32bit", "64bit"].contains(&found_revision))
+                        && !(*extension == "stderr" && ["32bit", "64bit"].contains(found_revision))
                     {
                         // Found some unexpected revision-esque component that is not a known
                         // compare-mode or expected revision.
diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs
index 53226fcb80e..7e295731c56 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 = 1619;
+const ISSUES_ENTRY_LIMIT: u32 = 1616;
 
 const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[
     "rs",     // test source files
@@ -57,11 +57,9 @@ const EXTENSION_EXCEPTION_PATHS: &[&str] = &[
 fn check_entries(tests_path: &Path, bad: &mut bool) {
     let mut directories: HashMap<PathBuf, u32> = HashMap::new();
 
-    for dir in Walk::new(&tests_path.join("ui")) {
-        if let Ok(entry) = dir {
-            let parent = entry.path().parent().unwrap().to_path_buf();
-            *directories.entry(parent).or_default() += 1;
-        }
+    for entry in Walk::new(tests_path.join("ui")).flatten() {
+        let parent = entry.path().parent().unwrap().to_path_buf();
+        *directories.entry(parent).or_default() += 1;
     }
 
     let (mut max, mut max_issues) = (0, 0);
@@ -99,7 +97,7 @@ pub fn check(root_path: &Path, bless: bool, bad: &mut bool) {
 "#;
 
     let path = &root_path.join("tests");
-    check_entries(&path, bad);
+    check_entries(path, bad);
 
     // the list of files in ui tests that are allowed to start with `issue-XXXX`
     // BTreeSet because we would like a stable ordering so --bless works
@@ -109,13 +107,12 @@ pub fn check(root_path: &Path, bless: bool, bad: &mut bool) {
         .strip_prefix(issues_txt_header)
         .unwrap()
         .lines()
-        .map(|line| {
+        .inspect(|&line| {
             if prev_line > line {
                 is_sorted = false;
             }
 
             prev_line = line;
-            line
         })
         .collect();
 
@@ -203,7 +200,7 @@ pub fn check(root_path: &Path, bless: bool, bad: &mut bool) {
         // so we don't bork things on panic or a contributor using Ctrl+C
         let blessed_issues_path = tidy_src.join("issues_blessed.txt");
         let mut blessed_issues_txt = fs::File::create(&blessed_issues_path).unwrap();
-        blessed_issues_txt.write(issues_txt_header.as_bytes()).unwrap();
+        blessed_issues_txt.write_all(issues_txt_header.as_bytes()).unwrap();
         // If we changed paths to use the OS separator, reassert Unix chauvinism for blessing.
         for filename in allowed_issue_names.difference(&remaining_issue_names) {
             writeln!(blessed_issues_txt, "{filename}").unwrap();
diff --git a/src/tools/tidy/src/unstable_book.rs b/src/tools/tidy/src/unstable_book.rs
index a2453a6c960..9dc9d42d466 100644
--- a/src/tools/tidy/src/unstable_book.rs
+++ b/src/tools/tidy/src/unstable_book.rs
@@ -38,7 +38,7 @@ fn dir_entry_is_file(dir_entry: &fs::DirEntry) -> bool {
 pub fn collect_unstable_feature_names(features: &Features) -> BTreeSet<String> {
     features
         .iter()
-        .filter(|&(_, ref f)| f.level == Status::Unstable)
+        .filter(|&(_, f)| f.level == Status::Unstable)
         .map(|(name, _)| name.replace('_', "-"))
         .collect()
 }
@@ -90,7 +90,7 @@ pub fn check(path: &Path, features: CollectedFeatures, bad: &mut bool) {
     let lib_features = features
         .lib
         .into_iter()
-        .filter(|&(ref name, _)| !lang_features.contains_key(name))
+        .filter(|(name, _)| !lang_features.contains_key(name))
         .collect::<Features>();
 
     // Library features
diff --git a/src/tools/tidy/src/walk.rs b/src/tools/tidy/src/walk.rs
index c2c9eb8d250..7825ebeb5ba 100644
--- a/src/tools/tidy/src/walk.rs
+++ b/src/tools/tidy/src/walk.rs
@@ -66,7 +66,7 @@ pub fn walk_many(
             Ok(s) => s,
             Err(_) => return, // skip this file
         };
-        f(&entry, &contents_str);
+        f(entry, contents_str);
     });
 }
 
@@ -83,7 +83,7 @@ pub(crate) fn walk_no_read(
         !skip(e.path(), e.file_type().map(|ft| ft.is_dir()).unwrap_or(false))
     });
     for entry in walker.build().flatten() {
-        if entry.file_type().map_or(true, |kind| kind.is_dir() || kind.is_symlink()) {
+        if entry.file_type().is_none_or(|kind| kind.is_dir() || kind.is_symlink()) {
             continue;
         }
         f(&entry);
diff --git a/src/tools/tidy/src/x_version.rs b/src/tools/tidy/src/x_version.rs
index 489343561e1..6a5e9eca813 100644
--- a/src/tools/tidy/src/x_version.rs
+++ b/src/tools/tidy/src/x_version.rs
@@ -26,7 +26,7 @@ pub fn check(root: &Path, cargo: &Path, bad: &mut bool) {
                     // Check this is the rust-lang/rust x tool installation since it should be
                     // installed at a path containing `src/tools/x`.
                     if let Some(path) = iter.next() {
-                        if path.contains(&"src/tools/x") {
+                        if path.contains("src/tools/x") {
                             let version = version.strip_prefix("v").unwrap();
                             installed = Some(Version::parse(version).unwrap());
                             break;
@@ -42,15 +42,15 @@ pub fn check(root: &Path, cargo: &Path, bad: &mut bool) {
 
         if let Some(expected) = get_x_wrapper_version(root, cargo) {
             if installed < expected {
-                return println!(
+                println!(
                     "Current version of x is {installed}, but the latest version is {expected}\nConsider updating to the newer version of x by running `cargo install --path src/tools/x`"
-                );
+                )
             }
         } else {
-            return tidy_error!(
+            tidy_error!(
                 bad,
                 "Unable to parse the latest version of `x` at `src/tools/x/Cargo.toml`"
-            );
+            )
         }
     } else {
         tidy_error!(bad, "failed to check version of `x`: {}", cargo_list.status)
diff --git a/src/tools/tier-check/Cargo.toml b/src/tools/tier-check/Cargo.toml
index 3f08165a3fc..a34f9447fab 100644
--- a/src/tools/tier-check/Cargo.toml
+++ b/src/tools/tier-check/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "tier-check"
 version = "0.1.0"
-edition = "2021"
+edition = "2024"
 license = "MIT OR Apache-2.0"
 
 [dependencies]
diff --git a/src/tools/tier-check/src/main.rs b/src/tools/tier-check/src/main.rs
index 91b96117db0..6c27a2ec442 100644
--- a/src/tools/tier-check/src/main.rs
+++ b/src/tools/tier-check/src/main.rs
@@ -25,29 +25,27 @@ fn main() {
     let doc_targets: HashSet<_> = doc_targets_md
         .lines()
         .filter(|line| line.starts_with(&['`', '['][..]) && line.contains('|'))
-        .map(|line| line.split('`').skip(1).next().expect("expected target code span"))
+        .map(|line| line.split('`').nth(1).expect("expected target code span"))
         .collect();
 
     let missing: Vec<_> = target_list.difference(&doc_targets).collect();
     let extra: Vec<_> = doc_targets.difference(&target_list).collect();
     for target in &missing {
         eprintln!(
-            "error: target `{}` is missing from {}\n\
-            If this is a new target, please add it to {}.",
-            target, filename, src
+            "error: target `{target}` is missing from {filename}\n\
+            If this is a new target, please add it to {src}."
         );
     }
     for target in &extra {
         eprintln!(
-            "error: target `{}` is in {}, but does not appear in the rustc target list\n\
-            If the target has been removed, please edit {} and remove the target.",
-            target, filename, src
+            "error: target `{target}` is in {filename}, but does not appear in the rustc target list\n\
+            If the target has been removed, please edit {src} and remove the target."
         );
     }
     // Check target names for unwanted characters like `.` that can cause problems e.g. in Cargo.
     // See also Tier 3 target policy.
     // If desired, target names can ignore this check.
-    let ignore_target_names = vec![
+    let ignore_target_names = [
         "thumbv8m.base-none-eabi",
         "thumbv8m.main-none-eabi",
         "thumbv8m.main-none-eabihf",
diff --git a/src/tools/unicode-table-generator/src/range_search.rs b/src/tools/unicode-table-generator/src/range_search.rs
index 02f9cf16d4d..4d1dd9b423b 100644
--- a/src/tools/unicode-table-generator/src/range_search.rs
+++ b/src/tools/unicode-table-generator/src/range_search.rs
@@ -80,7 +80,7 @@ unsafe fn skip_search<const SOR: usize, const OFFSETS: usize>(
     let needle = needle as u32;
 
     let last_idx =
-        match short_offset_runs.binary_search_by_key(&(needle << 11), |header| (header.0 << 11)) {
+        match short_offset_runs.binary_search_by_key(&(needle << 11), |header| header.0 << 11) {
             Ok(idx) => idx + 1,
             Err(idx) => idx,
         };
diff --git a/src/tools/x/Cargo.toml b/src/tools/x/Cargo.toml
index 84a42ca36ef..c59f5ff52a0 100644
--- a/src/tools/x/Cargo.toml
+++ b/src/tools/x/Cargo.toml
@@ -2,5 +2,5 @@
 name = "x"
 version = "0.1.1"
 description = "Run x.py slightly more conveniently"
-edition = "2021"
+edition = "2024"
 publish = false
diff --git a/src/tools/x/src/main.rs b/src/tools/x/src/main.rs
index b288cfcd5be..93167141d34 100644
--- a/src/tools/x/src/main.rs
+++ b/src/tools/x/src/main.rs
@@ -19,15 +19,14 @@ const PYTHON2: &str = "python2";
 const PYTHON3: &str = "python3";
 
 fn python() -> &'static str {
-    let val = match env::var_os("PATH") {
-        Some(val) => val,
-        None => return PYTHON,
+    let Some(path) = env::var_os("PATH") else {
+        return PYTHON;
     };
 
     let mut python2 = false;
     let mut python3 = false;
 
-    for dir in env::split_paths(&val) {
+    for dir in env::split_paths(&path) {
         // `python` should always take precedence over python2 / python3 if it exists
         if dir.join(PYTHON).with_extension(EXE_EXTENSION).exists() {
             return PYTHON;
@@ -89,7 +88,7 @@ fn exec_or_status(command: &mut Command) -> io::Result<ExitStatus> {
 fn handle_result(result: io::Result<ExitStatus>, cmd: Command) {
     match result {
         Err(error) => {
-            eprintln!("Failed to invoke `{:?}`: {}", cmd, error);
+            eprintln!("Failed to invoke `{cmd:?}`: {error}");
         }
         Ok(status) => {
             process::exit(status.code().unwrap_or(1));
@@ -98,14 +97,12 @@ fn handle_result(result: io::Result<ExitStatus>, cmd: Command) {
 }
 
 fn main() {
-    match env::args().skip(1).next().as_deref() {
-        Some("--wrapper-version") => {
-            let version = env!("CARGO_PKG_VERSION");
-            println!("{}", version);
-            return;
-        }
-        _ => {}
+    if env::args().nth(1).is_some_and(|s| s == "--wrapper-version") {
+        let version = env!("CARGO_PKG_VERSION");
+        println!("{version}");
+        return;
     }
+
     let current = match env::current_dir() {
         Ok(dir) => dir,
         Err(err) => {
@@ -113,7 +110,6 @@ fn main() {
             process::exit(1);
         }
     };
-
     for dir in current.ancestors() {
         let candidate = dir.join("x.py");
         if candidate.exists() {