about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorThe rustc-josh-sync Cronjob Bot <github-actions@github.com>2025-07-21 04:17:50 +0000
committerThe rustc-josh-sync Cronjob Bot <github-actions@github.com>2025-07-21 04:17:50 +0000
commitad20b064c30bbe9626e8c99f2b455cc6fd836d18 (patch)
tree1c8ac5ec462a793060ccc53aa22c33a0b9215640 /src
parent897d007f8f2d4018fba5f2884cb1b45846d8a371 (diff)
parent460259d14de0274b97b8801e08cb2fe5f16fdac5 (diff)
downloadrust-ad20b064c30bbe9626e8c99f2b455cc6fd836d18.tar.gz
rust-ad20b064c30bbe9626e8c99f2b455cc6fd836d18.zip
Merge ref '460259d14de0' from rust-lang/rust
Pull recent changes from https://github.com/rust-lang/rust via Josh.

Upstream ref: 460259d14de0274b97b8801e08cb2fe5f16fdac5
Filtered ref: 599ee17eb87c83f97eb37fd9fe264da65d4c9461

This merge was created using https://github.com/rust-lang/josh-sync.
Diffstat (limited to 'src')
-rw-r--r--src/bootstrap/README.md2
-rw-r--r--src/bootstrap/bootstrap.py7
-rw-r--r--src/bootstrap/src/core/build_steps/check.rs9
-rw-r--r--src/bootstrap/src/core/build_steps/compile.rs90
-rw-r--r--src/bootstrap/src/core/build_steps/dist.rs23
-rw-r--r--src/bootstrap/src/core/build_steps/gcc.rs25
-rw-r--r--src/bootstrap/src/core/build_steps/install.rs2
-rw-r--r--src/bootstrap/src/core/build_steps/test.rs30
-rw-r--r--src/bootstrap/src/core/build_steps/tool.rs330
-rw-r--r--src/bootstrap/src/core/builder/cargo.rs55
-rw-r--r--src/bootstrap/src/core/builder/mod.rs3
-rw-r--r--src/bootstrap/src/core/builder/tests.rs45
-rw-r--r--src/bootstrap/src/core/config/config.rs8
-rw-r--r--src/bootstrap/src/core/config/toml/llvm.rs16
-rw-r--r--src/bootstrap/src/core/config/toml/rust.rs28
-rw-r--r--src/bootstrap/src/lib.rs68
-rw-r--r--src/bootstrap/src/utils/change_tracker.rs5
-rw-r--r--src/build_helper/src/lib.rs1
-rw-r--r--src/build_helper/src/npm.rs30
-rw-r--r--src/ci/docker/host-aarch64/aarch64-gnu-llvm-19/Dockerfile8
-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/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.sh12
-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.yml16
-rwxr-xr-xsrc/ci/scripts/install-mingw.sh5
-rw-r--r--src/doc/rustc-dev-guide/src/effects.md9
-rw-r--r--src/doc/rustc-dev-guide/src/tests/directives.md8
-rw-r--r--src/doc/rustc-dev-guide/src/tests/ui.md20
-rw-r--r--src/doc/rustc/src/exploit-mitigations.md22
-rw-r--r--src/doc/rustc/src/platform-support/xtensa.md2
-rw-r--r--src/librustdoc/clean/mod.rs5
-rw-r--r--src/librustdoc/clean/types.rs6
-rw-r--r--src/librustdoc/clean/utils.rs17
-rw-r--r--src/librustdoc/doctest.rs4
-rw-r--r--src/librustdoc/doctest/rust.rs2
-rw-r--r--src/librustdoc/formats/cache.rs4
-rw-r--r--src/librustdoc/formats/renderer.rs4
-rw-r--r--src/librustdoc/html/format.rs35
-rw-r--r--src/librustdoc/html/highlight.rs4
-rw-r--r--src/librustdoc/html/layout.rs1
-rw-r--r--src/librustdoc/html/markdown.rs4
-rw-r--r--src/librustdoc/html/render/context.rs88
-rw-r--r--src/librustdoc/html/render/mod.rs50
-rw-r--r--src/librustdoc/html/render/ordered_json.rs2
-rw-r--r--src/librustdoc/html/render/print_item.rs89
-rw-r--r--src/librustdoc/html/render/search_index.rs16
-rw-r--r--src/librustdoc/html/render/sidebar.rs2
-rw-r--r--src/librustdoc/html/render/sorted_template.rs3
-rw-r--r--src/librustdoc/html/render/write_shared.rs4
-rw-r--r--src/librustdoc/html/static/js/rustdoc.d.ts7
-rw-r--r--src/librustdoc/html/static/js/search.js150
-rw-r--r--src/librustdoc/html/url_parts_builder.rs12
-rw-r--r--src/librustdoc/json/conversions.rs20
-rw-r--r--src/librustdoc/json/mod.rs6
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs25
-rw-r--r--src/librustdoc/passes/lint/redundant_explicit_links.rs16
-rw-r--r--src/librustdoc/passes/strip_aliased_non_local.rs10
m---------src/llvm-project0
-rw-r--r--src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/doc/mod.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/len_zero.rs2
-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/missing_inline.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/std_instead_of_core.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/trait_bounds.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/upper_case_acronyms.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/ast_utils/mod.rs5
-rw-r--r--src/tools/clippy/clippy_utils/src/check_proc_macro.rs4
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs17
-rw-r--r--src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs2
-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/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/ptr_arg.rs2
-rw-r--r--src/tools/clippy/tests/ui/ptr_arg.stderr11
-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/compiletest/src/common.rs62
-rw-r--r--src/tools/compiletest/src/directives.rs59
-rw-r--r--src/tools/compiletest/src/lib.rs19
-rw-r--r--src/tools/compiletest/src/runtest.rs7
-rw-r--r--src/tools/compiletest/src/runtest/ui.rs53
-rw-r--r--src/tools/lint-docs/Cargo.toml2
-rw-r--r--src/tools/miri/src/alloc_addresses/mod.rs2
-rw-r--r--src/tools/miri/src/bin/log/tracing_chrome.rs72
-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/machine.rs4
-rw-r--r--src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.rs4
-rw-r--r--src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.stderr5
-rw-r--r--src/tools/miri/tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.rs4
-rw-r--r--src/tools/miri/tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.stderr5
-rw-r--r--src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.rs4
-rw-r--r--src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.stderr5
-rw-r--r--src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.rs4
-rw-r--r--src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.stderr5
-rw-r--r--src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.rs4
-rw-r--r--src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.stderr5
-rw-r--r--src/tools/miri/tests/fail/function_calls/arg_inplace_observe_after.stderr7
-rw-r--r--src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.none.stderr7
-rw-r--r--src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_read.none.stderr7
-rw-r--r--src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr15
-rw-r--r--src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_data.rs5
-rw-r--r--src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_data.stderr5
-rw-r--r--src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_len.rs5
-rw-r--r--src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_len.stderr5
-rw-r--r--src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_thin.rs5
-rw-r--r--src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_thin.stderr5
-rw-r--r--src/tools/miri/tests/fail/read_from_trivial_switch.rs5
-rw-r--r--src/tools/miri/tests/fail/read_from_trivial_switch.stderr5
-rw-r--r--src/tools/miri/tests/fail/uninit/padding-enum.rs3
-rw-r--r--src/tools/miri/tests/fail/uninit/padding-enum.stderr5
-rw-r--r--src/tools/miri/tests/fail/uninit/padding-pair.rs3
-rw-r--r--src/tools/miri/tests/fail/uninit/padding-pair.stderr5
-rw-r--r--src/tools/miri/tests/fail/uninit/padding-struct.stderr7
-rw-r--r--src/tools/miri/tests/fail/uninit/padding-wide-ptr.rs3
-rw-r--r--src/tools/miri/tests/fail/uninit/padding-wide-ptr.stderr5
-rw-r--r--src/tools/miri/tests/fail/uninit/transmute-pair-uninit.rs3
-rw-r--r--src/tools/miri/tests/fail/uninit/transmute-pair-uninit.stderr5
-rw-r--r--src/tools/miri/tests/fail/uninit/uninit_byte_read.stderr7
-rw-r--r--src/tools/miri/tests/fail/validity/invalid_int_op.stderr7
-rw-r--r--src/tools/miri/tests/native-lib/fail/tracing/partial_init.rs2
-rw-r--r--src/tools/miri/tests/native-lib/fail/tracing/partial_init.stderr7
-rw-r--r--src/tools/miri/tests/panic/mir-validation.stderr14
-rw-r--r--src/tools/miri/tests/pass-dep/libc/libc-time.rs6
-rw-r--r--src/tools/miri/tests/pass/intrinsics/type-id.rs19
-rw-r--r--src/tools/opt-dist/src/environment.rs5
-rw-r--r--src/tools/opt-dist/src/exec.rs12
-rw-r--r--src/tools/opt-dist/src/main.rs122
-rw-r--r--src/tools/opt-dist/src/training.rs4
-rw-r--r--src/tools/rust-analyzer/Cargo.lock82
-rw-r--r--src/tools/rust-analyzer/Cargo.toml20
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs405
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs139
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/asm.rs15
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path/tests.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expr_store/scope.rs13
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs11
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/hir.rs11
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs42
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs24
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs22
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs42
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs6
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres.rs19
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/resolver.rs11
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/signatures.rs20
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/builtin/fn_macro.rs34
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/name.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/db.rs5
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs5
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs8
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs15
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/display.rs9
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/layout/target.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs8
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir.rs7
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/eval.rs14
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs14
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/tests.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/variance.rs18
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/lib.rs5
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/semantics.rs5
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs18
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/symbols.rs7
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs28
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs15
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_new.rs15
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs25
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs142
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs409
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs1000
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs69
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs87
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/replace_is_method_with_if_let_method.rs13
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/lib.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs30
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/utils.rs49
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs53
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs27
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt68
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs24
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs15
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/doc_links.rs6
-rwxr-xr-xsrc/tools/rust-analyzer/crates/ide/src/folding_ranges.rs52
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover/tests.rs65
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/inlay_hints/implied_dyn_trait.rs36
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs2
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs27
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/grammar/items.rs13
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/lexed_str.rs4
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/parser.rs7
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs12
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs4
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_kinds.rast48
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_kinds.rs5
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/global_asm.rast10
-rw-r--r--src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/global_asm.rs1
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/cargo_config_file.rs34
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs395
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/env.rs137
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/lib.rs5
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/sysroot.rs23
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/tests.rs9
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs4
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_data_layout.rs4
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_tuple.rs50
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/toolchain_info/version.rs4
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/workspace.rs126
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs4
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs2
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs3
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs3
-rw-r--r--src/tools/rust-analyzer/crates/span/src/ast_id.rs42
-rw-r--r--src/tools/rust-analyzer/crates/syntax/rust.ungram4
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs36
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs19
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/make.rs2
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs13
-rw-r--r--src/tools/rust-analyzer/crates/test-utils/src/minicore.rs4
-rw-r--r--src/tools/rust-analyzer/lib/lsp-server/Cargo.toml3
-rw-r--r--src/tools/rust-analyzer/lib/lsp-server/examples/goto_def.rs132
-rwxr-xr-xsrc/tools/rust-analyzer/lib/lsp-server/examples/manual_test.sh53
-rw-r--r--src/tools/rust-analyzer/lib/lsp-server/examples/minimal_lsp.rs335
-rw-r--r--src/tools/rust-analyzer/rust-version2
-rw-r--r--src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs2
-rw-r--r--src/tools/rustbook/Cargo.lock55
-rw-r--r--src/tools/rustdoc-gui-test/src/main.rs93
-rw-r--r--src/tools/rustdoc-js/tester.js71
-rw-r--r--src/tools/rustfmt/src/items.rs4
-rw-r--r--src/tools/tidy/src/features.rs35
-rw-r--r--src/tools/tidy/src/fluent_period.rs29
-rw-r--r--src/tools/tidy/src/fluent_used.rs2
-rw-r--r--src/tools/tidy/src/issues.txt2
-rw-r--r--src/tools/tidy/src/lib.rs7
-rw-r--r--src/tools/tidy/src/mir_opt_tests.rs31
-rw-r--r--src/tools/tidy/src/rustdoc_templates.rs2
-rw-r--r--src/tools/tidy/src/style.rs46
-rw-r--r--src/tools/tidy/src/target_specific_tests.rs22
-rw-r--r--src/tools/tidy/src/ui_tests.rs43
-rw-r--r--src/tools/tidy/src/x_version.rs12
-rw-r--r--src/tools/unicode-table-generator/Cargo.toml2
-rw-r--r--src/tools/unicode-table-generator/src/cascading_map.rs14
-rw-r--r--src/tools/unicode-table-generator/src/case_mapping.rs2
-rw-r--r--src/tools/unicode-table-generator/src/main.rs40
-rw-r--r--src/tools/unicode-table-generator/src/raw_emitter.rs25
271 files changed, 5417 insertions, 2308 deletions
diff --git a/src/bootstrap/README.md b/src/bootstrap/README.md
index 6ce4c6d62fa..2965174b45b 100644
--- a/src/bootstrap/README.md
+++ b/src/bootstrap/README.md
@@ -105,7 +105,7 @@ build/
       debuginfo/
       ...
 
-    # Bootstrap host tools (which are always compiled with the stage0 compiler)
+    # Host tools (which are always compiled with the stage0 compiler)
     # are stored here.
     bootstrap-tools/
 
diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py
index d8c6be78247..40e08361a0f 100644
--- a/src/bootstrap/bootstrap.py
+++ b/src/bootstrap/bootstrap.py
@@ -8,6 +8,7 @@ import re
 import shutil
 import subprocess
 import sys
+import sysconfig
 import tarfile
 import tempfile
 
@@ -333,7 +334,11 @@ def default_build_triple(verbose):
         if ostype == "Android":
             kernel = "linux-android"
         else:
-            kernel = "unknown-linux-gnu"
+            python_soabi = sysconfig.get_config_var("SOABI")
+            if python_soabi is not None and "musl" in python_soabi:
+                kernel = "unknown-linux-musl"
+            else:
+                kernel = "unknown-linux-gnu"
     elif kernel == "SunOS":
         kernel = "pc-solaris"
         # On Solaris, uname -m will return a machine classification instead
diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs
index 3278b55305c..f0acb7f7141 100644
--- a/src/bootstrap/src/core/build_steps/check.rs
+++ b/src/bootstrap/src/core/build_steps/check.rs
@@ -4,7 +4,10 @@ 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::build_steps::tool::{
+    COMPILETEST_ALLOW_FEATURES, SourceType, ToolTargetBuildMode, get_tool_target_compiler,
+    prepare_tool_cargo,
+};
 use crate::core::builder::{
     self, Alias, Builder, Kind, RunConfig, ShouldRun, Step, StepMetadata, crate_description,
 };
@@ -252,8 +255,10 @@ fn prepare_compiler_for_check(
     mode: Mode,
 ) -> Compiler {
     let host = builder.host_target;
+
     match mode {
         Mode::ToolBootstrap => builder.compiler(0, host),
+        Mode::ToolTarget => get_tool_target_compiler(builder, ToolTargetBuildMode::Build(target)),
         Mode::ToolStd => {
             if builder.config.compile_time_deps {
                 // When --compile-time-deps is passed, we can't use any rustc
@@ -350,7 +355,7 @@ 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, build_compiler.stage);
+        rustc_cargo_env(builder, &mut cargo, target);
 
         let _guard = builder.msg_check(format!("rustc_codegen_{backend}"), target, None);
 
diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs
index 09bb2e35bda..c7e7b0160b1 100644
--- a/src/bootstrap/src/core/build_steps/compile.rs
+++ b/src/bootstrap/src/core/build_steps/compile.rs
@@ -19,7 +19,7 @@ use serde_derive::Deserialize;
 use tracing::{instrument, span};
 
 use crate::core::build_steps::gcc::{Gcc, add_cg_gcc_cargo_flags};
-use crate::core::build_steps::tool::SourceType;
+use crate::core::build_steps::tool::{SourceType, copy_lld_artifacts};
 use crate::core::build_steps::{dist, llvm};
 use crate::core::builder;
 use crate::core::builder::{
@@ -1316,15 +1316,10 @@ pub fn rustc_cargo(
         cargo.env("RUSTC_WRAPPER", ccache);
     }
 
-    rustc_cargo_env(builder, cargo, target, build_compiler.stage);
+    rustc_cargo_env(builder, cargo, target);
 }
 
-pub fn rustc_cargo_env(
-    builder: &Builder<'_>,
-    cargo: &mut Cargo,
-    target: TargetSelection,
-    build_stage: u32,
-) {
+pub fn rustc_cargo_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetSelection) {
     // Set some configuration variables picked up by build scripts and
     // the compiler alike
     cargo
@@ -1379,18 +1374,24 @@ pub fn rustc_cargo_env(
         cargo.rustflag("--cfg=llvm_enzyme");
     }
 
-    // Note that this is disabled if LLVM itself is disabled or we're in a check
-    // build. If we are in a check build we still go ahead here presuming we've
-    // detected that LLVM is already built and good to go which helps prevent
-    // busting caches (e.g. like #71152).
+    // These conditionals represent a tension between three forces:
+    // - For non-check builds, we need to define some LLVM-related environment
+    //   variables, requiring LLVM to have been built.
+    // - For check builds, we want to avoid building LLVM if possible.
+    // - Check builds and non-check builds should have the same environment if
+    //   possible, to avoid unnecessary rebuilds due to cache-busting.
+    //
+    // Therefore we try to avoid building LLVM for check builds, but only if
+    // building LLVM would be expensive. If "building" LLVM is cheap
+    // (i.e. it's already built or is downloadable), we prefer to maintain a
+    // consistent environment between check and non-check builds.
     if builder.config.llvm_enabled(target) {
-        let building_is_expensive =
+        let building_llvm_is_expensive =
             crate::core::build_steps::llvm::prebuilt_llvm_config(builder, target, false)
                 .should_build();
-        // `top_stage == stage` might be false for `check --stage 1`, if we are building the stage 1 compiler
-        let can_skip_build = builder.kind == Kind::Check && builder.top_stage == build_stage;
-        let should_skip_build = building_is_expensive && can_skip_build;
-        if !should_skip_build {
+
+        let skip_llvm = (builder.kind == Kind::Check) && building_llvm_is_expensive;
+        if !skip_llvm {
             rustc_llvm_env(builder, cargo, target)
         }
     }
@@ -1407,6 +1408,9 @@ pub fn rustc_cargo_env(
 
 /// Pass down configuration from the LLVM build into the build of
 /// rustc_llvm and rustc_codegen_llvm.
+///
+/// Note that this has the side-effect of _building LLVM_, which is sometimes
+/// unwanted (e.g. for check builds).
 fn rustc_llvm_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetSelection) {
     if builder.config.is_rust_llvm(target) {
         cargo.env("LLVM_RUSTLLVM", "1");
@@ -1665,7 +1669,7 @@ 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);
 
         // Ideally, we'd have a separate step for the individual codegen backends,
         // like we have in tests (test::CodegenGCC) but that would require a lot of restructuring.
@@ -2050,19 +2054,20 @@ impl Step for Assemble {
             }
         }
 
-        let maybe_install_llvm_bitcode_linker = |compiler| {
+        let maybe_install_llvm_bitcode_linker = || {
             if builder.config.llvm_bitcode_linker_enabled {
                 trace!("llvm-bitcode-linker enabled, installing");
-                let llvm_bitcode_linker =
-                    builder.ensure(crate::core::build_steps::tool::LlvmBitcodeLinker {
-                        build_compiler: compiler,
-                        target: target_compiler.host,
-                    });
+                let llvm_bitcode_linker = builder.ensure(
+                    crate::core::build_steps::tool::LlvmBitcodeLinker::from_target_compiler(
+                        builder,
+                        target_compiler,
+                    ),
+                );
 
                 // 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));
+                    .sysroot(target_compiler)
+                    .join(format!("lib/rustlib/{}/bin/self-contained", target_compiler.host));
                 let tool_exe = exe("llvm-bitcode-linker", target_compiler.host);
 
                 t!(fs::create_dir_all(&bindir_self_contained));
@@ -2089,9 +2094,9 @@ impl Step for Assemble {
                 builder.info(&format!("Creating a sysroot for stage{stage} compiler (use `rustup toolchain link 'name' build/host/stage{stage}`)", stage = target_compiler.stage));
             }
 
-            let mut precompiled_compiler = target_compiler;
-            precompiled_compiler.forced_compiler(true);
-            maybe_install_llvm_bitcode_linker(precompiled_compiler);
+            // FIXME: this is incomplete, we do not copy a bunch of other stuff to the downloaded
+            // sysroot...
+            maybe_install_llvm_bitcode_linker();
 
             return target_compiler;
         }
@@ -2256,10 +2261,12 @@ impl Step for Assemble {
         copy_codegen_backends_to_sysroot(builder, build_compiler, target_compiler);
 
         if builder.config.lld_enabled {
-            builder.ensure(crate::core::build_steps::tool::LldWrapper {
-                build_compiler,
-                target_compiler,
-            });
+            let lld_wrapper =
+                builder.ensure(crate::core::build_steps::tool::LldWrapper::for_use_by_compiler(
+                    builder,
+                    target_compiler,
+                ));
+            copy_lld_artifacts(builder, lld_wrapper, target_compiler);
         }
 
         if builder.config.llvm_enabled(target_compiler.host) && builder.config.llvm_tools_enabled {
@@ -2284,15 +2291,14 @@ impl Step for Assemble {
         }
 
         // In addition to `rust-lld` also install `wasm-component-ld` when
-        // LLD is enabled. This is a relatively small binary that primarily
-        // delegates to the `rust-lld` binary for linking and then runs
-        // logic to create the final binary. This is used by the
-        // `wasm32-wasip2` target of Rust.
+        // is enabled. This is used by the `wasm32-wasip2` target of Rust.
         if builder.tool_enabled("wasm-component-ld") {
-            let wasm_component = builder.ensure(crate::core::build_steps::tool::WasmComponentLd {
-                compiler: build_compiler,
-                target: target_compiler.host,
-            });
+            let wasm_component = builder.ensure(
+                crate::core::build_steps::tool::WasmComponentLd::for_use_by_compiler(
+                    builder,
+                    target_compiler,
+                ),
+            );
             builder.copy_link(
                 &wasm_component.tool_path,
                 &libdir_bin.join(wasm_component.tool_path.file_name().unwrap()),
@@ -2300,7 +2306,7 @@ impl Step for Assemble {
             );
         }
 
-        maybe_install_llvm_bitcode_linker(target_compiler);
+        maybe_install_llvm_bitcode_linker();
 
         // Ensure that `libLLVM.so` ends up in the newly build compiler directory,
         // so that it can be found when the newly built `rustc` is run.
diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs
index 8b2d65ace50..39e4fb2ac01 100644
--- a/src/bootstrap/src/core/build_steps/dist.rs
+++ b/src/bootstrap/src/core/build_steps/dist.rs
@@ -1575,7 +1575,10 @@ impl Step for Extended {
             compiler: builder.compiler(stage, target),
             backend: "cranelift".to_string(),
         });
-        add_component!("llvm-bitcode-linker" => LlvmBitcodeLinker {compiler, target});
+        add_component!("llvm-bitcode-linker" => LlvmBitcodeLinker {
+            build_compiler: compiler,
+            target
+        });
 
         let etc = builder.src.join("src/etc/installer");
 
@@ -2341,9 +2344,13 @@ impl Step for LlvmTools {
     }
 }
 
+/// Distributes the `llvm-bitcode-linker` tool so that it can be used by a compiler whose host
+/// is `target`.
 #[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
 pub struct LlvmBitcodeLinker {
-    pub compiler: Compiler,
+    /// The linker will be compiled by this compiler.
+    pub build_compiler: Compiler,
+    /// The linker will by usable by rustc on this host.
     pub target: TargetSelection,
 }
 
@@ -2359,9 +2366,8 @@ impl Step for LlvmBitcodeLinker {
 
     fn make_run(run: RunConfig<'_>) {
         run.builder.ensure(LlvmBitcodeLinker {
-            compiler: run.builder.compiler_for(
-                run.builder.top_stage,
-                run.builder.config.host_target,
+            build_compiler: tool::LlvmBitcodeLinker::get_build_compiler_for_target(
+                run.builder,
                 run.target,
             ),
             target: run.target,
@@ -2369,13 +2375,10 @@ impl Step for LlvmBitcodeLinker {
     }
 
     fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
-        let compiler = self.compiler;
         let target = self.target;
 
-        builder.ensure(compile::Rustc::new(compiler, target));
-
-        let llbc_linker =
-            builder.ensure(tool::LlvmBitcodeLinker { build_compiler: compiler, target });
+        let llbc_linker = builder
+            .ensure(tool::LlvmBitcodeLinker::from_build_compiler(self.build_compiler, target));
 
         let self_contained_bin_dir = format!("lib/rustlib/{}/bin/self-contained", target.triple);
 
diff --git a/src/bootstrap/src/core/build_steps/gcc.rs b/src/bootstrap/src/core/build_steps/gcc.rs
index 899e3fd9a45..d4cbbe60921 100644
--- a/src/bootstrap/src/core/build_steps/gcc.rs
+++ b/src/bootstrap/src/core/build_steps/gcc.rs
@@ -220,21 +220,18 @@ fn build_gcc(metadata: &Meta, builder: &Builder<'_>, target: TargetSelection) {
     t!(fs::create_dir_all(install_dir));
 
     // GCC creates files (e.g. symlinks to the downloaded dependencies)
-    // in the source directory, which does not work with our CI setup, where we mount
+    // in the source directory, which does not work with our CI/Docker setup, where we mount
     // source directories as read-only on Linux.
-    // Therefore, as a part of the build in CI, we first copy the whole source directory
-    // to the build directory, and perform the build from there.
-    let src_dir = if builder.config.is_running_on_ci {
-        let src_dir = builder.gcc_out(target).join("src");
-        if src_dir.exists() {
-            builder.remove_dir(&src_dir);
-        }
-        builder.create_dir(&src_dir);
-        builder.cp_link_r(root, &src_dir);
-        src_dir
-    } else {
-        root.clone()
-    };
+    // And in general, we shouldn't be modifying the source directories if possible, even for local
+    // builds.
+    // Therefore, we first copy the whole source directory to the build directory, and perform the
+    // build from there.
+    let src_dir = builder.gcc_out(target).join("src");
+    if src_dir.exists() {
+        builder.remove_dir(&src_dir);
+    }
+    builder.create_dir(&src_dir);
+    builder.cp_link_r(root, &src_dir);
 
     command(src_dir.join("contrib/download_prerequisites")).current_dir(&src_dir).run(builder);
     let mut configure_cmd = command(src_dir.join("configure"));
diff --git a/src/bootstrap/src/core/build_steps/install.rs b/src/bootstrap/src/core/build_steps/install.rs
index 4434d6658eb..4156b49a8b3 100644
--- a/src/bootstrap/src/core/build_steps/install.rs
+++ b/src/bootstrap/src/core/build_steps/install.rs
@@ -287,7 +287,7 @@ install!((self, builder, _config),
         }
     };
     LlvmBitcodeLinker, alias = "llvm-bitcode-linker", Self::should_build(_config), only_hosts: true, {
-        if let Some(tarball) = builder.ensure(dist::LlvmBitcodeLinker { compiler: self.compiler, target: self.target }) {
+        if let Some(tarball) = builder.ensure(dist::LlvmBitcodeLinker { build_compiler: self.compiler, target: self.target }) {
             install_sh(builder, "llvm-bitcode-linker", self.compiler.stage, Some(self.target), &tarball);
         } else {
             builder.info(
diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs
index 9e7ea5c115f..7652ea1a488 100644
--- a/src/bootstrap/src/core/build_steps/test.rs
+++ b/src/bootstrap/src/core/build_steps/test.rs
@@ -1757,6 +1757,10 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
         cmd.arg("--host").arg(&*compiler.host.triple);
         cmd.arg("--llvm-filecheck").arg(builder.llvm_filecheck(builder.config.host_target));
 
+        if let Some(codegen_backend) = builder.config.default_codegen_backend(compiler.host) {
+            cmd.arg("--codegen-backend").arg(&codegen_backend);
+        }
+
         if builder.build.config.llvm_enzyme {
             cmd.arg("--has-enzyme");
         }
@@ -1810,7 +1814,24 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
         }
 
         let mut flags = if is_rustdoc { Vec::new() } else { vec!["-Crpath".to_string()] };
-        flags.push(format!("-Cdebuginfo={}", builder.config.rust_debuginfo_level_tests));
+        flags.push(format!(
+            "-Cdebuginfo={}",
+            if suite == "codegen" {
+                // codegen tests typically check LLVM IR and are sensitive to additional debuginfo.
+                // So do not apply `rust.debuginfo-level-tests` for codegen tests.
+                if builder.config.rust_debuginfo_level_tests
+                    != crate::core::config::DebuginfoLevel::None
+                {
+                    println!(
+                        "NOTE: ignoring `rust.debuginfo-level-tests={}` for codegen tests",
+                        builder.config.rust_debuginfo_level_tests
+                    );
+                }
+                crate::core::config::DebuginfoLevel::None
+            } else {
+                builder.config.rust_debuginfo_level_tests
+            }
+        ));
         flags.extend(builder.config.cmd.compiletest_rustc_args().iter().map(|s| s.to_string()));
 
         if suite != "mir-opt" {
@@ -2945,7 +2966,8 @@ impl Step for RemoteCopyLibs {
 
         builder.info(&format!("REMOTE copy libs to emulator ({target})"));
 
-        let remote_test_server = builder.ensure(tool::RemoteTestServer { compiler, target });
+        let remote_test_server =
+            builder.ensure(tool::RemoteTestServer { build_compiler: compiler, target });
 
         // Spawn the emulator and wait for it to come online
         let tool = builder.tool_exe(Tool::RemoteTestClient);
@@ -3386,7 +3408,7 @@ impl Step for CodegenCranelift {
             cargo
                 .arg("--manifest-path")
                 .arg(builder.src.join("compiler/rustc_codegen_cranelift/build_system/Cargo.toml"));
-            compile::rustc_cargo_env(builder, &mut cargo, target, compiler.stage);
+            compile::rustc_cargo_env(builder, &mut cargo, target);
 
             // Avoid incremental cache issues when changing rustc
             cargo.env("CARGO_BUILD_INCREMENTAL", "false");
@@ -3518,7 +3540,7 @@ impl Step for CodegenGCC {
             cargo
                 .arg("--manifest-path")
                 .arg(builder.src.join("compiler/rustc_codegen_gcc/build_system/Cargo.toml"));
-            compile::rustc_cargo_env(builder, &mut cargo, target, compiler.stage);
+            compile::rustc_cargo_env(builder, &mut cargo, target);
             add_cg_gcc_cargo_flags(&mut cargo, &gcc);
 
             // Avoid incremental cache issues when changing rustc
diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs
index 1c994b0ccfc..f5fa33b98f3 100644
--- a/src/bootstrap/src/core/build_steps/tool.rs
+++ b/src/bootstrap/src/core/build_steps/tool.rs
@@ -42,7 +42,8 @@ pub enum ToolArtifactKind {
 
 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
 struct ToolBuild {
-    compiler: Compiler,
+    /// Compiler that will build this tool.
+    build_compiler: Compiler,
     target: TargetSelection,
     tool: &'static str,
     path: &'static str,
@@ -112,34 +113,34 @@ impl Step for ToolBuild {
         let mut tool = self.tool;
         let path = self.path;
 
-        let target_compiler = self.compiler;
-        self.compiler = if self.mode == Mode::ToolRustc {
-            get_tool_rustc_compiler(builder, self.compiler)
+        let target_compiler = self.build_compiler;
+        self.build_compiler = if self.mode == Mode::ToolRustc {
+            get_tool_rustc_compiler(builder, self.build_compiler)
         } else {
-            self.compiler
+            self.build_compiler
         };
 
         match self.mode {
             Mode::ToolRustc => {
                 // If compiler was forced, its artifacts should have been prepared earlier.
-                if !self.compiler.is_forced_compiler() {
-                    builder.std(self.compiler, self.compiler.host);
-                    builder.ensure(compile::Rustc::new(self.compiler, target));
+                if !self.build_compiler.is_forced_compiler() {
+                    builder.std(self.build_compiler, self.build_compiler.host);
+                    builder.ensure(compile::Rustc::new(self.build_compiler, target));
                 }
             }
             Mode::ToolStd => {
                 // If compiler was forced, its artifacts should have been prepared earlier.
-                if !self.compiler.is_forced_compiler() {
-                    builder.std(self.compiler, target)
+                if !self.build_compiler.is_forced_compiler() {
+                    builder.std(self.build_compiler, target);
                 }
             }
-            Mode::ToolBootstrap => {} // uses downloaded stage0 compiler libs
+            Mode::ToolBootstrap | Mode::ToolTarget => {} // uses downloaded stage0 compiler libs
             _ => panic!("unexpected Mode for tool build"),
         }
 
         let mut cargo = prepare_tool_cargo(
             builder,
-            self.compiler,
+            self.build_compiler,
             self.mode,
             target,
             Kind::Build,
@@ -161,7 +162,7 @@ impl Step for ToolBuild {
 
         // Rustc tools (miri, clippy, cargo, rustfmt, rust-analyzer)
         // could use the additional optimizations.
-        if self.mode == Mode::ToolRustc && is_lto_stage(&self.compiler) {
+        if self.mode == Mode::ToolRustc && is_lto_stage(&self.build_compiler) {
             let lto = match builder.config.rust_lto {
                 RustcLto::Off => Some("off"),
                 RustcLto::Thin => Some("thin"),
@@ -183,8 +184,9 @@ impl Step for ToolBuild {
             Kind::Build,
             self.mode,
             self.tool,
-            self.compiler.stage,
-            &self.compiler.host,
+            // A stage N tool is built with the stage N-1 compiler.
+            self.build_compiler.stage + 1,
+            &self.build_compiler.host,
             &self.target,
         );
 
@@ -207,14 +209,14 @@ impl Step for ToolBuild {
             }
             let tool_path = match self.artifact_kind {
                 ToolArtifactKind::Binary => {
-                    copy_link_tool_bin(builder, self.compiler, self.target, self.mode, tool)
+                    copy_link_tool_bin(builder, self.build_compiler, self.target, self.mode, tool)
                 }
                 ToolArtifactKind::Library => builder
-                    .cargo_out(self.compiler, self.mode, self.target)
+                    .cargo_out(self.build_compiler, self.mode, self.target)
                     .join(format!("lib{tool}.rlib")),
             };
 
-            ToolBuildResult { tool_path, build_compiler: self.compiler, target_compiler }
+            ToolBuildResult { tool_path, build_compiler: self.build_compiler, target_compiler }
         }
     }
 }
@@ -365,6 +367,47 @@ pub(crate) fn get_tool_rustc_compiler(
     builder.compiler(target_compiler.stage.saturating_sub(1), builder.config.host_target)
 }
 
+/// Determines how to build a `ToolTarget`, i.e. which compiler should be used to compile it.
+/// The compiler stage is automatically bumped if we need to cross-compile a stage 1 tool.
+pub enum ToolTargetBuildMode {
+    /// Build the tool using rustc that corresponds to the selected CLI stage.
+    Build(TargetSelection),
+    /// Build the tool so that it can be attached to the sysroot of the passed compiler.
+    /// Since we always dist stage 2+, the compiler that builds the tool in this case has to be
+    /// stage 1+.
+    Dist(Compiler),
+}
+
+/// Returns compiler that is able to compile a `ToolTarget` tool with the given `mode`.
+pub(crate) fn get_tool_target_compiler(
+    builder: &Builder<'_>,
+    mode: ToolTargetBuildMode,
+) -> Compiler {
+    let (target, build_compiler_stage) = match mode {
+        ToolTargetBuildMode::Build(target) => {
+            assert!(builder.top_stage > 0);
+            // If we want to build a stage N tool, we need to compile it with stage N-1 rustc
+            (target, builder.top_stage - 1)
+        }
+        ToolTargetBuildMode::Dist(target_compiler) => {
+            assert!(target_compiler.stage > 0);
+            // If we want to dist a stage N rustc, we want to attach stage N tool to it.
+            // And to build that tool, we need to compile it with stage N-1 rustc
+            (target_compiler.host, target_compiler.stage - 1)
+        }
+    };
+
+    let compiler = if builder.host_target == target {
+        builder.compiler(build_compiler_stage, builder.host_target)
+    } else {
+        // If we are cross-compiling a stage 1 tool, we cannot do that with a stage 0 compiler,
+        // so we auto-bump the tool's stage to 2, which means we need a stage 1 compiler.
+        builder.compiler(build_compiler_stage.max(1), builder.host_target)
+    };
+    builder.std(compiler, target);
+    compiler
+}
+
 /// Links a built tool binary with the given `name` from the build directory to the
 /// tools directory.
 fn copy_link_tool_bin(
@@ -451,7 +494,7 @@ macro_rules! bootstrap_tool {
                 let compiletest_wants_stage0 = $tool_name == "compiletest" && builder.config.compiletest_use_stage0_libtest;
 
                 builder.ensure(ToolBuild {
-                    compiler: self.compiler,
+                    build_compiler: self.compiler,
                     target: self.target,
                     tool: $tool_name,
                     mode: if is_unstable && !compiletest_wants_stage0 {
@@ -521,7 +564,6 @@ bootstrap_tool!(
     // 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;
     CoverageDump, "src/tools/coverage-dump", "coverage-dump";
-    WasmComponentLd, "src/tools/wasm-component-ld", "wasm-component-ld", is_unstable_tool = true, allow_features = "min_specialization";
     UnicodeTableGenerator, "src/tools/unicode-table-generator", "unicode-table-generator";
     FeaturesStatusDump, "src/tools/features-status-dump", "features-status-dump";
     OptimizedDist, "src/tools/opt-dist", "opt-dist", submodules = &["src/tools/rustc-perf"];
@@ -560,7 +602,7 @@ impl Step for RustcPerf {
         builder.require_submodule("src/tools/rustc-perf", None);
 
         let tool = ToolBuild {
-            compiler: self.compiler,
+            build_compiler: self.compiler,
             target: self.target,
             tool: "collector",
             mode: Mode::ToolBootstrap,
@@ -576,7 +618,7 @@ impl Step for RustcPerf {
         let res = builder.ensure(tool.clone());
         // We also need to symlink the `rustc-fake` binary to the corresponding directory,
         // because `collector` expects it in the same directory.
-        copy_link_tool_bin(builder, tool.compiler, tool.target, tool.mode, "rustc-fake");
+        copy_link_tool_bin(builder, tool.build_compiler, tool.target, tool.mode, "rustc-fake");
 
         res
     }
@@ -620,7 +662,7 @@ impl Step for ErrorIndex {
 
     fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
         builder.ensure(ToolBuild {
-            compiler: self.compiler,
+            build_compiler: self.compiler,
             target: self.compiler.host,
             tool: "error_index_generator",
             mode: Mode::ToolRustc,
@@ -636,7 +678,7 @@ impl Step for ErrorIndex {
 
 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct RemoteTestServer {
-    pub compiler: Compiler,
+    pub build_compiler: Compiler,
     pub target: TargetSelection,
 }
 
@@ -649,17 +691,20 @@ impl Step for RemoteTestServer {
 
     fn make_run(run: RunConfig<'_>) {
         run.builder.ensure(RemoteTestServer {
-            compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.host_target),
+            build_compiler: get_tool_target_compiler(
+                run.builder,
+                ToolTargetBuildMode::Build(run.target),
+            ),
             target: run.target,
         });
     }
 
     fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
         builder.ensure(ToolBuild {
-            compiler: self.compiler,
+            build_compiler: self.build_compiler,
             target: self.target,
             tool: "remote-test-server",
-            mode: Mode::ToolStd,
+            mode: Mode::ToolTarget,
             path: "src/tools/remote-test-server",
             source_type: SourceType::InTree,
             extra_features: Vec::new(),
@@ -668,6 +713,10 @@ impl Step for RemoteTestServer {
             artifact_kind: ToolArtifactKind::Binary,
         })
     }
+
+    fn metadata(&self) -> Option<StepMetadata> {
+        Some(StepMetadata::build("remote-test-server", self.target).built_by(self.build_compiler))
+    }
 }
 
 #[derive(Debug, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]
@@ -757,7 +806,7 @@ impl Step for Rustdoc {
 
         let ToolBuildResult { tool_path, build_compiler, target_compiler } =
             builder.ensure(ToolBuild {
-                compiler: target_compiler,
+                build_compiler: target_compiler,
                 target,
                 // Cargo adds a number of paths to the dylib search path on windows, which results in
                 // the wrong rustdoc being executed. To avoid the conflicting rustdocs, we name the "tool"
@@ -825,7 +874,7 @@ impl Step for Cargo {
         builder.build.require_submodule("src/tools/cargo", None);
 
         builder.ensure(ToolBuild {
-            compiler: self.compiler,
+            build_compiler: self.compiler,
             target: self.target,
             tool: "cargo",
             mode: Mode::ToolRustc,
@@ -839,17 +888,50 @@ impl Step for Cargo {
     }
 }
 
+/// Represents a built LldWrapper, the `lld-wrapper` tool itself, and a directory
+/// containing a build of LLD.
+#[derive(Clone)]
+pub struct BuiltLldWrapper {
+    tool: ToolBuildResult,
+    lld_dir: PathBuf,
+}
+
 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct LldWrapper {
     pub build_compiler: Compiler,
-    pub target_compiler: Compiler,
+    pub target: TargetSelection,
+}
+
+impl LldWrapper {
+    /// Returns `LldWrapper` that should be **used** by the passed compiler.
+    pub fn for_use_by_compiler(builder: &Builder<'_>, target_compiler: Compiler) -> Self {
+        Self {
+            build_compiler: get_tool_target_compiler(
+                builder,
+                ToolTargetBuildMode::Dist(target_compiler),
+            ),
+            target: target_compiler.host,
+        }
+    }
 }
 
 impl Step for LldWrapper {
-    type Output = ToolBuildResult;
+    type Output = BuiltLldWrapper;
+
+    const ONLY_HOSTS: bool = true;
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
-        run.never()
+        run.path("src/tools/lld-wrapper")
+    }
+
+    fn make_run(run: RunConfig<'_>) {
+        run.builder.ensure(LldWrapper {
+            build_compiler: get_tool_target_compiler(
+                run.builder,
+                ToolTargetBuildMode::Build(run.target),
+            ),
+            target: run.target,
+        });
     }
 
     #[cfg_attr(
@@ -858,25 +940,16 @@ impl Step for LldWrapper {
             level = "debug",
             name = "LldWrapper::run",
             skip_all,
-            fields(build_compiler = ?self.build_compiler, target_compiler = ?self.target_compiler),
+            fields(build_compiler = ?self.build_compiler),
         ),
     )]
-    fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
-        if builder.config.dry_run() {
-            return ToolBuildResult {
-                tool_path: Default::default(),
-                build_compiler: self.build_compiler,
-                target_compiler: self.target_compiler,
-            };
-        }
-
-        let target = self.target_compiler.host;
-
-        let tool_result = builder.ensure(ToolBuild {
-            compiler: self.build_compiler,
-            target,
+    fn run(self, builder: &Builder<'_>) -> Self::Output {
+        let lld_dir = builder.ensure(llvm::Lld { target: self.target });
+        let tool = builder.ensure(ToolBuild {
+            build_compiler: self.build_compiler,
+            target: self.target,
             tool: "lld-wrapper",
-            mode: Mode::ToolStd,
+            mode: Mode::ToolTarget,
             path: "src/tools/lld-wrapper",
             source_type: SourceType::InTree,
             extra_features: Vec::new(),
@@ -884,38 +957,110 @@ impl Step for LldWrapper {
             cargo_args: Vec::new(),
             artifact_kind: ToolArtifactKind::Binary,
         });
+        BuiltLldWrapper { tool, lld_dir }
+    }
 
-        let libdir_bin = builder.sysroot_target_bindir(self.target_compiler, target);
-        t!(fs::create_dir_all(&libdir_bin));
+    fn metadata(&self) -> Option<StepMetadata> {
+        Some(StepMetadata::build("LldWrapper", self.target).built_by(self.build_compiler))
+    }
+}
 
-        let lld_install = builder.ensure(llvm::Lld { target });
-        let src_exe = exe("lld", target);
-        let dst_exe = exe("rust-lld", target);
+pub(crate) fn copy_lld_artifacts(
+    builder: &Builder<'_>,
+    lld_wrapper: BuiltLldWrapper,
+    target_compiler: Compiler,
+) {
+    let target = target_compiler.host;
 
+    let libdir_bin = builder.sysroot_target_bindir(target_compiler, target);
+    t!(fs::create_dir_all(&libdir_bin));
+
+    let src_exe = exe("lld", target);
+    let dst_exe = exe("rust-lld", target);
+
+    builder.copy_link(
+        &lld_wrapper.lld_dir.join("bin").join(src_exe),
+        &libdir_bin.join(dst_exe),
+        FileType::Executable,
+    );
+    let self_contained_lld_dir = libdir_bin.join("gcc-ld");
+    t!(fs::create_dir_all(&self_contained_lld_dir));
+
+    for name in crate::LLD_FILE_NAMES {
         builder.copy_link(
-            &lld_install.join("bin").join(src_exe),
-            &libdir_bin.join(dst_exe),
+            &lld_wrapper.tool.tool_path,
+            &self_contained_lld_dir.join(exe(name, target)),
             FileType::Executable,
         );
-        let self_contained_lld_dir = libdir_bin.join("gcc-ld");
-        t!(fs::create_dir_all(&self_contained_lld_dir));
-
-        for name in crate::LLD_FILE_NAMES {
-            builder.copy_link(
-                &tool_result.tool_path,
-                &self_contained_lld_dir.join(exe(name, target)),
-                FileType::Executable,
-            );
+    }
+}
+
+/// Builds the `wasm-component-ld` linker wrapper, which is shipped with rustc to be executed on the
+/// host platform where rustc runs.
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
+pub struct WasmComponentLd {
+    build_compiler: Compiler,
+    target: TargetSelection,
+}
+
+impl WasmComponentLd {
+    /// Returns `WasmComponentLd` that should be **used** by the passed compiler.
+    pub fn for_use_by_compiler(builder: &Builder<'_>, target_compiler: Compiler) -> Self {
+        Self {
+            build_compiler: get_tool_target_compiler(
+                builder,
+                ToolTargetBuildMode::Dist(target_compiler),
+            ),
+            target: target_compiler.host,
         }
+    }
+}
+
+impl Step for WasmComponentLd {
+    type Output = ToolBuildResult;
+
+    const ONLY_HOSTS: bool = true;
+
+    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
+        run.path("src/tools/wasm-component-ld")
+    }
+
+    fn make_run(run: RunConfig<'_>) {
+        run.builder.ensure(WasmComponentLd {
+            build_compiler: get_tool_target_compiler(
+                run.builder,
+                ToolTargetBuildMode::Build(run.target),
+            ),
+            target: run.target,
+        });
+    }
 
-        tool_result
+    #[cfg_attr(
+        feature = "tracing",
+        instrument(
+            level = "debug",
+            name = "WasmComponentLd::run",
+            skip_all,
+            fields(build_compiler = ?self.build_compiler),
+        ),
+    )]
+    fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
+        builder.ensure(ToolBuild {
+            build_compiler: self.build_compiler,
+            target: self.target,
+            tool: "wasm-component-ld",
+            mode: Mode::ToolTarget,
+            path: "src/tools/wasm-component-ld",
+            source_type: SourceType::InTree,
+            extra_features: vec![],
+            allow_features: "",
+            cargo_args: vec![],
+            artifact_kind: ToolArtifactKind::Binary,
+        })
     }
 
     fn metadata(&self) -> Option<StepMetadata> {
-        Some(
-            StepMetadata::build("LldWrapper", self.target_compiler.host)
-                .built_by(self.build_compiler),
-        )
+        Some(StepMetadata::build("WasmComponentLd", self.target).built_by(self.build_compiler))
     }
 }
 
@@ -948,7 +1093,7 @@ impl Step for RustAnalyzer {
 
     fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
         builder.ensure(ToolBuild {
-            compiler: self.compiler,
+            build_compiler: self.compiler,
             target: self.target,
             tool: "rust-analyzer",
             mode: Mode::ToolRustc,
@@ -993,7 +1138,7 @@ impl Step for RustAnalyzerProcMacroSrv {
 
     fn run(self, builder: &Builder<'_>) -> Option<ToolBuildResult> {
         let tool_result = builder.ensure(ToolBuild {
-            compiler: self.compiler,
+            build_compiler: self.compiler,
             target: self.target,
             tool: "rust-analyzer-proc-macro-srv",
             mode: Mode::ToolRustc,
@@ -1021,8 +1166,35 @@ impl Step for RustAnalyzerProcMacroSrv {
 
 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct LlvmBitcodeLinker {
-    pub build_compiler: Compiler,
-    pub target: TargetSelection,
+    build_compiler: Compiler,
+    target: TargetSelection,
+}
+
+impl LlvmBitcodeLinker {
+    /// Returns `LlvmBitcodeLinker` that will be **compiled** by the passed compiler, for the given
+    /// `target`.
+    pub fn from_build_compiler(build_compiler: Compiler, target: TargetSelection) -> Self {
+        Self { build_compiler, target }
+    }
+
+    /// Returns `LlvmBitcodeLinker` that should be **used** by the passed compiler.
+    pub fn from_target_compiler(builder: &Builder<'_>, target_compiler: Compiler) -> Self {
+        Self {
+            build_compiler: get_tool_target_compiler(
+                builder,
+                ToolTargetBuildMode::Dist(target_compiler),
+            ),
+            target: target_compiler.host,
+        }
+    }
+
+    /// Return a compiler that is able to build this tool for the given `target`.
+    pub fn get_build_compiler_for_target(
+        builder: &Builder<'_>,
+        target: TargetSelection,
+    ) -> Compiler {
+        get_tool_target_compiler(builder, ToolTargetBuildMode::Build(target))
+    }
 }
 
 impl Step for LlvmBitcodeLinker {
@@ -1038,9 +1210,7 @@ impl Step for LlvmBitcodeLinker {
 
     fn make_run(run: RunConfig<'_>) {
         run.builder.ensure(LlvmBitcodeLinker {
-            build_compiler: run
-                .builder
-                .compiler(run.builder.top_stage, run.builder.config.host_target),
+            build_compiler: Self::get_build_compiler_for_target(run.builder, run.target),
             target: run.target,
         });
     }
@@ -1051,10 +1221,10 @@ impl Step for LlvmBitcodeLinker {
     )]
     fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
         builder.ensure(ToolBuild {
-            compiler: self.build_compiler,
+            build_compiler: self.build_compiler,
             target: self.target,
             tool: "llvm-bitcode-linker",
-            mode: Mode::ToolRustc,
+            mode: Mode::ToolTarget,
             path: "src/tools/llvm-bitcode-linker",
             source_type: SourceType::InTree,
             extra_features: vec![],
@@ -1239,7 +1409,7 @@ fn run_tool_build_step(
 
     let ToolBuildResult { tool_path, build_compiler, target_compiler } =
         builder.ensure(ToolBuild {
-            compiler,
+            build_compiler: compiler,
             target,
             tool: tool_name,
             mode: Mode::ToolRustc,
@@ -1338,7 +1508,7 @@ impl Step for TestFloatParse {
         let compiler = builder.compiler(builder.top_stage, bootstrap_host);
 
         builder.ensure(ToolBuild {
-            compiler,
+            build_compiler: compiler,
             target: bootstrap_host,
             tool: "test-float-parse",
             mode: Mode::ToolStd,
diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs
index a3b471ca56e..badd5f24dba 100644
--- a/src/bootstrap/src/core/builder/cargo.rs
+++ b/src/bootstrap/src/core/builder/cargo.rs
@@ -537,7 +537,7 @@ impl Builder<'_> {
             }
         }
 
-        let stage = if compiler.stage == 0 && self.local_rebuild {
+        let build_compiler_stage = if compiler.stage == 0 && self.local_rebuild {
             // Assume the local-rebuild rustc already has stage1 features.
             1
         } else {
@@ -545,15 +545,17 @@ impl Builder<'_> {
         };
 
         // We synthetically interpret a stage0 compiler used to build tools as a
-        // "raw" compiler in that it's the exact snapshot we download. Normally
-        // the stage0 build means it uses libraries build by the stage0
-        // compiler, but for tools we just use the precompiled libraries that
-        // we've downloaded
-        let use_snapshot = mode == Mode::ToolBootstrap;
-        assert!(!use_snapshot || stage == 0 || self.local_rebuild);
-
-        let maybe_sysroot = self.sysroot(compiler);
-        let sysroot = if use_snapshot { self.rustc_snapshot_sysroot() } else { &maybe_sysroot };
+        // "raw" compiler in that it's the exact snapshot we download. For things like
+        // ToolRustc, we would have to use the artificial stage0-sysroot compiler instead.
+        let use_snapshot =
+            mode == Mode::ToolBootstrap || (mode == Mode::ToolTarget && build_compiler_stage == 0);
+        assert!(!use_snapshot || build_compiler_stage == 0 || self.local_rebuild);
+
+        let sysroot = if use_snapshot {
+            self.rustc_snapshot_sysroot().to_path_buf()
+        } else {
+            self.sysroot(compiler)
+        };
         let libdir = self.rustc_libdir(compiler);
 
         let sysroot_str = sysroot.as_os_str().to_str().expect("sysroot should be UTF-8");
@@ -562,7 +564,7 @@ impl Builder<'_> {
         }
 
         let mut rustflags = Rustflags::new(target);
-        if stage != 0 {
+        if build_compiler_stage != 0 {
             if let Ok(s) = env::var("CARGOFLAGS_NOT_BOOTSTRAP") {
                 cargo.args(s.split_whitespace());
             }
@@ -604,7 +606,7 @@ impl Builder<'_> {
         // sysroot. Passing this cfg enables raw-dylib support instead, which makes the native
         // library unnecessary. This can be removed when windows-rs enables raw-dylib
         // unconditionally.
-        if let Mode::Rustc | Mode::ToolRustc | Mode::ToolBootstrap = mode {
+        if let Mode::Rustc | Mode::ToolRustc | Mode::ToolBootstrap | Mode::ToolTarget = mode {
             rustflags.arg("--cfg=windows_raw_dylib");
         }
 
@@ -657,7 +659,7 @@ impl Builder<'_> {
         // FIXME(rust-lang/cargo#5754) we shouldn't be using special command arguments
         // to the host invocation here, but rather Cargo should know what flags to pass rustc
         // itself.
-        if stage == 0 {
+        if build_compiler_stage == 0 {
             hostflags.arg("--cfg=bootstrap");
         }
 
@@ -666,7 +668,7 @@ impl Builder<'_> {
         // #71458.
         let mut rustdocflags = rustflags.clone();
         rustdocflags.propagate_cargo_env("RUSTDOCFLAGS");
-        if stage == 0 {
+        if build_compiler_stage == 0 {
             rustdocflags.env("RUSTDOCFLAGS_BOOTSTRAP");
         } else {
             rustdocflags.env("RUSTDOCFLAGS_NOT_BOOTSTRAP");
@@ -677,7 +679,7 @@ impl Builder<'_> {
         }
 
         match mode {
-            Mode::Std | Mode::ToolBootstrap | Mode::ToolStd => {}
+            Mode::Std | Mode::ToolBootstrap | Mode::ToolStd | Mode::ToolTarget => {}
             Mode::Rustc | Mode::Codegen | Mode::ToolRustc => {
                 // Build proc macros both for the host and the target unless proc-macros are not
                 // supported by the target.
@@ -719,7 +721,7 @@ impl Builder<'_> {
         // feature on the rustc side.
         cargo.arg("-Zbinary-dep-depinfo");
         let allow_features = match mode {
-            Mode::ToolBootstrap | Mode::ToolStd => {
+            Mode::ToolBootstrap | Mode::ToolStd | Mode::ToolTarget => {
                 // Restrict the allowed features so we don't depend on nightly
                 // accidentally.
                 //
@@ -833,7 +835,7 @@ impl Builder<'_> {
         cargo
             .env("RUSTBUILD_NATIVE_DIR", self.native_dir(target))
             .env("RUSTC_REAL", self.rustc(compiler))
-            .env("RUSTC_STAGE", stage.to_string())
+            .env("RUSTC_STAGE", build_compiler_stage.to_string())
             .env("RUSTC_SYSROOT", sysroot)
             .env("RUSTC_LIBDIR", libdir)
             .env("RUSTDOC", self.bootstrap_out.join("rustdoc"))
@@ -878,7 +880,7 @@ impl Builder<'_> {
         let debuginfo_level = match mode {
             Mode::Rustc | Mode::Codegen => self.config.rust_debuginfo_level_rustc,
             Mode::Std => self.config.rust_debuginfo_level_std,
-            Mode::ToolBootstrap | Mode::ToolStd | Mode::ToolRustc => {
+            Mode::ToolBootstrap | Mode::ToolStd | Mode::ToolRustc | Mode::ToolTarget => {
                 self.config.rust_debuginfo_level_tools
             }
         };
@@ -890,11 +892,10 @@ impl Builder<'_> {
             profile_var("DEBUG_ASSERTIONS"),
             match mode {
                 Mode::Std => self.config.std_debug_assertions,
-                Mode::Rustc => self.config.rustc_debug_assertions,
-                Mode::Codegen => self.config.rustc_debug_assertions,
-                Mode::ToolBootstrap => self.config.tools_debug_assertions,
-                Mode::ToolStd => self.config.tools_debug_assertions,
-                Mode::ToolRustc => self.config.tools_debug_assertions,
+                Mode::Rustc | Mode::Codegen => self.config.rustc_debug_assertions,
+                Mode::ToolBootstrap | Mode::ToolStd | Mode::ToolRustc | Mode::ToolTarget => {
+                    self.config.tools_debug_assertions
+                }
             }
             .to_string(),
         );
@@ -965,7 +966,11 @@ impl Builder<'_> {
                     cargo.env("CFG_VIRTUAL_RUSTC_DEV_SOURCE_BASE_DIR", map_to);
                 }
             }
-            Mode::Std | Mode::ToolBootstrap | Mode::ToolRustc | Mode::ToolStd => {
+            Mode::Std
+            | Mode::ToolBootstrap
+            | Mode::ToolRustc
+            | Mode::ToolStd
+            | Mode::ToolTarget => {
                 if let Some(ref map_to) =
                     self.build.debuginfo_map_to(GitRepo::Rustc, RemapScheme::NonCompiler)
                 {
@@ -1280,7 +1285,7 @@ impl Builder<'_> {
             };
 
             if let Some(limit) = limit
-                && (stage == 0
+                && (build_compiler_stage == 0
                     || self.config.default_codegen_backend(target).unwrap_or_default() == "llvm")
             {
                 rustflags.arg(&format!("-Cllvm-args=-import-instr-limit={limit}"));
diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs
index 1b75d00b30e..d73e2bce25b 100644
--- a/src/bootstrap/src/core/builder/mod.rs
+++ b/src/bootstrap/src/core/builder/mod.rs
@@ -963,6 +963,7 @@ impl<'a> Builder<'a> {
                 tool::RemoteTestServer,
                 tool::RemoteTestClient,
                 tool::RustInstaller,
+                tool::FeaturesStatusDump,
                 tool::Cargo,
                 tool::RustAnalyzer,
                 tool::RustAnalyzerProcMacroSrv,
@@ -984,6 +985,8 @@ impl<'a> Builder<'a> {
                 tool::CoverageDump,
                 tool::LlvmBitcodeLinker,
                 tool::RustcPerf,
+                tool::WasmComponentLd,
+                tool::LldWrapper
             ),
             Kind::Clippy => describe!(
                 clippy::Std,
diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs
index 51a90649692..e60a115b19d 100644
--- a/src/bootstrap/src/core/builder/tests.rs
+++ b/src/bootstrap/src/core/builder/tests.rs
@@ -712,7 +712,11 @@ mod snapshot {
         [build] llvm <host>
         [build] rustc 0 <host> -> rustc 1 <host>
         ");
+    }
 
+    #[test]
+    fn build_rustc_no_explicit_stage() {
+        let ctx = TestCtx::new();
         insta::assert_snapshot!(
             ctx.config("build")
                 .path("rustc")
@@ -769,11 +773,11 @@ mod snapshot {
         [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 0 <host> -> LlvmBitcodeLinker 1 <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> -> LlvmBitcodeLinker 2 <host>
         [build] rustc 2 <host> -> std 2 <host>
         [build] rustdoc 1 <host>
         "
@@ -793,17 +797,17 @@ mod snapshot {
         [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 0 <host> -> LlvmBitcodeLinker 1 <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> -> LlvmBitcodeLinker 2 <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] rustc 1 <host> -> LlvmBitcodeLinker 2 <target1>
         [build] rustdoc 1 <target1>
         "
         );
@@ -1062,18 +1066,28 @@ mod snapshot {
     fn dist_extended() {
         let ctx = TestCtx::new();
         insta::assert_snapshot!(
-            ctx
-                .config("dist")
-                .args(&["--set", "build.extended=true"])
-                .render_steps(), @r"
+            ctx.config("dist")
+            .args(&[
+                "--set",
+                "build.extended=true",
+                "--set",
+                "rust.llvm-bitcode-linker=true",
+                "--set",
+                "rust.lld=true",
+            ])
+            .render_steps(), @r"
         [build] rustc 0 <host> -> UnstableBookGen 1 <host>
         [build] rustc 0 <host> -> Rustbook 1 <host>
         [build] llvm <host>
         [build] rustc 0 <host> -> rustc 1 <host>
+        [build] rustc 0 <host> -> LldWrapper 1 <host>
         [build] rustc 0 <host> -> WasmComponentLd 1 <host>
+        [build] rustc 0 <host> -> LlvmBitcodeLinker 1 <host>
         [build] rustc 1 <host> -> std 1 <host>
         [build] rustc 1 <host> -> rustc 2 <host>
+        [build] rustc 1 <host> -> LldWrapper 2 <host>
         [build] rustc 1 <host> -> WasmComponentLd 2 <host>
+        [build] rustc 1 <host> -> LlvmBitcodeLinker 2 <host>
         [build] rustdoc 1 <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>
@@ -1092,7 +1106,6 @@ 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>
         ");
     }
 
@@ -1294,17 +1307,19 @@ mod snapshot {
             ctx.config("check")
                 .path("compiler")
                 .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>
         ");
+    }
 
+    #[test]
+    fn check_rustc_no_explicit_stage() {
+        let ctx = TestCtx::new();
         insta::assert_snapshot!(
             ctx.config("check")
                 .path("rustc")
                 .render_steps(), @r"
-        [build] llvm <host>
         [check] rustc 0 <host> -> rustc 1 <host>
         ");
     }
@@ -1324,7 +1339,6 @@ mod snapshot {
                 .path("compiler")
                 .stage(1)
                 .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>
@@ -1456,7 +1470,6 @@ mod snapshot {
                 .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>
@@ -1470,7 +1483,6 @@ mod snapshot {
             ctx.config("check")
                 .path("miri")
                 .render_steps(), @r"
-        [build] llvm <host>
         [check] rustc 0 <host> -> rustc 1 <host>
         [check] rustc 0 <host> -> Miri 1 <host>
         ");
@@ -1491,7 +1503,6 @@ mod snapshot {
                 .path("miri")
                 .stage(1)
                 .render_steps(), @r"
-        [build] llvm <host>
         [check] rustc 0 <host> -> rustc 1 <host>
         [check] rustc 0 <host> -> Miri 1 <host>
         ");
@@ -1544,7 +1555,6 @@ mod snapshot {
             ctx.config("check")
                 .path("rustc_codegen_cranelift")
                 .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>
@@ -1558,7 +1568,6 @@ mod snapshot {
             ctx.config("check")
                 .path("rust-analyzer")
                 .render_steps(), @r"
-        [build] llvm <host>
         [check] rustc 0 <host> -> rustc 1 <host>
         [check] rustc 0 <host> -> rust-analyzer 1 <host>
         ");
diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs
index 28958b60fc3..22a75183404 100644
--- a/src/bootstrap/src/core/config/config.rs
+++ b/src/bootstrap/src/core/config/config.rs
@@ -697,7 +697,7 @@ impl Config {
         config.change_id = toml.change_id.inner;
 
         let Build {
-            mut description,
+            description,
             build,
             host,
             target,
@@ -749,7 +749,7 @@ impl Config {
             compiletest_diff_tool,
             compiletest_use_stage0_libtest,
             tidy_extra_checks,
-            mut ccache,
+            ccache,
             exclude,
         } = toml.build.unwrap_or_default();
 
@@ -942,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;
@@ -963,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);
 
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 0fae235bb93..71fab0e6ae6 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,
@@ -541,6 +531,14 @@ impl Config {
             lld_enabled = lld_enabled_toml;
             std_features = std_features_toml;
 
+            if optimize_toml.as_ref().is_some_and(|v| matches!(v, RustOptimize::Bool(false))) {
+                eprintln!(
+                    "WARNING: setting `optimize` to `false` is known to cause errors and \
+                    should be considered unsupported. Refer to `bootstrap.example.toml` \
+                    for more details."
+                );
+            }
+
             optimize = optimize_toml;
             self.rust_new_symbol_mangling = new_symbol_mangling;
             set(&mut self.rust_optimize_tests, optimize_tests);
@@ -552,14 +550,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
diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs
index 44be51815c7..63aab4d116a 100644
--- a/src/bootstrap/src/lib.rs
+++ b/src/bootstrap/src/lib.rs
@@ -253,12 +253,24 @@ pub enum Mode {
     /// These tools are intended to be only executed on the host system that
     /// invokes bootstrap, and they thus cannot be cross-compiled.
     ///
-    /// They are always built using the stage0 compiler, and typically they
+    /// They are always built using the stage0 compiler, and they
     /// can be compiled with stable Rust.
     ///
     /// These tools also essentially do not participate in staging.
     ToolBootstrap,
 
+    /// Build a cross-compilable helper tool. These tools do not depend on unstable features or
+    /// compiler internals, but they might be cross-compilable (so we cannot build them using the
+    /// stage0 compiler, unlike `ToolBootstrap`).
+    ///
+    /// Some of these tools are also shipped in our `dist` archives.
+    /// While we could compile them using the stage0 compiler when not cross-compiling, we instead
+    /// use the in-tree compiler (and std) to build them, so that we can ship e.g. std security
+    /// fixes and avoid depending fully on stage0 for the artifacts that we ship.
+    ///
+    /// This mode is used e.g. for linkers and linker tools invoked by rustc on its host target.
+    ToolTarget,
+
     /// Build a tool which uses the locally built std, placing output in the
     /// "stageN-tools" directory. Its usage is quite rare, mainly used by
     /// compiletest which needs libtest.
@@ -273,11 +285,21 @@ pub enum Mode {
 
 impl Mode {
     pub fn is_tool(&self) -> bool {
-        matches!(self, Mode::ToolBootstrap | Mode::ToolRustc | Mode::ToolStd)
+        match self {
+            Mode::ToolBootstrap | Mode::ToolRustc | Mode::ToolStd | Mode::ToolTarget => true,
+            Mode::Std | Mode::Codegen | Mode::Rustc => false,
+        }
     }
 
     pub fn must_support_dlopen(&self) -> bool {
-        matches!(self, Mode::Std | Mode::Codegen)
+        match self {
+            Mode::Std | Mode::Codegen => true,
+            Mode::ToolBootstrap
+            | Mode::ToolRustc
+            | Mode::ToolStd
+            | Mode::ToolTarget
+            | Mode::Rustc => false,
+        }
     }
 }
 
@@ -802,17 +824,39 @@ impl Build {
     /// stage when running with a particular host compiler.
     ///
     /// The mode indicates what the root directory is for.
-    fn stage_out(&self, compiler: Compiler, mode: Mode) -> PathBuf {
-        let suffix = match mode {
-            Mode::Std => "-std",
-            Mode::Rustc => "-rustc",
-            Mode::Codegen => "-codegen",
-            Mode::ToolBootstrap => {
-                return self.out.join(compiler.host).join("bootstrap-tools");
+    fn stage_out(&self, build_compiler: Compiler, mode: Mode) -> PathBuf {
+        use std::fmt::Write;
+
+        fn bootstrap_tool() -> (Option<u32>, &'static str) {
+            (None, "bootstrap-tools")
+        }
+        fn staged_tool(build_compiler: Compiler) -> (Option<u32>, &'static str) {
+            (Some(build_compiler.stage), "tools")
+        }
+
+        let (stage, suffix) = match mode {
+            Mode::Std => (Some(build_compiler.stage), "std"),
+            Mode::Rustc => (Some(build_compiler.stage), "rustc"),
+            Mode::Codegen => (Some(build_compiler.stage), "codegen"),
+            Mode::ToolBootstrap => bootstrap_tool(),
+            Mode::ToolStd | Mode::ToolRustc => (Some(build_compiler.stage), "tools"),
+            Mode::ToolTarget => {
+                // If we're not cross-compiling (the common case), share the target directory with
+                // bootstrap tools to reuse the build cache.
+                if build_compiler.stage == 0 {
+                    bootstrap_tool()
+                } else {
+                    staged_tool(build_compiler)
+                }
             }
-            Mode::ToolStd | Mode::ToolRustc => "-tools",
         };
-        self.out.join(compiler.host).join(format!("stage{}{}", compiler.stage, suffix))
+        let path = self.out.join(build_compiler.host);
+        let mut dir_name = String::new();
+        if let Some(stage) = stage {
+            write!(dir_name, "stage{stage}-").unwrap();
+        }
+        dir_name.push_str(suffix);
+        path.join(dir_name)
     }
 
     /// Returns the root output directory for all Cargo output in a given stage,
diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs
index d888a7863bc..f802640a42d 100644
--- a/src/bootstrap/src/utils/change_tracker.rs
+++ b/src/bootstrap/src/utils/change_tracker.rs
@@ -481,4 +481,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[
         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/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/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/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-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/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 da7d084d48d..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 \
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 445fc0dd018..0a6ebe44b3d 100644
--- a/src/ci/github-actions/jobs.yml
+++ b/src/ci/github-actions/jobs.yml
@@ -34,6 +34,8 @@ runners:
     os: windows-2022
     <<: *base-job
 
+  # NOTE: windows-2025 has less disk space available than windows-2022,
+  # because the D drive is missing.
   - &job-windows-25
     os: windows-2025
     <<: *base-job
@@ -170,9 +172,9 @@ try:
 optional:
   # This job is used just to test optional jobs.
   # It will be replaced by tier 2 and tier 3 jobs in the future.
-  - name: optional-mingw-check-1
+  - name: optional-pr-check-1
     env:
-      IMAGE: mingw-check-1
+      IMAGE: pr-check-1
     <<: *job-linux-4c
 
 # Main CI jobs that have to be green to merge a commit into master
@@ -315,16 +317,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
@@ -544,13 +544,13 @@ auto:
     env:
       RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-sanitizers --enable-profiler
       SCRIPT: make ci-msvc-py
-    <<: *job-windows-25
+    <<: *job-windows
 
   - name: x86_64-msvc-2
     env:
       RUST_CONFIGURE_ARGS: --build=x86_64-pc-windows-msvc --enable-sanitizers --enable-profiler
       SCRIPT: make ci-msvc-ps1
-    <<: *job-windows-25
+    <<: *job-windows
 
   # i686-msvc is split into two jobs to run tests in parallel.
   - name: i686-msvc-1
diff --git a/src/ci/scripts/install-mingw.sh b/src/ci/scripts/install-mingw.sh
index ad852071f29..ed87628659b 100755
--- a/src/ci/scripts/install-mingw.sh
+++ b/src/ci/scripts/install-mingw.sh
@@ -43,4 +43,9 @@ if isWindows && isKnownToBeMingwBuild; then
     curl -o mingw.7z "${MIRRORS_BASE}/${mingw_archive}"
     7z x -y mingw.7z > /dev/null
     ciCommandAddPath "$(cygpath -m "$(pwd)/${mingw_dir}/bin")"
+
+    # Initialize mingw for the user.
+    # This should be done by github but isn't for some reason.
+    # (see https://github.com/actions/runner-images/issues/12600)
+    /c/msys64/usr/bin/bash -lc ' '
 fi
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/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md
index 63aa08c389c..5c3ae359ba0 100644
--- a/src/doc/rustc-dev-guide/src/tests/directives.md
+++ b/src/doc/rustc-dev-guide/src/tests/directives.md
@@ -75,8 +75,10 @@ expectations](ui.md#controlling-passfail-expectations).
 | `check-fail`                | Building (no codegen) should fail           | `ui`, `crashes`                           | N/A             |
 | `build-pass`                | Building should pass                        | `ui`, `crashes`, `codegen`, `incremental` | N/A             |
 | `build-fail`                | Building should fail                        | `ui`, `crashes`                           | N/A             |
-| `run-pass`                  | Running the test binary should pass         | `ui`, `crashes`, `incremental`            | N/A             |
-| `run-fail`                  | Running the test binary should fail         | `ui`, `crashes`                           | N/A             |
+| `run-pass`                  | Program must exit with code `0`             | `ui`, `crashes`, `incremental`            | N/A             |
+| `run-fail`                  | Program must exit with code `1..=127`       | `ui`, `crashes`                           | N/A             |
+| `run-crash`                 | Program must crash                          | `ui`                                      | N/A             |
+| `run-fail-or-crash`         | Program must `run-fail` or `run-crash`      | `ui`                                      | N/A             |
 | `ignore-pass`               | Ignore `--pass` flag                        | `ui`, `crashes`, `codegen`, `incremental` | N/A             |
 | `dont-check-failure-status` | Don't check exact failure status (i.e. `1`) | `ui`, `incremental`                       | N/A             |
 | `failure-status`            | Check                                       | `ui`, `crashes`                           | Any `u16`       |
@@ -203,6 +205,8 @@ settings:
   on `wasm32-unknown-unknown` target because the target does not support the
   `proc-macro` crate type.
 - `needs-target-std` — ignores if target platform does not have std support.
+- `ignore-backends` — ignores the listed backends, separated by whitespace characters.
+- `needs-backends` — only runs the test if current codegen backend is listed.
 
 The following directives will check LLVM support:
 
diff --git a/src/doc/rustc-dev-guide/src/tests/ui.md b/src/doc/rustc-dev-guide/src/tests/ui.md
index 4fce5838b6e..9bfc60e08a6 100644
--- a/src/doc/rustc-dev-guide/src/tests/ui.md
+++ b/src/doc/rustc-dev-guide/src/tests/ui.md
@@ -448,7 +448,7 @@ even run the resulting program. Just add one of the following
   - `//@ build-pass` — compilation and linking should succeed but do
     not run the resulting binary.
   - `//@ run-pass` — compilation should succeed and running the resulting
-    binary should also succeed.
+    binary should make it exit with code 0 which indicates success.
 - Fail directives:
   - `//@ check-fail` — compilation should fail (the codegen phase is skipped).
     This is the default for UI tests.
@@ -457,10 +457,20 @@ even run the resulting program. Just add one of the following
     - First time is to ensure that the compile succeeds without the codegen phase
     - Second time is to ensure that the full compile fails
   - `//@ run-fail` — compilation should succeed, but running the resulting
-    binary should fail.
-
-For `run-pass` and `run-fail` tests, by default the output of the program itself
-is not checked.
+    binary should make it exit with a code in the range `1..=127` which
+    indicates regular failure. On targets without unwind support, crashes
+    are also accepted.
+  - `//@ run-crash` — compilation should succeed, but running the resulting
+    binary should fail with a crash. Crashing is defined as "not exiting with
+    a code in the range `0..=127`". Example on Linux: Termination by `SIGABRT`
+    or `SIGSEGV`. Example on Windows: Exiting with the code for
+    `STATUS_ILLEGAL_INSTRUCTION` (`0xC000001D`).
+  - `//@ run-fail-or-crash` — compilation should succeed, but running the
+    resulting binary should either `run-fail` or `run-crash`. Useful if a test
+    crashes on some targets but just fails on others.
+
+For `run-pass`. `run-fail`, `run-crash` and `run-fail-or-crash` tests, by
+default the output of the program itself is not checked.
 
 If you want to check the output of running the program, include the
 `check-run-results` directive. This will check for a `.run.stderr` and
diff --git a/src/doc/rustc/src/exploit-mitigations.md b/src/doc/rustc/src/exploit-mitigations.md
index f8bafe03214..c80d7d8743c 100644
--- a/src/doc/rustc/src/exploit-mitigations.md
+++ b/src/doc/rustc/src/exploit-mitigations.md
@@ -54,17 +54,17 @@ Summary of exploit mitigations supported by the Rust compiler when building
 programs for the Linux operating system on the AMD64 architecture and
 equivalent.
 
-| Exploit mitigation | Supported and enabled by default | Since |
-| - | - | - |
-| Position-independent executable | Yes | 0.12.0 (2014-10-09) |
-| Integer overflow checks | Yes (enabled when debug assertions are enabled, and disabled when debug assertions are disabled) | 1.1.0 (2015-06-25) |
-| Non-executable memory regions | Yes | 1.8.0 (2016-04-14) |
-| Stack clashing protection | Yes | 1.20.0 (2017-08-31) |
-| Read-only relocations and immediate binding | Yes | 1.21.0 (2017-10-12) |
-| Heap corruption protection | Yes | 1.32.0 (2019-01-17) (via operating system default or specified allocator) |
-| Stack smashing protection | Yes | Nightly |
-| Forward-edge control flow protection | Yes | Nightly |
-| Backward-edge control flow protection (e.g., shadow and safe stack) | Yes | Nightly |
+| Exploit mitigation | Supported | Enabled by default | Since |
+| - | - | - | - |
+| Position-independent executable | Yes | Yes | 0.12.0 (2014-10-09) |
+| Integer overflow checks | Yes | (enabled when debug assertions are enabled, and disabled when debug assertions are disabled) | 1.1.0 (2015-06-25) |
+| Non-executable memory regions | Yes | Yes | 1.8.0 (2016-04-14) |
+| Stack clashing protection | Yes | Yes | 1.20.0 (2017-08-31) |
+| Read-only relocations and immediate binding | Yes | Yes | 1.21.0 (2017-10-12) |
+| Heap corruption protection | Yes | Yes | 1.32.0 (2019-01-17) (via operating system default or specified allocator) |
+| Stack smashing protection | Yes | No, `-Z stack-protector` | Nightly |
+| Forward-edge control flow protection | Yes | No, `-Z sanitizer=cfi` | Nightly |
+| Backward-edge control flow protection (e.g., shadow and safe stack) | Yes | No, `-Z sanitizer=shadow-call-stack,safestack` | Nightly |
 
 [^all-targets]: See <https://github.com/rust-lang/rust/tree/master/compiler/rustc_target/src/spec>
     for a list of targets and their default options.
diff --git a/src/doc/rustc/src/platform-support/xtensa.md b/src/doc/rustc/src/platform-support/xtensa.md
index 994b3adb92e..8592ce7eda9 100644
--- a/src/doc/rustc/src/platform-support/xtensa.md
+++ b/src/doc/rustc/src/platform-support/xtensa.md
@@ -24,4 +24,4 @@ Xtensa targets that support `std` are documented in the [ESP-IDF platform suppor
 
 ## Building the targets
 
-The targets can be built by installing the [Xtensa enabled Rust channel](https://github.com/esp-rs/rust/). See instructions in the [RISC-V and Xtensa Targets section of The Rust on ESP Book](https://docs.esp-rs.org/book/installation/riscv-and-xtensa.html).
+The targets can be built by installing the [Xtensa enabled Rust channel](https://github.com/esp-rs/rust/). See instructions in the [RISC-V and Xtensa Targets section of The Rust on ESP Book](https://docs.espressif.com/projects/rust/book/installation/index.html).
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index e7a1f4d8397..1265a39d27b 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -390,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,
     }
 }
@@ -2864,7 +2865,7 @@ 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), cx))
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 20babc6168b..5ac5da24299 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 {
@@ -1677,7 +1677,7 @@ impl Type {
         }
     }
 
-    pub(crate) fn generics<'a>(&'a self) -> Option<impl Iterator<Item = &'a Type>> {
+    pub(crate) fn generics(&self) -> Option<impl Iterator<Item = &Type>> {
         match self {
             Type::Path { path, .. } => path.generics(),
             _ => None,
@@ -2227,7 +2227,7 @@ impl Path {
         self.segments.last().map(|seg| &seg.args)
     }
 
-    pub(crate) fn generics<'a>(&'a self) -> Option<impl Iterator<Item = &'a Type>> {
+    pub(crate) fn generics(&self) -> Option<impl Iterator<Item = &Type>> {
         self.segments.last().and_then(|seg| {
             if let GenericArgs::AngleBracketed { ref args, .. } = seg.args {
                 Some(args.iter().filter_map(|arg| match arg {
diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs
index bf3f7607274..813fdee57e1 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(
@@ -348,13 +343,11 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol {
 pub(crate) fn print_const(cx: &DocContext<'_>, n: ty::Const<'_>) -> String {
     match n.kind() {
         ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, args: _ }) => {
-            let s = if let Some(def) = def.as_local() {
+            if let Some(def) = def.as_local() {
                 rendered_const(cx.tcx, cx.tcx.hir_body_owned_by(def), def)
             } else {
                 inline::print_inlined_const(cx.tcx, def)
-            };
-
-            s
+            }
         }
         // array lengths are obviously usize
         ty::ConstKind::Value(cv) if *cv.ty.kind() == ty::Uint(ty::UintTy::Usize) => {
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index 9b4d2533954..38ba6b4503d 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -632,7 +632,7 @@ fn run_test(
         // the user to exploit nightly-only features on stable
         runner_compiler.env("RUSTC_BOOTSTRAP", "1");
         runner_compiler.args(compiler_args);
-        runner_compiler.args(&["--crate-type=bin", "-o"]).arg(&output_file);
+        runner_compiler.args(["--crate-type=bin", "-o"]).arg(&output_file);
         let mut extern_path = std::ffi::OsString::from(format!(
             "--extern=doctest_bundle_{edition}=",
             edition = doctest.edition
@@ -657,7 +657,7 @@ fn run_test(
         extern_path.push(&output_bundle_file);
         runner_compiler.arg(extern_path);
         runner_compiler.arg(&runner_input_file);
-        if std::fs::write(&runner_input_file, &merged_test_code).is_err() {
+        if std::fs::write(&runner_input_file, merged_test_code).is_err() {
             // If we cannot write this file for any reason, we leave. All combined tests will be
             // tested as standalone tests.
             return Err(TestFailure::CompileError);
diff --git a/src/librustdoc/doctest/rust.rs b/src/librustdoc/doctest/rust.rs
index 96975105ac5..f5ec828187a 100644
--- a/src/librustdoc/doctest/rust.rs
+++ b/src/librustdoc/doctest/rust.rs
@@ -140,7 +140,7 @@ impl HirCollector<'_> {
                 .iter()
                 .filter(|a| a.has_name(sym::attr))
                 .flat_map(|a| a.meta_item_list().unwrap_or_default())
-                .map(|i| pprust::meta_list_item_to_string(i))
+                .map(pprust::meta_list_item_to_string)
             {
                 // Add the additional attributes to the global_crate_attrs vector
                 self.collector.global_crate_attrs.push(attr);
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/formats/renderer.rs b/src/librustdoc/formats/renderer.rs
index 79ff1fa38c3..aa4be4db997 100644
--- a/src/librustdoc/formats/renderer.rs
+++ b/src/librustdoc/formats/renderer.rs
@@ -81,7 +81,7 @@ fn run_format_inner<'tcx, T: FormatRenderer<'tcx>>(
         let _timer =
             prof.generic_activity_with_arg("render_mod_item", item.name.unwrap().to_string());
 
-        cx.mod_item_in(&item)?;
+        cx.mod_item_in(item)?;
         let (clean::StrippedItem(box clean::ModuleItem(ref module))
         | clean::ModuleItem(ref module)) = item.inner.kind
         else {
@@ -99,7 +99,7 @@ fn run_format_inner<'tcx, T: FormatRenderer<'tcx>>(
     } else if let Some(item_name) = item.name
         && !item.is_extern_crate()
     {
-        prof.generic_activity_with_arg("render_item", item_name.as_str()).run(|| cx.item(&item))?;
+        prof.generic_activity_with_arg("render_item", item_name.as_str()).run(|| cx.item(item))?;
     }
     Ok(())
 }
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index bcb3e57c844..be8a2d511e9 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};
@@ -113,9 +114,9 @@ impl clean::Generics {
             let real_params =
                 fmt::from_fn(|f| real_params.clone().map(|g| g.print(cx)).joined(", ", f));
             if f.alternate() {
-                write!(f, "<{:#}>", real_params)
+                write!(f, "<{real_params:#}>")
             } else {
-                write!(f, "&lt;{}&gt;", real_params)
+                write!(f, "&lt;{real_params}&gt;")
             }
         })
     }
@@ -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(
@@ -605,7 +594,7 @@ pub(crate) fn href_with_root_path(
             }
         }
     };
-    let url_parts = make_href(root_path, shortty, url_parts, &fqp, is_remote);
+    let url_parts = make_href(root_path, shortty, url_parts, fqp, is_remote);
     Ok((url_parts, shortty, fqp.clone()))
 }
 
@@ -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)
@@ -1126,7 +1115,7 @@ impl clean::Impl {
                 {
                     let last = ty.last();
                     if f.alternate() {
-                        write!(f, "{}<", last)?;
+                        write!(f, "{last}<")?;
                         self.print_type(inner_type, f, use_absolute, cx)?;
                         write!(f, ">")?;
                     } else {
@@ -1230,7 +1219,7 @@ pub(crate) fn print_params(params: &[clean::Parameter], cx: &Context<'_>) -> imp
             .map(|param| {
                 fmt::from_fn(|f| {
                     if let Some(name) = param.name {
-                        write!(f, "{}: ", name)?;
+                        write!(f, "{name}: ")?;
                     }
                     param.type_.print(cx).fmt(f)
                 })
@@ -1352,7 +1341,7 @@ impl clean::FnDecl {
                     write!(f, "const ")?;
                 }
                 if let Some(name) = param.name {
-                    write!(f, "{}: ", name)?;
+                    write!(f, "{name}: ")?;
                 }
                 param.type_.print(cx).fmt(f)?;
             }
diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs
index b2feee36c93..272180fb990 100644
--- a/src/librustdoc/html/highlight.rs
+++ b/src/librustdoc/html/highlight.rs
@@ -547,7 +547,7 @@ impl<'a> Iterator for TokenIter<'a> {
 fn get_real_ident_class(text: &str, allow_path_keywords: bool) -> Option<Class> {
     let ignore: &[&str] =
         if allow_path_keywords { &["self", "Self", "super", "crate"] } else { &["self", "Self"] };
-    if ignore.iter().any(|k| *k == text) {
+    if ignore.contains(&text) {
         return None;
     }
     Some(match text {
@@ -1159,7 +1159,7 @@ fn string_without_closing_tag<T: Display>(
         return Some("</a>");
     }
     if !open_tag {
-        write!(out, "{}", text_s).unwrap();
+        out.write_str(&text_s).unwrap();
         return None;
     }
     let klass_s = klass.as_html();
diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs
index 50320cb231d..1f92c521d46 100644
--- a/src/librustdoc/html/layout.rs
+++ b/src/librustdoc/html/layout.rs
@@ -132,6 +132,5 @@ pub(crate) fn redirect(url: &str) -> String {
     <script>location.replace("{url}" + location.search + location.hash);</script>
 </body>
 </html>"##,
-        url = url,
     )
 }
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index e41435de29c..4addf2c3c96 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -251,7 +251,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
                     if !parse_result.rust {
                         let added_classes = parse_result.added_classes;
                         let lang_string = if let Some(lang) = parse_result.unknown.first() {
-                            format!("language-{}", lang)
+                            format!("language-{lang}")
                         } else {
                             String::new()
                         };
@@ -999,7 +999,7 @@ impl<'a, 'tcx> TagIterator<'a, 'tcx> {
 
         if let Some((_, c)) = self.inner.next() {
             if c != '=' {
-                self.emit_error(format!("expected `=`, found `{}`", c));
+                self.emit_error(format!("expected `=`, found `{c}`"));
                 return None;
             }
         } else {
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index 3b4dae841ee..5ceb1fc988d 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;
@@ -193,14 +193,12 @@ impl<'tcx> Context<'tcx> {
         if it.is_stripped()
             && let Some(def_id) = it.def_id()
             && def_id.is_local()
+            && (self.info.is_inside_inlined_module
+                || self.shared.cache.inlined_items.contains(&def_id))
         {
-            if self.info.is_inside_inlined_module
-                || self.shared.cache.inlined_items.contains(&def_id)
-            {
-                // For now we're forced to generate a redirect page for stripped items until
-                // `record_extern_fqn` correctly points to external items.
-                render_redirect_pages = true;
-            }
+            // For now we're forced to generate a redirect page for stripped items until
+            // `record_extern_fqn` correctly points to external items.
+            render_redirect_pages = true;
         }
         let mut title = String::new();
         if !is_module {
@@ -211,7 +209,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_();
@@ -254,40 +252,36 @@ impl<'tcx> Context<'tcx> {
                 &self.shared.style_files,
             )
         } else {
-            if let Some(&(ref names, ty)) = self.cache().paths.get(&it.item_id.expect_def_id()) {
-                if self.current.len() + 1 != names.len()
-                    || self.current.iter().zip(names.iter()).any(|(a, b)| a != b)
-                {
-                    // We checked that the redirection isn't pointing to the current file,
-                    // preventing an infinite redirection loop in the generated
-                    // documentation.
-
-                    let path = fmt::from_fn(|f| {
-                        for name in &names[..names.len() - 1] {
-                            write!(f, "{name}/")?;
-                        }
-                        write!(f, "{}", print_item_path(ty, names.last().unwrap().as_str()))
-                    });
-                    match self.shared.redirections {
-                        Some(ref redirections) => {
-                            let mut current_path = String::new();
-                            for name in &self.current {
-                                current_path.push_str(name.as_str());
-                                current_path.push('/');
-                            }
-                            let _ = write!(
-                                current_path,
-                                "{}",
-                                print_item_path(ty, names.last().unwrap().as_str())
-                            );
-                            redirections.borrow_mut().insert(current_path, path.to_string());
-                        }
-                        None => {
-                            return layout::redirect(&format!(
-                                "{root}{path}",
-                                root = self.root_path()
-                            ));
+            if let Some(&(ref names, ty)) = self.cache().paths.get(&it.item_id.expect_def_id())
+                && (self.current.len() + 1 != names.len()
+                    || self.current.iter().zip(names.iter()).any(|(a, b)| a != b))
+            {
+                // We checked that the redirection isn't pointing to the current file,
+                // preventing an infinite redirection loop in the generated
+                // documentation.
+
+                let path = fmt::from_fn(|f| {
+                    for name in &names[..names.len() - 1] {
+                        write!(f, "{name}/")?;
+                    }
+                    write!(f, "{}", print_item_path(ty, names.last().unwrap().as_str()))
+                });
+                match self.shared.redirections {
+                    Some(ref redirections) => {
+                        let mut current_path = String::new();
+                        for name in &self.current {
+                            current_path.push_str(name.as_str());
+                            current_path.push('/');
                         }
+                        let _ = write!(
+                            current_path,
+                            "{}",
+                            print_item_path(ty, names.last().unwrap().as_str())
+                        );
+                        redirections.borrow_mut().insert(current_path, path.to_string());
+                    }
+                    None => {
+                        return layout::redirect(&format!("{root}{path}", root = self.root_path()));
                     }
                 }
             }
@@ -762,11 +756,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
         // Flush pending errors.
         self.shared.fs.close();
         let nb_errors = self.shared.errors.iter().map(|err| self.tcx().dcx().err(err)).count();
-        if nb_errors > 0 {
-            Err(Error::new(io::Error::new(io::ErrorKind::Other, "I/O error"), ""))
-        } else {
-            Ok(())
-        }
+        if nb_errors > 0 { Err(Error::new(io::Error::other("I/O error"), "")) } else { Ok(()) }
     }
 
     fn mod_item_in(&mut self, item: &clean::Item) -> Result<(), Error> {
@@ -842,7 +832,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
             self.info.render_redirect_pages = item.is_stripped();
         }
 
-        let buf = self.render_item(&item, false);
+        let buf = self.render_item(item, false);
         // buf will be empty if the item is stripped and there is no redirect for it
         if !buf.is_empty() {
             let name = item.name.as_ref().unwrap();
@@ -853,7 +843,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
             self.shared.fs.write(joint_dst, buf)?;
 
             if !self.info.render_redirect_pages {
-                self.shared.all.borrow_mut().append(full_path(self, &item), &item_type);
+                self.shared.all.borrow_mut().append(full_path(self, item), &item_type);
             }
             // If the item is a macro, redirect from the old macro URL (with !)
             // to the new one (without).
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 06de4944d97..872dbbcd19e 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,
@@ -1109,7 +1110,7 @@ fn since_to_string(since: &StableSince) -> Option<String> {
     match since {
         StableSince::Version(since) => Some(since.to_string()),
         StableSince::Current => Some(RustcVersion::CURRENT.to_string()),
-        StableSince::Err => None,
+        StableSince::Err(_) => None,
     }
 }
 
@@ -1482,10 +1483,10 @@ fn render_deref_methods(
             }
         }
         render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs);
-    } else if let Some(prim) = target.primitive_type() {
-        if let Some(&did) = cache.primitive_locations.get(&prim) {
-            render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs);
-        }
+    } else if let Some(prim) = target.primitive_type()
+        && let Some(&did) = cache.primitive_locations.get(&prim)
+    {
+        render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs);
     }
 }
 
@@ -2057,21 +2058,20 @@ fn render_impl(
         // default items which weren't overridden in the implementation block.
         // We don't emit documentation for default items if they appear in the
         // Implementations on Foreign Types or Implementors sections.
-        if rendering_params.show_default_items {
-            if let Some(t) = trait_
-                && !impl_.is_negative_trait_impl()
-            {
-                render_default_items(
-                    &mut default_impl_items,
-                    &mut impl_items,
-                    cx,
-                    t,
-                    impl_,
-                    &i.impl_item,
-                    render_mode,
-                    rendering_params,
-                )?;
-            }
+        if rendering_params.show_default_items
+            && let Some(t) = trait_
+            && !impl_.is_negative_trait_impl()
+        {
+            render_default_items(
+                &mut default_impl_items,
+                &mut impl_items,
+                cx,
+                t,
+                impl_,
+                &i.impl_item,
+                render_mode,
+                rendering_params,
+            )?;
         }
         if render_mode == RenderMode::Normal {
             let toggled = !(impl_items.is_empty() && default_impl_items.is_empty());
@@ -2555,7 +2555,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));
         }
     };
 
@@ -2569,7 +2569,7 @@ fn collect_paths_for_type(first_ty: &clean::Type, cache: &Cache) -> Vec<String>
         match ty {
             clean::Type::Path { path } => process_path(path.def_id()),
             clean::Type::Tuple(tys) => {
-                work.extend(tys.into_iter());
+                work.extend(tys.iter());
             }
             clean::Type::Slice(ty) => {
                 work.push_back(ty);
diff --git a/src/librustdoc/html/render/ordered_json.rs b/src/librustdoc/html/render/ordered_json.rs
index d1dddfebc83..be51dad1c2b 100644
--- a/src/librustdoc/html/render/ordered_json.rs
+++ b/src/librustdoc/html/render/ordered_json.rs
@@ -25,7 +25,7 @@ impl OrderedJson {
             .into_iter()
             .sorted_unstable_by(|a, b| a.borrow().cmp(b.borrow()))
             .format_with(",", |item, f| f(item.borrow()));
-        Self(format!("[{}]", items))
+        Self(format!("[{items}]"))
     }
 
     pub(crate) fn array_unsorted<T: Borrow<Self>, I: IntoIterator<Item = T>>(items: I) -> Self {
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 667d39e9bc2..02ee34aaac6 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>",
@@ -1450,7 +1451,7 @@ item_template!(
 
 impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> {
     fn render_union(&self) -> impl Display {
-        render_union(self.it, Some(&self.generics), &self.fields, self.cx)
+        render_union(self.it, Some(self.generics), self.fields, self.cx)
     }
 
     fn document_field(&self, field: &'a clean::Item) -> impl Display {
@@ -1981,16 +1982,14 @@ fn item_constant(
                 w.write_str(";")?;
             }
 
-            if !is_literal {
-                if let Some(value) = &value {
-                    let value_lowercase = value.to_lowercase();
-                    let expr_lowercase = expr.to_lowercase();
+            if !is_literal && let Some(value) = &value {
+                let value_lowercase = value.to_lowercase();
+                let expr_lowercase = expr.to_lowercase();
 
-                    if value_lowercase != expr_lowercase
-                        && value_lowercase.trim_end_matches("i32") != expr_lowercase
-                    {
-                        write!(w, " // {value}", value = Escape(value))?;
-                    }
+                if value_lowercase != expr_lowercase
+                    && value_lowercase.trim_end_matches("i32") != expr_lowercase
+                {
+                    write!(w, " // {value}", value = Escape(value))?;
                 }
             }
             Ok::<(), fmt::Error>(())
@@ -2070,41 +2069,39 @@ fn item_fields(
                 _ => None,
             })
             .peekable();
-        if let None | Some(CtorKind::Fn) = ctor_kind {
-            if fields.peek().is_some() {
-                let title = format!(
-                    "{}{}",
-                    if ctor_kind.is_none() { "Fields" } else { "Tuple Fields" },
-                    document_non_exhaustive_header(it),
-                );
+        if let None | Some(CtorKind::Fn) = ctor_kind
+            && fields.peek().is_some()
+        {
+            let title = format!(
+                "{}{}",
+                if ctor_kind.is_none() { "Fields" } else { "Tuple Fields" },
+                document_non_exhaustive_header(it),
+            );
+            write!(
+                w,
+                "{}",
+                write_section_heading(
+                    &title,
+                    "fields",
+                    Some("fields"),
+                    document_non_exhaustive(it)
+                )
+            )?;
+            for (index, (field, ty)) in fields.enumerate() {
+                let field_name =
+                    field.name.map_or_else(|| index.to_string(), |sym| sym.as_str().to_string());
+                let id = cx.derive_id(format!("{typ}.{field_name}", typ = ItemType::StructField));
                 write!(
                     w,
-                    "{}",
-                    write_section_heading(
-                        &title,
-                        "fields",
-                        Some("fields"),
-                        document_non_exhaustive(it)
-                    )
+                    "<span id=\"{id}\" class=\"{item_type} section-header\">\
+                        <a href=\"#{id}\" class=\"anchor field\">§</a>\
+                        <code>{field_name}: {ty}</code>\
+                    </span>\
+                    {doc}",
+                    item_type = ItemType::StructField,
+                    ty = ty.print(cx),
+                    doc = document(cx, field, Some(it), HeadingOffset::H3),
                 )?;
-                for (index, (field, ty)) in fields.enumerate() {
-                    let field_name = field
-                        .name
-                        .map_or_else(|| index.to_string(), |sym| sym.as_str().to_string());
-                    let id =
-                        cx.derive_id(format!("{typ}.{field_name}", typ = ItemType::StructField));
-                    write!(
-                        w,
-                        "<span id=\"{id}\" class=\"{item_type} section-header\">\
-                            <a href=\"#{id}\" class=\"anchor field\">§</a>\
-                            <code>{field_name}: {ty}</code>\
-                        </span>\
-                        {doc}",
-                        item_type = ItemType::StructField,
-                        ty = ty.print(cx),
-                        doc = document(cx, field, Some(it), HeadingOffset::H3),
-                    )?;
-                }
             }
         }
         Ok(())
@@ -2256,7 +2253,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..3c9be29ccc3 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,
@@ -116,7 +116,7 @@ pub(crate) fn build_index(
     // Set up alias indexes.
     for (i, item) in cache.search_index.iter().enumerate() {
         for alias in &item.aliases[..] {
-            aliases.entry(alias.as_str().to_lowercase()).or_default().push(i);
+            aliases.entry(alias.to_string()).or_default().push(i);
         }
     }
 
@@ -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/sidebar.rs b/src/librustdoc/html/render/sidebar.rs
index 91540e06e33..b9f5ada417c 100644
--- a/src/librustdoc/html/render/sidebar.rs
+++ b/src/librustdoc/html/render/sidebar.rs
@@ -541,7 +541,7 @@ fn sidebar_deref_methods<'a>(
                 .iter()
                 .filter(|i| {
                     i.inner_impl().trait_.is_none()
-                        && real_target.is_doc_subtype_of(&i.inner_impl().for_, &c)
+                        && real_target.is_doc_subtype_of(&i.inner_impl().for_, c)
                 })
                 .flat_map(|i| get_methods(i.inner_impl(), true, used_links, deref_mut, cx.tcx()))
                 .collect::<Vec<_>>();
diff --git a/src/librustdoc/html/render/sorted_template.rs b/src/librustdoc/html/render/sorted_template.rs
index a7b954ab70b..659c5e6093b 100644
--- a/src/librustdoc/html/render/sorted_template.rs
+++ b/src/librustdoc/html/render/sorted_template.rs
@@ -63,7 +63,8 @@ impl<F: FileFormat> fmt::Display for SortedTemplate<F> {
         for (p, fragment) in self.fragments.iter().with_position() {
             let mut f = DeltaWriter { inner: &mut f, delta: 0 };
             let sep = if matches!(p, Position::First | Position::Only) { "" } else { F::SEPARATOR };
-            write!(f, "{}{}", sep, fragment)?;
+            f.write_str(sep)?;
+            f.write_str(fragment)?;
             fragment_lengths.push(f.delta);
         }
         let offset = Offset { start: self.before.len(), fragment_lengths };
diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs
index 0078671fcc5..1f691392b17 100644
--- a/src/librustdoc/html/render/write_shared.rs
+++ b/src/librustdoc/html/render/write_shared.rs
@@ -26,6 +26,7 @@ use std::{fmt, fs};
 
 use indexmap::IndexMap;
 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;
@@ -43,7 +44,6 @@ use crate::docfs::PathError;
 use crate::error::Error;
 use crate::formats::Impl;
 use crate::formats::item_type::ItemType;
-use crate::html::format::join_with_double_colon;
 use crate::html::layout;
 use crate::html::render::ordered_json::{EscapedJson, OrderedJson};
 use crate::html::render::search_index::{SerializedSearchIndex, build_index};
@@ -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 = join_with_double_colon(&type_alias_fqp);
+                        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/static/js/rustdoc.d.ts b/src/librustdoc/html/static/js/rustdoc.d.ts
index ca2512e5ab6..a9589764547 100644
--- a/src/librustdoc/html/static/js/rustdoc.d.ts
+++ b/src/librustdoc/html/static/js/rustdoc.d.ts
@@ -219,6 +219,8 @@ declare namespace rustdoc {
         crate: string,
         descShard: SearchDescShard,
         id: number,
+        // This is the name of the item. For doc aliases, if you want the name of the aliased
+        // item, take a look at `Row.original.name`.
         name: string,
         normalizedName: string,
         word: string,
@@ -227,6 +229,11 @@ declare namespace rustdoc {
         path: string,
         ty: number,
         type: FunctionSearchType | null,
+        descIndex: number,
+        bitIndex: number,
+        implDisambiguator: String | null,
+        is_alias?: boolean,
+        original?: Row,
     }
 
     /**
diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js
index 15cad31f555..2caf214ff73 100644
--- a/src/librustdoc/html/static/js/search.js
+++ b/src/librustdoc/html/static/js/search.js
@@ -830,7 +830,7 @@ function createQueryElement(query, parserState, name, generics, isInGenerics) {
  */
 function makePrimitiveElement(name, extra) {
     return Object.assign({
-        name: name,
+        name,
         id: null,
         fullPath: [name],
         pathWithoutLast: [],
@@ -1483,6 +1483,7 @@ class DocSearch {
          */
         this.assocTypeIdNameMap = new Map();
         this.ALIASES = new Map();
+        this.FOUND_ALIASES = new Set();
         this.rootPath = rootPath;
         this.searchState = searchState;
 
@@ -2030,6 +2031,8 @@ class DocSearch {
         // normalized names, type signature objects and fingerprints, and aliases.
         id = 0;
 
+        /** @type {Array<[string, { [key: string]: Array<number> },  number]>} */
+        const allAliases = [];
         for (const [crate, crateCorpus] of rawSearchIndex) {
             // a string representing the lengths of each description shard
             // a string representing the list of function types
@@ -2178,10 +2181,10 @@ class DocSearch {
                 paths[i] = { ty, name, path, exactPath, unboxFlag };
             }
 
-            // convert `item*` into an object form, and construct word indices.
+            // Convert `item*` into an object form, and construct word indices.
             //
-            // before any analysis is performed lets gather the search terms to
-            // search against apart from the rest of the data.  This is a quick
+            // Before any analysis is performed, let's gather the search terms to
+            // search against apart from the rest of the data. This is a quick
             // operation that is cached for the life of the page state so that
             // all other search operations have access to this cached data for
             // faster analysis operations
@@ -2269,29 +2272,58 @@ class DocSearch {
             }
 
             if (aliases) {
-                const currentCrateAliases = new Map();
-                this.ALIASES.set(crate, currentCrateAliases);
-                for (const alias_name in aliases) {
-                    if (!Object.prototype.hasOwnProperty.call(aliases, alias_name)) {
-                        continue;
-                    }
-
-                    /** @type{number[]} */
-                    let currentNameAliases;
-                    if (currentCrateAliases.has(alias_name)) {
-                        currentNameAliases = currentCrateAliases.get(alias_name);
-                    } else {
-                        currentNameAliases = [];
-                        currentCrateAliases.set(alias_name, currentNameAliases);
-                    }
-                    for (const local_alias of aliases[alias_name]) {
-                        currentNameAliases.push(local_alias + currentIndex);
-                    }
-                }
+                // We need to add the aliases in `searchIndex` after we finished filling it
+                // to not mess up indexes.
+                allAliases.push([crate, aliases, currentIndex]);
             }
             currentIndex += itemTypes.length;
             this.searchState.descShards.set(crate, descShardList);
         }
+
+        for (const [crate, aliases, index] of allAliases) {
+            for (const [alias_name, alias_refs] of Object.entries(aliases)) {
+                if (!this.ALIASES.has(crate)) {
+                    this.ALIASES.set(crate, new Map());
+                }
+                const word = alias_name.toLowerCase();
+                const crate_alias_map = this.ALIASES.get(crate);
+                if (!crate_alias_map.has(word)) {
+                    crate_alias_map.set(word, []);
+                }
+                const aliases_map = crate_alias_map.get(word);
+
+                const normalizedName = word.indexOf("_") === -1 ? word : word.replace(/_/g, "");
+                for (const alias of alias_refs) {
+                    const originalIndex = alias + index;
+                    const original = searchIndex[originalIndex];
+                    /** @type {rustdoc.Row} */
+                    const row = {
+                        crate,
+                        name: alias_name,
+                        normalizedName,
+                        is_alias: true,
+                        ty: original.ty,
+                        type: original.type,
+                        paramNames: [],
+                        word,
+                        id,
+                        parent: undefined,
+                        original,
+                        path: "",
+                        implDisambiguator: original.implDisambiguator,
+                        // Needed to load the description of the original item.
+                        // @ts-ignore
+                        descShard: original.descShard,
+                        descIndex: original.descIndex,
+                        bitIndex: original.bitIndex,
+                    };
+                    aliases_map.push(row);
+                    this.nameTrie.insert(normalizedName, id, this.tailTable);
+                    id += 1;
+                    searchIndex.push(row);
+                }
+            }
+        }
         // Drop the (rather large) hash table used for reusing function items
         this.TYPES_POOL = new Map();
         return searchIndex;
@@ -2536,6 +2568,8 @@ class DocSearch {
             parsedQuery.elems.reduce((acc, next) => acc + next.pathLast.length, 0) +
             parsedQuery.returned.reduce((acc, next) => acc + next.pathLast.length, 0);
         const maxEditDistance = Math.floor(queryLen / 3);
+        // We reinitialize the `FOUND_ALIASES` map.
+        this.FOUND_ALIASES.clear();
 
         /**
          * @type {Map<string, number>}
@@ -2695,6 +2729,10 @@ class DocSearch {
         const buildHrefAndPath = item => {
             let displayPath;
             let href;
+            if (item.is_alias) {
+                this.FOUND_ALIASES.add(item.word);
+                item = item.original;
+            }
             const type = itemTypes[item.ty];
             const name = item.name;
             let path = item.path;
@@ -3198,8 +3236,7 @@ class DocSearch {
                 result.item = this.searchIndex[result.id];
                 result.word = this.searchIndex[result.id].word;
                 if (isReturnTypeQuery) {
-                    // we are doing a return-type based search,
-                    // deprioritize "clone-like" results,
+                    // We are doing a return-type based search, deprioritize "clone-like" results,
                     // ie. functions that also take the queried type as an argument.
                     const resultItemType = result.item && result.item.type;
                     if (!resultItemType) {
@@ -4259,28 +4296,13 @@ class DocSearch {
             return false;
         }
 
-        // this does not yet have a type in `rustdoc.d.ts`.
-        // @ts-expect-error
-        function createAliasFromItem(item) {
-            return {
-                crate: item.crate,
-                name: item.name,
-                path: item.path,
-                descShard: item.descShard,
-                descIndex: item.descIndex,
-                exactPath: item.exactPath,
-                ty: item.ty,
-                parent: item.parent,
-                type: item.type,
-                is_alias: true,
-                bitIndex: item.bitIndex,
-                implDisambiguator: item.implDisambiguator,
-            };
-        }
-
         // @ts-expect-error
         const handleAliases = async(ret, query, filterCrates, currentCrate) => {
             const lowerQuery = query.toLowerCase();
+            if (this.FOUND_ALIASES.has(lowerQuery)) {
+                return;
+            }
+            this.FOUND_ALIASES.add(lowerQuery);
             // We separate aliases and crate aliases because we want to have current crate
             // aliases to be before the others in the displayed results.
             // @ts-expect-error
@@ -4292,7 +4314,7 @@ class DocSearch {
                     && this.ALIASES.get(filterCrates).has(lowerQuery)) {
                     const query_aliases = this.ALIASES.get(filterCrates).get(lowerQuery);
                     for (const alias of query_aliases) {
-                        aliases.push(createAliasFromItem(this.searchIndex[alias]));
+                        aliases.push(alias);
                     }
                 }
             } else {
@@ -4302,7 +4324,7 @@ class DocSearch {
                         const pushTo = crate === currentCrate ? crateAliases : aliases;
                         const query_aliases = crateAliasesIndex.get(lowerQuery);
                         for (const alias of query_aliases) {
-                            pushTo.push(createAliasFromItem(this.searchIndex[alias]));
+                            pushTo.push(alias);
                         }
                     }
                 }
@@ -4310,9 +4332,9 @@ class DocSearch {
 
             // @ts-expect-error
             const sortFunc = (aaa, bbb) => {
-                if (aaa.path < bbb.path) {
+                if (aaa.original.path < bbb.original.path) {
                     return 1;
-                } else if (aaa.path === bbb.path) {
+                } else if (aaa.original.path === bbb.original.path) {
                     return 0;
                 }
                 return -1;
@@ -4322,20 +4344,9 @@ class DocSearch {
             aliases.sort(sortFunc);
 
             // @ts-expect-error
-            const fetchDesc = alias => {
-                // @ts-expect-error
-                return this.searchIndexEmptyDesc.get(alias.crate).contains(alias.bitIndex) ?
-                    "" : this.searchState.loadDesc(alias);
-            };
-            const [crateDescs, descs] = await Promise.all([
-                // @ts-expect-error
-                Promise.all(crateAliases.map(fetchDesc)),
-                Promise.all(aliases.map(fetchDesc)),
-            ]);
-
-            // @ts-expect-error
             const pushFunc = alias => {
-                alias.alias = query;
+                // Cloning `alias` to prevent its fields to be updated.
+                alias = {...alias};
                 const res = buildHrefAndPath(alias);
                 alias.displayPath = pathSplitter(res[0]);
                 alias.fullPath = alias.displayPath + alias.name;
@@ -4347,16 +4358,8 @@ class DocSearch {
                 }
             };
 
-            aliases.forEach((alias, i) => {
-                // @ts-expect-error
-                alias.desc = descs[i];
-            });
             aliases.forEach(pushFunc);
             // @ts-expect-error
-            crateAliases.forEach((alias, i) => {
-                alias.desc = crateDescs[i];
-            });
-            // @ts-expect-error
             crateAliases.forEach(pushFunc);
         };
 
@@ -4802,7 +4805,7 @@ async function addTab(array, query, display) {
         output.className = "search-results " + extraClass;
 
         const lis = Promise.all(array.map(async item => {
-            const name = item.name;
+            const name = item.is_alias ? item.original.name : item.name;
             const type = itemTypes[item.ty];
             const longType = longItemTypes[item.ty];
             const typeName = longType.length !== 0 ? `${longType}` : "?";
@@ -4822,7 +4825,7 @@ async function addTab(array, query, display) {
             let alias = " ";
             if (item.is_alias) {
                 alias = ` <div class="alias">\
-<b>${item.alias}</b><i class="grey">&nbsp;- see&nbsp;</i>\
+<b>${item.name}</b><i class="grey">&nbsp;- see&nbsp;</i>\
 </div>`;
             }
             resultName.insertAdjacentHTML(
@@ -5201,6 +5204,7 @@ function registerSearchEvents() {
         if (searchState.input.value.length === 0) {
             searchState.hideResults();
         } else {
+            // @ts-ignore
             searchState.timeout = setTimeout(search, 500);
         }
     };
@@ -5842,8 +5846,8 @@ Lev1TParametricDescription.prototype.offsetIncrs3 = /*2 bits per value */ new In
 // be called ONLY when the whole file has been parsed and loaded.
 
 // @ts-expect-error
-function initSearch(searchIndx) {
-    rawSearchIndex = searchIndx;
+function initSearch(searchIndex) {
+    rawSearchIndex = searchIndex;
     if (typeof window !== "undefined") {
         // @ts-expect-error
         docSearch = new DocSearch(rawSearchIndex, ROOT_PATH, searchState);
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 0a84d8caa30..08bc0bb1950 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -50,7 +50,7 @@ impl JsonRenderer<'_> {
         let span = item.span(self.tcx);
         let visibility = item.visibility(self.tcx);
         let clean::ItemInner { name, item_id, .. } = *item.inner;
-        let id = self.id_from_item(&item);
+        let id = self.id_from_item(item);
         let inner = match item.kind {
             clean::KeywordItem => return None,
             clean::StrippedItem(ref inner) => {
@@ -86,14 +86,14 @@ impl JsonRenderer<'_> {
         items
             .iter()
             .filter(|i| !i.is_stripped() && !i.is_keyword())
-            .map(|i| self.id_from_item(&i))
+            .map(|i| self.id_from_item(i))
             .collect()
     }
 
     fn ids_keeping_stripped(&self, items: &[clean::Item]) -> Vec<Option<Id>> {
         items
             .iter()
-            .map(|i| (!i.is_stripped() && !i.is_keyword()).then(|| self.id_from_item(&i)))
+            .map(|i| (!i.is_stripped() && !i.is_keyword()).then(|| self.id_from_item(i)))
             .collect()
     }
 }
@@ -358,12 +358,12 @@ impl FromClean<clean::Struct> for Struct {
         let clean::Struct { ctor_kind, generics, fields } = struct_;
 
         let kind = match ctor_kind {
-            Some(CtorKind::Fn) => StructKind::Tuple(renderer.ids_keeping_stripped(&fields)),
+            Some(CtorKind::Fn) => StructKind::Tuple(renderer.ids_keeping_stripped(fields)),
             Some(CtorKind::Const) => {
                 assert!(fields.is_empty());
                 StructKind::Unit
             }
-            None => StructKind::Plain { fields: renderer.ids(&fields), has_stripped_fields },
+            None => StructKind::Plain { fields: renderer.ids(fields), has_stripped_fields },
         };
 
         Struct {
@@ -381,7 +381,7 @@ impl FromClean<clean::Union> for Union {
         Union {
             generics: generics.into_json(renderer),
             has_stripped_fields,
-            fields: renderer.ids(&fields),
+            fields: renderer.ids(fields),
             impls: Vec::new(), // Added in JsonRenderer::item
         }
     }
@@ -659,7 +659,7 @@ impl FromClean<clean::FnDecl> for FunctionSignature {
         let clean::FnDecl { inputs, output, c_variadic } = decl;
         FunctionSignature {
             inputs: inputs
-                .into_iter()
+                .iter()
                 .map(|param| {
                     // `_` is the most sensible name for missing param names.
                     let name = param.name.unwrap_or(kw::Underscore).to_string();
@@ -684,7 +684,7 @@ impl FromClean<clean::Trait> for Trait {
             is_auto,
             is_unsafe,
             is_dyn_compatible,
-            items: renderer.ids(&items),
+            items: renderer.ids(items),
             generics: generics.into_json(renderer),
             bounds: bounds.into_json(renderer),
             implementations: Vec::new(), // Added in JsonRenderer::item
@@ -727,7 +727,7 @@ impl FromClean<clean::Impl> for Impl {
                 .collect(),
             trait_: trait_.into_json(renderer),
             for_: for_.into_json(renderer),
-            items: renderer.ids(&items),
+            items: renderer.ids(items),
             is_negative,
             is_synthetic,
             blanket_impl: blanket_impl.map(|x| x.into_json(renderer)),
@@ -770,7 +770,7 @@ impl FromClean<clean::Variant> for Variant {
 
         let kind = match &variant.kind {
             CLike => VariantKind::Plain,
-            Tuple(fields) => VariantKind::Tuple(renderer.ids_keeping_stripped(&fields)),
+            Tuple(fields) => VariantKind::Tuple(renderer.ids_keeping_stripped(fields)),
             Struct(s) => VariantKind::Struct {
                 has_stripped_fields: s.has_stripped_entries(),
                 fields: renderer.ids(&s.fields),
diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs
index 600a4b429f3..760e48baffa 100644
--- a/src/librustdoc/json/mod.rs
+++ b/src/librustdoc/json/mod.rs
@@ -133,7 +133,7 @@ fn target(sess: &rustc_session::Session) -> types::Target {
     let feature_stability: FxHashMap<&str, Stability> = sess
         .target
         .rust_target_features()
-        .into_iter()
+        .iter()
         .copied()
         .map(|(name, stability, _)| (name, stability))
         .collect();
@@ -143,7 +143,7 @@ fn target(sess: &rustc_session::Session) -> types::Target {
         target_features: sess
             .target
             .rust_target_features()
-            .into_iter()
+            .iter()
             .copied()
             .filter(|(_, stability, _)| {
                 // Describe only target features which the user can toggle
@@ -157,7 +157,7 @@ fn target(sess: &rustc_session::Session) -> types::Target {
                         _ => None,
                     },
                     implies_features: implied_features
-                        .into_iter()
+                        .iter()
                         .copied()
                         .filter(|name| {
                             // Imply only target features which the user can toggle
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index ca6f67eb6df..5a9aa2a94c8 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -274,7 +274,7 @@ impl From<DiagnosticInfo<'_>> for OwnedDiagnosticInfo {
 }
 
 impl OwnedDiagnosticInfo {
-    pub(crate) fn into_info(&self) -> DiagnosticInfo<'_> {
+    pub(crate) fn as_info(&self) -> DiagnosticInfo<'_> {
         DiagnosticInfo {
             item: &self.item,
             ori_link: &self.ori_link,
@@ -1177,7 +1177,7 @@ impl LinkCollector<'_, '_> {
                     // Primitive types are always valid.
                     Res::Primitive(_) => true,
                 });
-                let diag_info = info.diag_info.into_info();
+                let diag_info = info.diag_info.as_info();
                 match info.resolved.len() {
                     1 => {
                         let (res, fragment) = info.resolved.pop().unwrap();
@@ -1243,17 +1243,16 @@ impl LinkCollector<'_, '_> {
             disambiguator,
             None | Some(Disambiguator::Namespace(Namespace::TypeNS) | Disambiguator::Primitive)
         ) && !matches!(res, Res::Primitive(_))
+            && let Some(prim) = resolve_primitive(path_str, TypeNS)
         {
-            if let Some(prim) = resolve_primitive(path_str, TypeNS) {
-                // `prim@char`
-                if matches!(disambiguator, Some(Disambiguator::Primitive)) {
-                    res = prim;
-                } else {
-                    // `[char]` when a `char` module is in scope
-                    let candidates = &[(res, res.def_id(self.cx.tcx)), (prim, None)];
-                    ambiguity_error(self.cx, &diag_info, path_str, candidates, true);
-                    return None;
-                }
+            // `prim@char`
+            if matches!(disambiguator, Some(Disambiguator::Primitive)) {
+                res = prim;
+            } else {
+                // `[char]` when a `char` module is in scope
+                let candidates = &[(res, res.def_id(self.cx.tcx)), (prim, None)];
+                ambiguity_error(self.cx, &diag_info, path_str, candidates, true);
+                return None;
             }
         }
 
@@ -2233,7 +2232,7 @@ fn ambiguity_error(
     // proc macro can exist in multiple namespaces at once, so we need to compare `DefIds`
     //  to remove the candidate in the fn namespace.
     let mut possible_proc_macro_id = None;
-    let is_proc_macro_crate = cx.tcx.crate_types() == &[CrateType::ProcMacro];
+    let is_proc_macro_crate = cx.tcx.crate_types() == [CrateType::ProcMacro];
     let mut kinds = candidates
         .iter()
         .map(|(res, def_id)| {
diff --git a/src/librustdoc/passes/lint/redundant_explicit_links.rs b/src/librustdoc/passes/lint/redundant_explicit_links.rs
index 5757b6a9740..e69cf87f957 100644
--- a/src/librustdoc/passes/lint/redundant_explicit_links.rs
+++ b/src/librustdoc/passes/lint/redundant_explicit_links.rs
@@ -93,14 +93,14 @@ fn check_redundant_explicit_link<'md>(
         if let Event::Start(Tag::Link { link_type, dest_url, .. }) = event {
             let link_data = collect_link_data(&mut offset_iter);
 
-            if let Some(resolvable_link) = link_data.resolvable_link.as_ref() {
-                if &link_data.display_link.replace('`', "") != resolvable_link {
-                    // Skips if display link does not match to actual
-                    // resolvable link, usually happens if display link
-                    // has several segments, e.g.
-                    // [this is just an `Option`](Option)
-                    continue;
-                }
+            if let Some(resolvable_link) = link_data.resolvable_link.as_ref()
+                && &link_data.display_link.replace('`', "") != resolvable_link
+            {
+                // Skips if display link does not match to actual
+                // resolvable link, usually happens if display link
+                // has several segments, e.g.
+                // [this is just an `Option`](Option)
+                continue;
             }
 
             let explicit_link = dest_url.to_string();
diff --git a/src/librustdoc/passes/strip_aliased_non_local.rs b/src/librustdoc/passes/strip_aliased_non_local.rs
index b53e3b4e3d7..bb13308e6c2 100644
--- a/src/librustdoc/passes/strip_aliased_non_local.rs
+++ b/src/librustdoc/passes/strip_aliased_non_local.rs
@@ -47,13 +47,11 @@ impl DocFolder for NonLocalStripper<'_> {
         // FIXME(#125009): Not-local should probably consider same Cargo workspace
         if let Some(def_id) = i.def_id()
             && !def_id.is_local()
-        {
-            if i.is_doc_hidden()
+            && (i.is_doc_hidden()
                 // Default to *not* stripping items with inherited visibility.
-                || i.visibility(self.tcx).is_some_and(|viz| viz != Visibility::Public)
-            {
-                return Some(strip_item(i));
-            }
+                || i.visibility(self.tcx).is_some_and(|viz| viz != Visibility::Public))
+        {
+            return Some(strip_item(i));
         }
 
         Some(self.fold_item_recur(i))
diff --git a/src/llvm-project b/src/llvm-project
-Subproject d3c793b025645a4565ac59aceb30d2d116ff1a4
+Subproject e8a2ffcf322f45b8dce82c65ab27a3e2430a6b5
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 07df893ae3c..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
@@ -306,7 +306,7 @@ impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering {
                     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<(TraitItemId, Ident)> = None;
diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs
index 2bf52216b83..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,
diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs
index d32017a8b41..1bf03480c82 100644
--- a/src/tools/clippy/clippy_lints/src/len_zero.rs
+++ b/src/tools/clippy/clippy_lints/src/len_zero.rs
@@ -125,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);
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/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs
index 329f7193437..c4a3d10299b 100644
--- a/src/tools/clippy/clippy_lints/src/missing_inline.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs
@@ -101,7 +101,7 @@ 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 {
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 cf70e883bd0..216f168471e 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
@@ -249,7 +249,7 @@ fn is_stable(cx: &LateContext<'_>, mut def_id: DefId, msrv: Msrv) -> bool {
             let stable = match since {
                 StableSince::Version(v) => msrv.meets(cx, v),
                 StableSince::Current => msrv.current(cx).is_none(),
-                StableSince::Err => false,
+                StableSince::Err(_) => false,
             };
 
             if !stable {
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/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_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/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/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index ad8c816e204..ff1ee663f9b 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -89,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;
@@ -3245,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;
@@ -3256,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:: ::
@@ -3268,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))
     }
 }
 
diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
index 942c71ac33b..b3356450d38 100644
--- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
+++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
@@ -432,7 +432,7 @@ pub fn is_stable_const_fn(cx: &LateContext<'_>, def_id: DefId, msrv: Msrv) -> bo
                     let const_stab_rust_version = match since {
                         StableSince::Version(version) => version,
                         StableSince::Current => RustcVersion::CURRENT,
-                        StableSince::Err => return false,
+                        StableSince::Err(_) => return false,
                     };
 
                     msrv.meets(cx, const_stab_rust_version)
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/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/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/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/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs
index 33da1a25db1..12084fa0b15 100644
--- a/src/tools/compiletest/src/common.rs
+++ b/src/tools/compiletest/src/common.rs
@@ -88,11 +88,37 @@ string_enum! {
     }
 }
 
+string_enum! {
+    #[derive(Clone, Copy, PartialEq, Debug, Hash)]
+    pub enum RunResult {
+        Pass => "run-pass",
+        Fail => "run-fail",
+        Crash => "run-crash",
+    }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
+pub enum RunFailMode {
+    /// Running the program must make it exit with a regular failure exit code
+    /// in the range `1..=127`. If the program is terminated by e.g. a signal
+    /// the test will fail.
+    Fail,
+    /// Running the program must result in a crash, e.g. by `SIGABRT` or
+    /// `SIGSEGV` on Unix or on Windows by having an appropriate NTSTATUS high
+    /// bit in the exit code.
+    Crash,
+    /// Running the program must either fail or crash. Useful for e.g. sanitizer
+    /// tests since some sanitizer implementations exit the process with code 1
+    /// to in the face of memory errors while others abort (crash) the process
+    /// in the face of memory errors.
+    FailOrCrash,
+}
+
 #[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
 pub enum FailMode {
     Check,
     Build,
-    Run,
+    Run(RunFailMode),
 }
 
 string_enum! {
@@ -149,6 +175,36 @@ pub enum Sanitizer {
     Hwaddress,
 }
 
+#[derive(Clone, Copy, Debug, PartialEq)]
+pub enum CodegenBackend {
+    Cranelift,
+    Gcc,
+    Llvm,
+}
+
+impl<'a> TryFrom<&'a str> for CodegenBackend {
+    type Error = &'static str;
+
+    fn try_from(value: &'a str) -> Result<Self, Self::Error> {
+        match value.to_lowercase().as_str() {
+            "cranelift" => Ok(Self::Cranelift),
+            "gcc" => Ok(Self::Gcc),
+            "llvm" => Ok(Self::Llvm),
+            _ => Err("unknown backend"),
+        }
+    }
+}
+
+impl CodegenBackend {
+    pub fn as_str(self) -> &'static str {
+        match self {
+            Self::Cranelift => "cranelift",
+            Self::Gcc => "gcc",
+            Self::Llvm => "llvm",
+        }
+    }
+}
+
 /// Configuration for `compiletest` *per invocation*.
 ///
 /// In terms of `bootstrap`, this means that `./x test tests/ui tests/run-make` actually correspond
@@ -625,6 +681,9 @@ pub struct Config {
     /// need `core` stubs in cross-compilation scenarios that do not otherwise want/need to
     /// `-Zbuild-std`. Used in e.g. ABI tests.
     pub minicore_path: Utf8PathBuf,
+
+    /// Current codegen backend used.
+    pub codegen_backend: CodegenBackend,
 }
 
 impl Config {
@@ -727,6 +786,7 @@ impl Config {
             profiler_runtime: Default::default(),
             diff_command: Default::default(),
             minicore_path: Default::default(),
+            codegen_backend: CodegenBackend::Llvm,
         }
     }
 
diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs
index 93133ea0bfd..1397c87ab07 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, PassMode, TestMode};
+use crate::common::{CodegenBackend, Config, Debugger, FailMode, PassMode, RunFailMode, TestMode};
 use crate::debuggers::{extract_cdb_version, extract_gdb_version};
 use crate::directives::auxiliary::{AuxProps, parse_and_update_aux};
 use crate::directives::needs::CachedNeedsConditions;
@@ -654,7 +654,13 @@ impl TestProps {
             Some(FailMode::Build)
         } else if config.parse_name_directive(ln, "run-fail") {
             check_ui("run");
-            Some(FailMode::Run)
+            Some(FailMode::Run(RunFailMode::Fail))
+        } else if config.parse_name_directive(ln, "run-crash") {
+            check_ui("run");
+            Some(FailMode::Run(RunFailMode::Crash))
+        } else if config.parse_name_directive(ln, "run-fail-or-crash") {
+            check_ui("run");
+            Some(FailMode::Run(RunFailMode::FailOrCrash))
         } else {
             None
         };
@@ -812,6 +818,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "ignore-arm-unknown-linux-musleabihf",
     "ignore-auxiliary",
     "ignore-avr",
+    "ignore-backends",
     "ignore-beta",
     "ignore-cdb",
     "ignore-compare-mode-next-solver",
@@ -901,6 +908,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "min-llvm-version",
     "min-system-llvm-version",
     "needs-asm-support",
+    "needs-backends",
     "needs-crate-type",
     "needs-deterministic-layouts",
     "needs-dlltool",
@@ -1007,7 +1015,9 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "regex-error-pattern",
     "remap-src-base",
     "revisions",
+    "run-crash",
     "run-fail",
+    "run-fail-or-crash",
     "run-flags",
     "run-pass",
     "run-rustfix",
@@ -1661,6 +1671,8 @@ pub(crate) fn make_test_description<R: Read>(
             decision!(cfg::handle_only(config, ln));
             decision!(needs::handle_needs(&cache.needs, config, ln));
             decision!(ignore_llvm(config, path, ln));
+            decision!(ignore_backends(config, path, ln));
+            decision!(needs_backends(config, path, ln));
             decision!(ignore_cdb(config, ln));
             decision!(ignore_gdb(config, ln));
             decision!(ignore_lldb(config, ln));
@@ -1787,6 +1799,49 @@ fn ignore_lldb(config: &Config, line: &str) -> IgnoreDecision {
     IgnoreDecision::Continue
 }
 
+fn ignore_backends(config: &Config, path: &Utf8Path, line: &str) -> IgnoreDecision {
+    if let Some(backends_to_ignore) = config.parse_name_value_directive(line, "ignore-backends") {
+        for backend in backends_to_ignore.split_whitespace().map(|backend| {
+            match CodegenBackend::try_from(backend) {
+                Ok(backend) => backend,
+                Err(error) => {
+                    panic!("Invalid ignore-backends value `{backend}` in `{path}`: {error}")
+                }
+            }
+        }) {
+            if config.codegen_backend == backend {
+                return IgnoreDecision::Ignore {
+                    reason: format!("{} backend is marked as ignore", backend.as_str()),
+                };
+            }
+        }
+    }
+    IgnoreDecision::Continue
+}
+
+fn needs_backends(config: &Config, path: &Utf8Path, line: &str) -> IgnoreDecision {
+    if let Some(needed_backends) = config.parse_name_value_directive(line, "needs-backends") {
+        if !needed_backends
+            .split_whitespace()
+            .map(|backend| match CodegenBackend::try_from(backend) {
+                Ok(backend) => backend,
+                Err(error) => {
+                    panic!("Invalid needs-backends value `{backend}` in `{path}`: {error}")
+                }
+            })
+            .any(|backend| config.codegen_backend == backend)
+        {
+            return IgnoreDecision::Ignore {
+                reason: format!(
+                    "{} backend is not part of required backends",
+                    config.codegen_backend.as_str()
+                ),
+            };
+        }
+    }
+    IgnoreDecision::Continue
+}
+
 fn ignore_llvm(config: &Config, path: &Utf8Path, line: &str) -> IgnoreDecision {
     if let Some(needed_components) =
         config.parse_name_value_directive(line, "needs-llvm-components")
diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs
index f3b3605a120..41bed8ed8a0 100644
--- a/src/tools/compiletest/src/lib.rs
+++ b/src/tools/compiletest/src/lib.rs
@@ -39,7 +39,7 @@ use walkdir::WalkDir;
 
 use self::directives::{EarlyProps, make_test_description};
 use crate::common::{
-    CompareMode, Config, Debugger, PassMode, TestMode, TestPaths, UI_EXTENSIONS,
+    CodegenBackend, CompareMode, Config, Debugger, PassMode, TestMode, TestPaths, UI_EXTENSIONS,
     expected_output_path, output_base_dir, output_relative_path,
 };
 use crate::directives::DirectivesCache;
@@ -203,6 +203,12 @@ pub fn parse_config(args: Vec<String>) -> Config {
             "debugger",
             "only test a specific debugger in debuginfo tests",
             "gdb | lldb | cdb",
+        )
+        .optopt(
+            "",
+            "codegen-backend",
+            "the codegen backend currently used",
+            "CODEGEN BACKEND NAME",
         );
 
     let (argv0, args_) = args.split_first().unwrap();
@@ -264,6 +270,15 @@ pub fn parse_config(args: Vec<String>) -> Config {
             || directives::extract_llvm_version_from_binary(&matches.opt_str("llvm-filecheck")?),
         );
 
+    let codegen_backend = match matches.opt_str("codegen-backend").as_deref() {
+        Some(backend) => match CodegenBackend::try_from(backend) {
+            Ok(backend) => backend,
+            Err(error) => panic!("invalid value `{backend}` for `--codegen-backend`: {error}"),
+        },
+        // By default, it's always llvm.
+        None => CodegenBackend::Llvm,
+    };
+
     let run_ignored = matches.opt_present("ignored");
     let with_rustc_debug_assertions = matches.opt_present("with-rustc-debug-assertions");
     let with_std_debug_assertions = matches.opt_present("with-std-debug-assertions");
@@ -449,6 +464,8 @@ pub fn parse_config(args: Vec<String>) -> Config {
         diff_command: matches.opt_str("compiletest-diff-tool"),
 
         minicore_path: opt_path(matches, "minicore-path"),
+
+        codegen_backend,
     }
 }
 
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index cb8f593c9df..f66d4f98f1f 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -16,8 +16,8 @@ use regex::{Captures, Regex};
 use tracing::*;
 
 use crate::common::{
-    CompareMode, Config, Debugger, FailMode, PassMode, TestMode, TestPaths, TestSuite,
-    UI_EXTENSIONS, UI_FIXED, UI_RUN_STDERR, UI_RUN_STDOUT, UI_STDERR, UI_STDOUT, UI_SVG,
+    CompareMode, Config, Debugger, FailMode, PassMode, RunFailMode, RunResult, 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,
 };
@@ -282,7 +282,8 @@ impl<'test> TestCx<'test> {
     fn should_run(&self, pm: Option<PassMode>) -> WillExecute {
         let test_should_run = match self.config.mode {
             TestMode::Ui
-                if pm == Some(PassMode::Run) || self.props.fail_mode == Some(FailMode::Run) =>
+                if pm == Some(PassMode::Run)
+                    || matches!(self.props.fail_mode, Some(FailMode::Run(_))) =>
             {
                 true
             }
diff --git a/src/tools/compiletest/src/runtest/ui.rs b/src/tools/compiletest/src/runtest/ui.rs
index f6bc85cd051..0507c2600ae 100644
--- a/src/tools/compiletest/src/runtest/ui.rs
+++ b/src/tools/compiletest/src/runtest/ui.rs
@@ -6,8 +6,8 @@ use rustfix::{Filter, apply_suggestions, get_suggestions_from_json};
 use tracing::debug;
 
 use super::{
-    AllowUnused, Emit, FailMode, LinkToAux, PassMode, TargetLocation, TestCx, TestOutput,
-    Truncated, UI_FIXED, WillExecute,
+    AllowUnused, Emit, FailMode, LinkToAux, PassMode, RunFailMode, RunResult, TargetLocation,
+    TestCx, TestOutput, Truncated, UI_FIXED, WillExecute,
 };
 use crate::json;
 
@@ -140,12 +140,53 @@ impl TestCx<'_> {
                     &proc_res,
                 );
             }
+            let code = proc_res.status.code();
+            let run_result = if proc_res.status.success() {
+                RunResult::Pass
+            } else if code.is_some_and(|c| c >= 1 && c <= 127) {
+                RunResult::Fail
+            } else {
+                RunResult::Crash
+            };
+            // Help users understand why the test failed by including the actual
+            // exit code and actual run result in the failure message.
+            let pass_hint = format!("code={code:?} so test would pass with `{run_result}`");
             if self.should_run_successfully(pm) {
-                if !proc_res.status.success() {
-                    self.fatal_proc_rec("test run failed!", &proc_res);
+                if run_result != RunResult::Pass {
+                    self.fatal_proc_rec(
+                        &format!("test did not exit with success! {pass_hint}"),
+                        &proc_res,
+                    );
+                }
+            } else if self.props.fail_mode == Some(FailMode::Run(RunFailMode::Fail)) {
+                // If the test is marked as `run-fail` but do not support
+                // unwinding we allow it to crash, since a panic will trigger an
+                // abort (crash) instead of unwind (exit with code 101).
+                let crash_ok = !self.config.can_unwind();
+                if run_result != RunResult::Fail && !(crash_ok && run_result == RunResult::Crash) {
+                    let err = if crash_ok {
+                        format!(
+                            "test did not exit with failure or crash (`{}` can't unwind)! {pass_hint}",
+                            self.config.target
+                        )
+                    } else {
+                        format!("test did not exit with failure! {pass_hint}")
+                    };
+                    self.fatal_proc_rec(&err, &proc_res);
                 }
-            } else if proc_res.status.success() {
-                self.fatal_proc_rec("test run succeeded!", &proc_res);
+            } else if self.props.fail_mode == Some(FailMode::Run(RunFailMode::Crash)) {
+                if run_result != RunResult::Crash {
+                    self.fatal_proc_rec(&format!("test did not crash! {pass_hint}"), &proc_res);
+                }
+            } else if self.props.fail_mode == Some(FailMode::Run(RunFailMode::FailOrCrash)) {
+                if run_result != RunResult::Fail && run_result != RunResult::Crash {
+                    self.fatal_proc_rec(
+                        &format!("test did not exit with failure or crash! {pass_hint}"),
+                        &proc_res,
+                    );
+                }
+            } else {
+                unreachable!("run_ui_test() must not be called if the test should not run");
             }
 
             self.get_output(&proc_res)
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/miri/src/alloc_addresses/mod.rs b/src/tools/miri/src/alloc_addresses/mod.rs
index 3cc38fa087c..10339928ac2 100644
--- a/src/tools/miri/src/alloc_addresses/mod.rs
+++ b/src/tools/miri/src/alloc_addresses/mod.rs
@@ -157,7 +157,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                         this.get_alloc_bytes_unchecked_raw(alloc_id)?
                     }
                 }
-                AllocKind::Function | AllocKind::VTable => {
+                AllocKind::Function | AllocKind::Virtual => {
                     // Allocate some dummy memory to get a unique address for this function/vtable.
                     let alloc_bytes = MiriAllocBytes::from_bytes(
                         &[0u8; 1],
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/borrow_tracker/stacked_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs
index 834a4b41f22..e834fdffdd1 100644
--- a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs
+++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs
@@ -650,7 +650,7 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> {
                         dcx.log_protector();
                     }
                 },
-                AllocKind::Function | AllocKind::VTable | AllocKind::Dead => {
+                AllocKind::Function | AllocKind::Virtual | AllocKind::Dead => {
                     // No stacked borrows on these allocations.
                 }
             }
@@ -1021,7 +1021,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 trace!("Stacked Borrows tag {tag:?} exposed in {alloc_id:?}");
                 alloc_extra.borrow_tracker_sb().borrow_mut().exposed_tags.insert(tag);
             }
-            AllocKind::Function | AllocKind::VTable | AllocKind::Dead => {
+            AllocKind::Function | AllocKind::Virtual | AllocKind::Dead => {
                 // No stacked borrows on these allocations.
             }
         }
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 c157c69d7c8..aa92f8a8c30 100644
--- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs
+++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs
@@ -673,7 +673,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 trace!("Tree Borrows tag {tag:?} exposed in {alloc_id:?}");
                 alloc_extra.borrow_tracker_tb().borrow_mut().expose_tag(tag);
             }
-            AllocKind::Function | AllocKind::VTable | AllocKind::Dead => {
+            AllocKind::Function | AllocKind::Virtual | AllocKind::Dead => {
                 // No tree borrows on these allocations.
             }
         }
diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs
index 9c077e5b7b6..f309e34c75b 100644
--- a/src/tools/miri/src/machine.rs
+++ b/src/tools/miri/src/machine.rs
@@ -1086,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()
@@ -1790,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
diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.rs
index 047fe07df14..5778765589d 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.rs
@@ -1,4 +1,6 @@
 //@ignore-target: windows # No pthreads on Windows
+//@ normalize-stderr-test: "(\n)ALLOC \(.*\) \{\n(.*\n)*\}(\n)" -> "${1}ALLOC DUMP${3}"
+//@ normalize-stderr-test: "\[0x[0-9a-z]..0x[0-9a-z]\]" -> "[0xX..0xY]"
 
 /// Test that destroying a pthread_cond twice fails, even without a check for number validity
 
@@ -15,6 +17,6 @@ fn main() {
         libc::pthread_cond_destroy(cond.as_mut_ptr());
 
         libc::pthread_cond_destroy(cond.as_mut_ptr());
-        //~^ ERROR: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+        //~^ ERROR: /Undefined Behavior: reading memory .*, but memory is uninitialized/
     }
 }
diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.stderr
index 7abdfa87f75..6156070cf95 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.stderr
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0xX..0xY], but memory is uninitialized at [0xX..0xY], and this operation requires initialized memory
   --> tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.rs:LL:CC
    |
 LL |         libc::pthread_cond_destroy(cond.as_mut_ptr());
@@ -9,6 +9,9 @@ LL |         libc::pthread_cond_destroy(cond.as_mut_ptr());
    = note: BACKTRACE:
    = note: inside `main` at tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.rs:LL:CC
 
+Uninitialized memory occurred at ALLOC[0xX..0xY], in this allocation:
+ALLOC DUMP
+
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
 error: aborting due to 1 previous error
diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.rs
index 90e33d58673..91169660491 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.rs
@@ -1,5 +1,7 @@
 //@ignore-target: windows # No pthreads on Windows
 //@ignore-target: apple # Our macOS condattr don't have any fields so we do not notice this.
+//@ normalize-stderr-test: "(\n)ALLOC \(.*\) \{\n(.*\n)*\}(\n)" -> "${1}ALLOC DUMP${3}"
+//@ normalize-stderr-test: "\[0x[0-9a-z]..0x[0-9a-z]\]" -> "[0xX..0xY]"
 
 /// Test that destroying a pthread_condattr twice fails, even without a check for number validity
 
@@ -13,6 +15,6 @@ fn main() {
         libc::pthread_condattr_destroy(attr.as_mut_ptr());
 
         libc::pthread_condattr_destroy(attr.as_mut_ptr());
-        //~^ ERROR: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+        //~^ ERROR: /Undefined Behavior: reading memory .*, but memory is uninitialized/
     }
 }
diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.stderr
index 28a66253ae8..da64970ff2e 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.stderr
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0xX..0xY], but memory is uninitialized at [0xX..0xY], and this operation requires initialized memory
   --> tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.rs:LL:CC
    |
 LL |         libc::pthread_condattr_destroy(attr.as_mut_ptr());
@@ -9,6 +9,9 @@ LL |         libc::pthread_condattr_destroy(attr.as_mut_ptr());
    = note: BACKTRACE:
    = note: inside `main` at tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.rs:LL:CC
 
+Uninitialized memory occurred at ALLOC[0xX..0xY], in this allocation:
+ALLOC DUMP
+
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
 error: aborting due to 1 previous error
diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.rs
index 1792c227e13..f04fe8be6b3 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.rs
@@ -1,4 +1,6 @@
 //@ignore-target: windows # No pthreads on Windows
+//@ normalize-stderr-test: "(\n)ALLOC \(.*\) \{\n(.*\n)*\}(\n)" -> "${1}ALLOC DUMP${3}"
+//@ normalize-stderr-test: "\[0x[0-9a-z]..0x[0-9a-z]\]" -> "[0xX..0xY]"
 
 /// Test that destroying a pthread_mutex twice fails, even without a check for number validity
 
@@ -16,6 +18,6 @@ fn main() {
         libc::pthread_mutex_destroy(mutex.as_mut_ptr());
 
         libc::pthread_mutex_destroy(mutex.as_mut_ptr());
-        //~^ ERROR: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+        //~^ ERROR: /Undefined Behavior: reading memory .*, but memory is uninitialized/
     }
 }
diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.stderr
index e7a6dee0203..05db823b252 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.stderr
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0xX..0xY], but memory is uninitialized at [0xX..0xY], and this operation requires initialized memory
   --> tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.rs:LL:CC
    |
 LL |         libc::pthread_mutex_destroy(mutex.as_mut_ptr());
@@ -9,6 +9,9 @@ LL |         libc::pthread_mutex_destroy(mutex.as_mut_ptr());
    = note: BACKTRACE:
    = note: inside `main` at tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.rs:LL:CC
 
+Uninitialized memory occurred at ALLOC[0xX..0xY], in this allocation:
+ALLOC DUMP
+
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
 error: aborting due to 1 previous error
diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.rs
index 3711c1f8dc1..d9daf5259bb 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.rs
@@ -1,4 +1,6 @@
 //@ignore-target: windows # No pthreads on Windows
+//@ normalize-stderr-test: "(\n)ALLOC \(.*\) \{\n(.*\n)*\}(\n)" -> "${1}ALLOC DUMP${3}"
+//@ normalize-stderr-test: "\[0x[0-9a-z]..0x[0-9a-z]\]" -> "[0xX..0xY]"
 
 /// Test that destroying a pthread_mutexattr twice fails, even without a check for number validity
 
@@ -12,6 +14,6 @@ fn main() {
         libc::pthread_mutexattr_destroy(attr.as_mut_ptr());
 
         libc::pthread_mutexattr_destroy(attr.as_mut_ptr());
-        //~^ ERROR: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+        //~^ ERROR: /Undefined Behavior: reading memory .*, but memory is uninitialized/
     }
 }
diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.stderr
index 0c9ee71de45..ee3883de36b 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.stderr
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0xX..0xY], but memory is uninitialized at [0xX..0xY], and this operation requires initialized memory
   --> tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.rs:LL:CC
    |
 LL |         libc::pthread_mutexattr_destroy(attr.as_mut_ptr());
@@ -9,6 +9,9 @@ LL |         libc::pthread_mutexattr_destroy(attr.as_mut_ptr());
    = note: BACKTRACE:
    = note: inside `main` at tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.rs:LL:CC
 
+Uninitialized memory occurred at ALLOC[0xX..0xY], in this allocation:
+ALLOC DUMP
+
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
 error: aborting due to 1 previous error
diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.rs
index 6a31e972e68..720ba71d238 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.rs
@@ -1,4 +1,6 @@
 //@ignore-target: windows # No pthreads on Windows
+//@ normalize-stderr-test: "(\n)ALLOC \(.*\) \{\n(.*\n)*\}(\n)" -> "${1}ALLOC DUMP${3}"
+//@ normalize-stderr-test: "\[0x[0-9a-z]..0x[0-9a-z]\]" -> "[0xX..0xY]"
 
 /// Test that destroying a pthread_rwlock twice fails, even without a check for number validity
 
@@ -9,6 +11,6 @@ fn main() {
         libc::pthread_rwlock_destroy(&mut lock);
 
         libc::pthread_rwlock_destroy(&mut lock);
-        //~^ ERROR: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+        //~^ ERROR: /Undefined Behavior: reading memory .*, but memory is uninitialized/
     }
 }
diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.stderr
index 836f0d060bd..430398dc8fd 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.stderr
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0xX..0xY], but memory is uninitialized at [0xX..0xY], and this operation requires initialized memory
   --> tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.rs:LL:CC
    |
 LL |         libc::pthread_rwlock_destroy(&mut lock);
@@ -9,6 +9,9 @@ LL |         libc::pthread_rwlock_destroy(&mut lock);
    = note: BACKTRACE:
    = note: inside `main` at tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.rs:LL:CC
 
+Uninitialized memory occurred at ALLOC[0xX..0xY], in this allocation:
+ALLOC DUMP
+
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
 error: aborting due to 1 previous error
diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_after.stderr b/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_after.stderr
index 6a7d9a495f9..3252368ea6d 100644
--- a/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_after.stderr
+++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_after.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0x0..0x4], but memory is uninitialized at [0x0..0x4], and this operation requires initialized memory
   --> tests/fail/function_calls/arg_inplace_observe_after.rs:LL:CC
    |
 LL |             _observe = non_copy.0;
@@ -9,6 +9,11 @@ LL |             _observe = non_copy.0;
    = note: BACKTRACE:
    = note: inside `main` at tests/fail/function_calls/arg_inplace_observe_after.rs:LL:CC
 
+Uninitialized memory occurred at ALLOC[0x0..0x4], in this allocation:
+ALLOC (stack variable, size: 4, align: 4) {
+    __ __ __ __                                     │ ░░░░
+}
+
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
 error: aborting due to 1 previous error
diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.none.stderr b/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.none.stderr
index 0fc634bb7fc..09a5b9a6496 100644
--- a/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.none.stderr
+++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.none.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0x0..0x4], but memory is uninitialized at [0x0..0x4], and this operation requires initialized memory
   --> tests/fail/function_calls/arg_inplace_observe_during.rs:LL:CC
    |
 LL |     unsafe { ptr.read() };
@@ -14,6 +14,11 @@ note: inside `main`
 LL |             Call(_unit = change_arg(Move(*ptr), ptr), ReturnTo(after_call), UnwindContinue())
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
+Uninitialized memory occurred at ALLOC[0x0..0x4], in this allocation:
+ALLOC (stack variable, size: 4, align: 4) {
+    __ __ __ __                                     │ ░░░░
+}
+
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
 error: aborting due to 1 previous error
diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_read.none.stderr b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_read.none.stderr
index 746ab2e59ca..24091547258 100644
--- a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_read.none.stderr
+++ b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_read.none.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0x0..0x4], but memory is uninitialized at [0x0..0x4], and this operation requires initialized memory
   --> tests/fail/function_calls/return_pointer_aliasing_read.rs:LL:CC
    |
 LL |     unsafe { ptr.read() };
@@ -14,6 +14,11 @@ note: inside `main`
 LL |             Call(*ptr = myfun(ptr), ReturnTo(after_call), UnwindContinue())
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
+Uninitialized memory occurred at ALLOC[0x0..0x4], in this allocation:
+ALLOC (stack variable, size: 4, align: 4) {
+    __ __ __ __                                     │ ░░░░
+}
+
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
 error: aborting due to 1 previous error
diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr b/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr
index 7747a75d1cf..93720ca7d27 100644
--- a/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr
+++ b/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr
@@ -3,7 +3,7 @@ thread 'main' panicked at tests/fail/function_calls/return_pointer_on_unwind.rs:
 explicit panic
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
 note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0x0..0x4], but memory is uninitialized at [0x0..0x4], and this operation requires initialized memory
   --> tests/fail/function_calls/return_pointer_on_unwind.rs:LL:CC
    |
 LL |     dbg!(x.0);
@@ -15,6 +15,19 @@ LL |     dbg!(x.0);
    = note: inside `main` at RUSTLIB/std/src/macros.rs:LL:CC
    = note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
 
+Uninitialized memory occurred at ALLOC[0x0..0x4], in this allocation:
+ALLOC (stack variable, size: 132, align: 4) {
+    0x00 │ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ ░░░░░░░░░░░░░░░░
+    0x10 │ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ ░░░░░░░░░░░░░░░░
+    0x20 │ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ ░░░░░░░░░░░░░░░░
+    0x30 │ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ ░░░░░░░░░░░░░░░░
+    0x40 │ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ ░░░░░░░░░░░░░░░░
+    0x50 │ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ ░░░░░░░░░░░░░░░░
+    0x60 │ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ ░░░░░░░░░░░░░░░░
+    0x70 │ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ ░░░░░░░░░░░░░░░░
+    0x80 │ __ __ __ __                                     │ ░░░░
+}
+
 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_metadata_uninit_slice_data.rs b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_data.rs
index 0c305eed6e1..c03e468cfba 100644
--- a/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_data.rs
+++ b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_data.rs
@@ -1,4 +1,7 @@
 //@compile-flags: -Zmiri-disable-validation
+//@ normalize-stderr-test: "(\n)ALLOC \(.*\) \{\n(.*\n)*\}(\n)" -> "${1}ALLOC DUMP${3}"
+//@ normalize-stderr-test: "\[0x[0-9a-z]..0x[0-9a-z]\]" -> "[0xX..0xY]"
+
 #![feature(core_intrinsics, custom_mir)]
 use std::intrinsics::mir::*;
 
@@ -9,7 +12,7 @@ use std::intrinsics::mir::*;
 pub unsafe fn deref_meta(p: *const *const [i32]) -> usize {
     mir! {
         {
-            RET = PtrMetadata(*p); //~ ERROR: Undefined Behavior: using uninitialized data
+            RET = PtrMetadata(*p); //~ ERROR: /Undefined Behavior: .* but memory is uninitialized/
             Return()
         }
     }
diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_data.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_data.stderr
index 1c22876ba43..1e7f500edb2 100644
--- a/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_data.stderr
+++ b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_data.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0xX..0xY], but memory is uninitialized at [0xX..0xY], and this operation requires initialized memory
   --> tests/fail/intrinsics/ptr_metadata_uninit_slice_data.rs:LL:CC
    |
 LL |             RET = PtrMetadata(*p);
@@ -14,6 +14,9 @@ note: inside `main`
 LL |         let _meta = deref_meta(p.as_ptr().cast());
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
+Uninitialized memory occurred at ALLOC[0xX..0xY], in this allocation:
+ALLOC DUMP
+
 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_metadata_uninit_slice_len.rs b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_len.rs
index a2ffdc92c4e..7053c0f6e18 100644
--- a/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_len.rs
+++ b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_len.rs
@@ -1,4 +1,7 @@
 //@compile-flags: -Zmiri-disable-validation
+//@ normalize-stderr-test: "(\n)ALLOC \(.*\) \{\n(.*\n)*\}(\n)" -> "${1}ALLOC DUMP${3}"
+//@ normalize-stderr-test: "\[0x[0-9a-z]+..0x[0-9a-z]+\]" -> "[0xX..0xY]"
+
 #![feature(core_intrinsics, custom_mir)]
 use std::intrinsics::mir::*;
 
@@ -9,7 +12,7 @@ use std::intrinsics::mir::*;
 pub unsafe fn deref_meta(p: *const *const [i32]) -> usize {
     mir! {
         {
-            RET = PtrMetadata(*p); //~ ERROR: Undefined Behavior: using uninitialized data
+            RET = PtrMetadata(*p); //~ ERROR: /Undefined Behavior: .* but memory is uninitialized/
             Return()
         }
     }
diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_len.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_len.stderr
index 00e63b1275f..80b4c8bec0d 100644
--- a/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_len.stderr
+++ b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_len.stderr
@@ -12,7 +12,7 @@ LL |         (*p.as_mut_ptr().cast::<[*const i32; 2]>())[0] = 4 as *const i32;
    = note: BACKTRACE:
    = note: inside `main` at tests/fail/intrinsics/ptr_metadata_uninit_slice_len.rs:LL:CC
 
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0xX..0xY], but memory is uninitialized at [0xX..0xY], and this operation requires initialized memory
   --> tests/fail/intrinsics/ptr_metadata_uninit_slice_len.rs:LL:CC
    |
 LL |             RET = PtrMetadata(*p);
@@ -28,6 +28,9 @@ note: inside `main`
 LL |         let _meta = deref_meta(p.as_ptr().cast());
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
+Uninitialized memory occurred at ALLOC[0xX..0xY], in this allocation:
+ALLOC DUMP
+
 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/fail/intrinsics/ptr_metadata_uninit_thin.rs b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_thin.rs
index e5a51289a8a..3ba29847337 100644
--- a/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_thin.rs
+++ b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_thin.rs
@@ -1,4 +1,7 @@
 //@compile-flags: -Zmiri-disable-validation
+//@ normalize-stderr-test: "(\n)ALLOC \(.*\) \{\n(.*\n)*\}(\n)" -> "${1}ALLOC DUMP${3}"
+//@ normalize-stderr-test: "\[0x[0-9a-z]..0x[0-9a-z]\]" -> "[0xX..0xY]"
+
 #![feature(core_intrinsics, custom_mir)]
 use std::intrinsics::mir::*;
 
@@ -9,7 +12,7 @@ use std::intrinsics::mir::*;
 pub unsafe fn deref_meta(p: *const *const i32) -> () {
     mir! {
         {
-            RET = PtrMetadata(*p); //~ ERROR: Undefined Behavior: using uninitialized data
+            RET = PtrMetadata(*p); //~ ERROR: /Undefined Behavior: .*, but memory is uninitialized/
             Return()
         }
     }
diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_thin.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_thin.stderr
index 24066953d79..7a1f3d6ea84 100644
--- a/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_thin.stderr
+++ b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_thin.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0xX..0xY], but memory is uninitialized at [0xX..0xY], and this operation requires initialized memory
   --> tests/fail/intrinsics/ptr_metadata_uninit_thin.rs:LL:CC
    |
 LL |             RET = PtrMetadata(*p);
@@ -14,6 +14,9 @@ note: inside `main`
 LL |         let _meta = deref_meta(p.as_ptr());
    |                     ^^^^^^^^^^^^^^^^^^^^^^
 
+Uninitialized memory occurred at ALLOC[0xX..0xY], in this allocation:
+ALLOC DUMP
+
 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/read_from_trivial_switch.rs b/src/tools/miri/tests/fail/read_from_trivial_switch.rs
index d34b1cd5820..2696c42aeae 100644
--- a/src/tools/miri/tests/fail/read_from_trivial_switch.rs
+++ b/src/tools/miri/tests/fail/read_from_trivial_switch.rs
@@ -4,11 +4,14 @@
 //
 // See <https://github.com/rust-lang/miri/issues/4237>.
 
+//@ normalize-stderr-test: "(\n)ALLOC \(.*\) \{\n(.*\n)*\}(\n)" -> "${1}ALLOC DUMP${3}"
+//@ normalize-stderr-test: "\[0x[0-9a-z]..0x[0-9a-z]\]" -> "[0xX..0xY]"
+
 use std::mem::MaybeUninit;
 
 fn main() {
     let uninit: MaybeUninit<i32> = MaybeUninit::uninit();
     let bad_ref: &i32 = unsafe { uninit.assume_init_ref() };
     let &(0 | _) = bad_ref;
-    //~^ ERROR: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+    //~^ ERROR: /Undefined Behavior: .*, but memory is uninitialized .* requires initialized memory/
 }
diff --git a/src/tools/miri/tests/fail/read_from_trivial_switch.stderr b/src/tools/miri/tests/fail/read_from_trivial_switch.stderr
index 923d836ee0c..1dcc341b7e6 100644
--- a/src/tools/miri/tests/fail/read_from_trivial_switch.stderr
+++ b/src/tools/miri/tests/fail/read_from_trivial_switch.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0xX..0xY], but memory is uninitialized at [0xX..0xY], and this operation requires initialized memory
   --> tests/fail/read_from_trivial_switch.rs:LL:CC
    |
 LL |     let &(0 | _) = bad_ref;
@@ -9,6 +9,9 @@ LL |     let &(0 | _) = bad_ref;
    = note: BACKTRACE:
    = note: inside `main` at tests/fail/read_from_trivial_switch.rs:LL:CC
 
+Uninitialized memory occurred at ALLOC[0xX..0xY], in this allocation:
+ALLOC DUMP
+
 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/uninit/padding-enum.rs b/src/tools/miri/tests/fail/uninit/padding-enum.rs
index e1a16bea23c..606fa21016f 100644
--- a/src/tools/miri/tests/fail/uninit/padding-enum.rs
+++ b/src/tools/miri/tests/fail/uninit/padding-enum.rs
@@ -1,3 +1,6 @@
+//@ normalize-stderr-test: "(\n)ALLOC \(.*\) \{\n(.*\n)*\}(\n)" -> "${1}ALLOC DUMP${3}"
+//@ normalize-stderr-test: "\[0x[0-9a-z]..0x[0-9a-z]\]" -> "[0xX..0xY]"
+
 use std::mem;
 
 // We have three fields to avoid the ScalarPair optimization.
diff --git a/src/tools/miri/tests/fail/uninit/padding-enum.stderr b/src/tools/miri/tests/fail/uninit/padding-enum.stderr
index a9a5568f4e8..64229ac8817 100644
--- a/src/tools/miri/tests/fail/uninit/padding-enum.stderr
+++ b/src/tools/miri/tests/fail/uninit/padding-enum.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0xX..0xY], but memory is uninitialized at [0xX..0xY], and this operation requires initialized memory
   --> tests/fail/uninit/padding-enum.rs:LL:CC
    |
 LL |         let _val = *c.add(padding_offset);
@@ -9,6 +9,9 @@ LL |         let _val = *c.add(padding_offset);
    = note: BACKTRACE:
    = note: inside `main` at tests/fail/uninit/padding-enum.rs:LL:CC
 
+Uninitialized memory occurred at ALLOC[0xX..0xY], in this allocation:
+ALLOC DUMP
+
 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/uninit/padding-pair.rs b/src/tools/miri/tests/fail/uninit/padding-pair.rs
index c8c00b3c65a..70ae48ff77d 100644
--- a/src/tools/miri/tests/fail/uninit/padding-pair.rs
+++ b/src/tools/miri/tests/fail/uninit/padding-pair.rs
@@ -1,3 +1,6 @@
+//@ normalize-stderr-test: "(\n)ALLOC \(.*\) \{\n(.*\n)*\}(\n)" -> "${1}ALLOC DUMP${3}"
+//@ normalize-stderr-test: "\[0x[0-9a-z]..0x[0-9a-z]\]" -> "[0xX..0xY]"
+
 #![feature(core_intrinsics)]
 
 use std::mem::{self, MaybeUninit};
diff --git a/src/tools/miri/tests/fail/uninit/padding-pair.stderr b/src/tools/miri/tests/fail/uninit/padding-pair.stderr
index d281a351d41..2e7a577f204 100644
--- a/src/tools/miri/tests/fail/uninit/padding-pair.stderr
+++ b/src/tools/miri/tests/fail/uninit/padding-pair.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0xX..0xY], but memory is uninitialized at [0xX..0xY], and this operation requires initialized memory
   --> tests/fail/uninit/padding-pair.rs:LL:CC
    |
 LL |     let v = unsafe { *z.offset(first_undef) };
@@ -9,6 +9,9 @@ LL |     let v = unsafe { *z.offset(first_undef) };
    = note: BACKTRACE:
    = note: inside `main` at tests/fail/uninit/padding-pair.rs:LL:CC
 
+Uninitialized memory occurred at ALLOC[0xX..0xY], in this allocation:
+ALLOC DUMP
+
 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/uninit/padding-struct.stderr b/src/tools/miri/tests/fail/uninit/padding-struct.stderr
index 3298f6a4510..05d754625d3 100644
--- a/src/tools/miri/tests/fail/uninit/padding-struct.stderr
+++ b/src/tools/miri/tests/fail/uninit/padding-struct.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0x1..0x2], but memory is uninitialized at [0x1..0x2], and this operation requires initialized memory
   --> tests/fail/uninit/padding-struct.rs:LL:CC
    |
 LL |         let _val = *c.add(1);
@@ -9,6 +9,11 @@ LL |         let _val = *c.add(1);
    = note: BACKTRACE:
    = note: inside `main` at tests/fail/uninit/padding-struct.rs:LL:CC
 
+Uninitialized memory occurred at ALLOC[0x1..0x2], in this allocation:
+ALLOC (stack variable, size: 4, align: 2) {
+    00 __ 00 00                                     │ .░..
+}
+
 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/uninit/padding-wide-ptr.rs b/src/tools/miri/tests/fail/uninit/padding-wide-ptr.rs
index 4e363dbf81e..549785e0223 100644
--- a/src/tools/miri/tests/fail/uninit/padding-wide-ptr.rs
+++ b/src/tools/miri/tests/fail/uninit/padding-wide-ptr.rs
@@ -1,3 +1,6 @@
+//@ normalize-stderr-test: "(\n)ALLOC \(.*\) \{\n(.*\n)*\}(\n)" -> "${1}ALLOC DUMP${3}"
+//@ normalize-stderr-test: "\[0x[0-9a-z]..0x[0-9a-z]\]" -> "[0xX..0xY]"
+
 use std::mem;
 
 // If this is `None`, the metadata becomes padding.
diff --git a/src/tools/miri/tests/fail/uninit/padding-wide-ptr.stderr b/src/tools/miri/tests/fail/uninit/padding-wide-ptr.stderr
index d92d05ae631..ce11320ca1b 100644
--- a/src/tools/miri/tests/fail/uninit/padding-wide-ptr.stderr
+++ b/src/tools/miri/tests/fail/uninit/padding-wide-ptr.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0xX..0xY], but memory is uninitialized at [0xX..0xY], and this operation requires initialized memory
   --> tests/fail/uninit/padding-wide-ptr.rs:LL:CC
    |
 LL |         let _val = *c.add(mem::size_of::<*const u8>());
@@ -9,6 +9,9 @@ LL |         let _val = *c.add(mem::size_of::<*const u8>());
    = note: BACKTRACE:
    = note: inside `main` at tests/fail/uninit/padding-wide-ptr.rs:LL:CC
 
+Uninitialized memory occurred at ALLOC[0xX..0xY], in this allocation:
+ALLOC DUMP
+
 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/uninit/transmute-pair-uninit.rs b/src/tools/miri/tests/fail/uninit/transmute-pair-uninit.rs
index 0ba5520a544..c1d284c7057 100644
--- a/src/tools/miri/tests/fail/uninit/transmute-pair-uninit.rs
+++ b/src/tools/miri/tests/fail/uninit/transmute-pair-uninit.rs
@@ -1,3 +1,6 @@
+//@ normalize-stderr-test: "(\n)ALLOC \(.*\) \{\n(.*\n)*\}(\n)" -> "${1}ALLOC DUMP${3}"
+//@ normalize-stderr-test: "\[0x[0-9a-z]..0x[0-9a-z]\]" -> "[0xX..0xY]"
+
 #![feature(core_intrinsics)]
 
 use std::mem::{self, MaybeUninit};
diff --git a/src/tools/miri/tests/fail/uninit/transmute-pair-uninit.stderr b/src/tools/miri/tests/fail/uninit/transmute-pair-uninit.stderr
index 0ae0ce5de9c..eb049dd41ec 100644
--- a/src/tools/miri/tests/fail/uninit/transmute-pair-uninit.stderr
+++ b/src/tools/miri/tests/fail/uninit/transmute-pair-uninit.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0xX..0xY], but memory is uninitialized at [0xX..0xY], and this operation requires initialized memory
   --> tests/fail/uninit/transmute-pair-uninit.rs:LL:CC
    |
 LL |     let v = unsafe { *z.offset(first_undef) };
@@ -9,6 +9,9 @@ LL |     let v = unsafe { *z.offset(first_undef) };
    = note: BACKTRACE:
    = note: inside `main` at tests/fail/uninit/transmute-pair-uninit.rs:LL:CC
 
+Uninitialized memory occurred at ALLOC[0xX..0xY], in this allocation:
+ALLOC DUMP
+
 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/uninit/uninit_byte_read.stderr b/src/tools/miri/tests/fail/uninit/uninit_byte_read.stderr
index d2a5a2d3831..5a5aa12987c 100644
--- a/src/tools/miri/tests/fail/uninit/uninit_byte_read.stderr
+++ b/src/tools/miri/tests/fail/uninit/uninit_byte_read.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0x5..0x6], but memory is uninitialized at [0x5..0x6], and this operation requires initialized memory
   --> tests/fail/uninit/uninit_byte_read.rs:LL:CC
    |
 LL |     let undef = unsafe { *v.as_ptr().add(5) };
@@ -9,6 +9,11 @@ LL |     let undef = unsafe { *v.as_ptr().add(5) };
    = note: BACKTRACE:
    = note: inside `main` at tests/fail/uninit/uninit_byte_read.rs:LL:CC
 
+Uninitialized memory occurred at ALLOC[0x5..0x6], in this allocation:
+ALLOC (Rust heap, size: 10, align: 1) {
+    __ __ __ __ __ __ __ __ __ __                   │ ░░░░░░░░░░
+}
+
 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/validity/invalid_int_op.stderr b/src/tools/miri/tests/fail/validity/invalid_int_op.stderr
index 6e24cadfc20..0b1915621b2 100644
--- a/src/tools/miri/tests/fail/validity/invalid_int_op.stderr
+++ b/src/tools/miri/tests/fail/validity/invalid_int_op.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0x0..0x4], but memory is uninitialized at [0x0..0x4], and this operation requires initialized memory
   --> tests/fail/validity/invalid_int_op.rs:LL:CC
    |
 LL |     let i = unsafe { std::mem::MaybeUninit::<i32>::uninit().assume_init() };
@@ -9,6 +9,11 @@ LL |     let i = unsafe { std::mem::MaybeUninit::<i32>::uninit().assume_init() }
    = note: BACKTRACE:
    = note: inside `main` at tests/fail/validity/invalid_int_op.rs:LL:CC
 
+Uninitialized memory occurred at ALLOC[0x0..0x4], in this allocation:
+ALLOC (stack variable, size: 4, align: 4) {
+    __ __ __ __                                     │ ░░░░
+}
+
 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
index e267f82e215..7ab160773ff 100644
--- a/src/tools/miri/tests/native-lib/fail/tracing/partial_init.rs
+++ b/src/tools/miri/tests/native-lib/fail/tracing/partial_init.rs
@@ -20,6 +20,6 @@ fn partial_init() {
         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
+        let _val = *slice_ptr.offset(2); //~ ERROR: /Undefined Behavior: reading memory.*, but memory is uninitialized/
     }
 }
diff --git a/src/tools/miri/tests/native-lib/fail/tracing/partial_init.stderr b/src/tools/miri/tests/native-lib/fail/tracing/partial_init.stderr
index 84fd913b5e5..74a599ede5c 100644
--- a/src/tools/miri/tests/native-lib/fail/tracing/partial_init.stderr
+++ b/src/tools/miri/tests/native-lib/fail/tracing/partial_init.stderr
@@ -17,7 +17,7 @@ note: inside `main`
 LL |     partial_init();
    |     ^^^^^^^^^^^^^^
 
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0x2..0x3], but memory is uninitialized at [0x2..0x3], and this operation requires initialized memory
   --> tests/native-lib/fail/tracing/partial_init.rs:LL:CC
    |
 LL |         let _val = *slice_ptr.offset(2);
@@ -33,6 +33,11 @@ note: inside `main`
 LL |     partial_init();
    |     ^^^^^^^^^^^^^^
 
+Uninitialized memory occurred at ALLOC[0x2..0x3], in this allocation:
+ALLOC (stack variable, size: 3, align: 1) {
+    ╾00[wildcard] (1 ptr byte)╼ ╾00[wildcard] (1 ptr byte)╼ __                                        │ ━━░
+}
+
 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/panic/mir-validation.stderr b/src/tools/miri/tests/panic/mir-validation.stderr
index dc70d129da3..f801ac907e6 100644
--- a/src/tools/miri/tests/panic/mir-validation.stderr
+++ b/src/tools/miri/tests/panic/mir-validation.stderr
@@ -1,11 +1,15 @@
+error: internal compiler error: compiler/rustc_mir_transform/src/validate.rs:LL:CC: broken MIR in Item(DefId) (after phase change to runtime-optimized) at bb0[1]:
+                                place (*(_2.0: *mut i32)) has deref as a later projection (it is only permitted as the first projection)
+  --> tests/panic/mir-validation.rs:LL:CC
+   |
+LL |             *(tuple.0) = 1;
+   |             ^^^^^^^^^^^^^^
+
 
 thread 'rustc' panicked at compiler/rustc_mir_transform/src/validate.rs:LL:CC:
-broken MIR in Item(DefId) (after phase change to runtime-optimized) at bb0[1]:
-place (*(_2.0: *mut i32)) has deref as a later projection (it is only permitted as the first projection)
+Box<dyn Any>
 stack backtrace:
 
-error: the compiler unexpectedly panicked. this is a bug.
-
 
 
 
@@ -20,3 +24,5 @@ LL |     extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/pass-dep/libc/libc-time.rs b/src/tools/miri/tests/pass-dep/libc/libc-time.rs
index e8957846ad5..9e9fadfca9e 100644
--- a/src/tools/miri/tests/pass-dep/libc/libc-time.rs
+++ b/src/tools/miri/tests/pass-dep/libc/libc-time.rs
@@ -336,7 +336,7 @@ fn test_nanosleep() {
     let remainder = ptr::null_mut::<libc::timespec>();
     let is_error = unsafe { libc::nanosleep(&duration_zero, remainder) };
     assert_eq!(is_error, 0);
-    assert!(start_test_sleep.elapsed() < Duration::from_millis(10));
+    assert!(start_test_sleep.elapsed() < Duration::from_millis(100));
 
     let start_test_sleep = Instant::now();
     let duration_100_millis = libc::timespec { tv_sec: 0, tv_nsec: 1_000_000_000 / 10 };
@@ -390,7 +390,7 @@ mod test_clock_nanosleep {
             )
         };
         assert_eq!(error, 0);
-        assert!(start_test_sleep.elapsed() < Duration::from_millis(10));
+        assert!(start_test_sleep.elapsed() < Duration::from_millis(100));
 
         let start_test_sleep = Instant::now();
         let hunderd_millis_after_start = add_100_millis(timespec_now(libc::CLOCK_MONOTONIC));
@@ -417,7 +417,7 @@ mod test_clock_nanosleep {
             libc::clock_nanosleep(libc::CLOCK_MONOTONIC, NO_FLAGS, &duration_zero, remainder)
         };
         assert_eq!(error, 0);
-        assert!(start_test_sleep.elapsed() < Duration::from_millis(10));
+        assert!(start_test_sleep.elapsed() < Duration::from_millis(100));
 
         let start_test_sleep = Instant::now();
         let duration_100_millis = libc::timespec { tv_sec: 0, tv_nsec: 1_000_000_000 / 10 };
diff --git a/src/tools/miri/tests/pass/intrinsics/type-id.rs b/src/tools/miri/tests/pass/intrinsics/type-id.rs
new file mode 100644
index 00000000000..123fdbdc9ce
--- /dev/null
+++ b/src/tools/miri/tests/pass/intrinsics/type-id.rs
@@ -0,0 +1,19 @@
+use std::any::{Any, TypeId};
+
+fn main() {
+    let t1 = TypeId::of::<u64>();
+    let t2 = TypeId::of::<u64>();
+    assert_eq!(t1, t2);
+    let t3 = TypeId::of::<usize>();
+    assert_ne!(t1, t3);
+
+    let _ = format!("{t1:?}"); // test that we can debug-print
+
+    let b = Box::new(0u64) as Box<dyn Any>;
+    assert_eq!(*b.downcast_ref::<u64>().unwrap(), 0);
+    assert!(b.downcast_ref::<usize>().is_none());
+
+    // Get the first pointer chunk and try to make it a ZST ref.
+    // This used to trigger an error because TypeId allocs got misclassified as "LiveData".
+    let _raw_chunk = unsafe { (&raw const t1).cast::<&()>().read() };
+}
diff --git a/src/tools/opt-dist/src/environment.rs b/src/tools/opt-dist/src/environment.rs
index 946e926a3c0..d41dc80e6b2 100644
--- a/src/tools/opt-dist/src/environment.rs
+++ b/src/tools/opt-dist/src/environment.rs
@@ -27,6 +27,7 @@ pub struct Environment {
     shared_llvm: bool,
     run_tests: bool,
     fast_try_build: bool,
+    build_llvm: bool,
 }
 
 impl Environment {
@@ -111,6 +112,10 @@ impl Environment {
     pub fn is_fast_try_build(&self) -> bool {
         self.fast_try_build
     }
+
+    pub fn build_llvm(&self) -> bool {
+        self.build_llvm
+    }
 }
 
 /// What is the extension of binary executables on this platform?
diff --git a/src/tools/opt-dist/src/exec.rs b/src/tools/opt-dist/src/exec.rs
index 0dc6e56b9d5..56eff2ca2a7 100644
--- a/src/tools/opt-dist/src/exec.rs
+++ b/src/tools/opt-dist/src/exec.rs
@@ -139,8 +139,10 @@ impl Bootstrap {
         self
     }
 
-    pub fn llvm_pgo_optimize(mut self, profile: &LlvmPGOProfile) -> Self {
-        self.cmd = self.cmd.arg("--llvm-profile-use").arg(profile.0.as_str());
+    pub fn llvm_pgo_optimize(mut self, profile: Option<&LlvmPGOProfile>) -> Self {
+        if let Some(prof) = profile {
+            self.cmd = self.cmd.arg("--llvm-profile-use").arg(prof.0.as_str());
+        }
         self
     }
 
@@ -174,8 +176,10 @@ impl Bootstrap {
         self
     }
 
-    pub fn with_bolt_profile(mut self, profile: BoltProfile) -> Self {
-        self.cmd = self.cmd.arg("--reproducible-artifact").arg(profile.0.as_str());
+    pub fn with_bolt_profile(mut self, profile: Option<BoltProfile>) -> Self {
+        if let Some(prof) = profile {
+            self.cmd = self.cmd.arg("--reproducible-artifact").arg(prof.0.as_str());
+        }
         self
     }
 
diff --git a/src/tools/opt-dist/src/main.rs b/src/tools/opt-dist/src/main.rs
index 9c8a6637a3b..7857f196626 100644
--- a/src/tools/opt-dist/src/main.rs
+++ b/src/tools/opt-dist/src/main.rs
@@ -98,6 +98,10 @@ enum EnvironmentCmd {
         /// Perform tests after final build if it's not a fast try build
         #[arg(long)]
         run_tests: bool,
+
+        /// Will be LLVM built during the run?
+        #[arg(long, default_value_t = true, action(clap::ArgAction::Set))]
+        build_llvm: bool,
     },
     /// Perform an optimized build on Linux CI, from inside Docker.
     LinuxCi {
@@ -133,6 +137,7 @@ fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec<String>)>
             benchmark_cargo_config,
             shared,
             run_tests,
+            build_llvm,
         } => {
             let env = EnvironmentBuilder::default()
                 .host_tuple(target_triple)
@@ -148,6 +153,7 @@ fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec<String>)>
                 .benchmark_cargo_config(benchmark_cargo_config)
                 .run_tests(run_tests)
                 .fast_try_build(is_fast_try_build)
+                .build_llvm(build_llvm)
                 .build()?;
 
             (env, shared.build_args)
@@ -172,6 +178,7 @@ fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec<String>)>
                 .skipped_tests(vec![])
                 .run_tests(true)
                 .fast_try_build(is_fast_try_build)
+                .build_llvm(true)
                 .build()?;
 
             (env, shared.build_args)
@@ -193,6 +200,7 @@ fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec<String>)>
                 .skipped_tests(vec![])
                 .run_tests(true)
                 .fast_try_build(is_fast_try_build)
+                .build_llvm(true)
                 .build()?;
 
             (env, shared.build_args)
@@ -255,30 +263,35 @@ fn execute_pipeline(
     // Stage 2: Gather LLVM PGO profiles
     // Here we build a PGO instrumented LLVM, reusing the previously PGO optimized rustc.
     // Then we use the instrumented LLVM to gather LLVM PGO profiles.
-    let llvm_pgo_profile = timer.section("Stage 2 (LLVM PGO)", |stage| {
-        // Remove the previous, uninstrumented build of LLVM.
-        clear_llvm_files(env)?;
+    let llvm_pgo_profile = if env.build_llvm() {
+        timer.section("Stage 2 (LLVM PGO)", |stage| {
+            // Remove the previous, uninstrumented build of LLVM.
+            clear_llvm_files(env)?;
 
-        let llvm_profile_dir_root = env.artifact_dir().join("llvm-pgo");
+            let llvm_profile_dir_root = env.artifact_dir().join("llvm-pgo");
 
-        stage.section("Build PGO instrumented LLVM", |section| {
-            Bootstrap::build(env)
-                .llvm_pgo_instrument(&llvm_profile_dir_root)
-                .avoid_rustc_rebuild()
-                .run(section)
-        })?;
+            stage.section("Build PGO instrumented LLVM", |section| {
+                Bootstrap::build(env)
+                    .llvm_pgo_instrument(&llvm_profile_dir_root)
+                    .avoid_rustc_rebuild()
+                    .run(section)
+            })?;
 
-        let profile = stage
-            .section("Gather profiles", |_| gather_llvm_profiles(env, &llvm_profile_dir_root))?;
+            let profile = stage.section("Gather profiles", |_| {
+                gather_llvm_profiles(env, &llvm_profile_dir_root)
+            })?;
 
-        print_free_disk_space()?;
+            print_free_disk_space()?;
 
-        // Proactively delete the instrumented artifacts, to avoid using them by accident in
-        // follow-up stages.
-        clear_llvm_files(env)?;
+            // Proactively delete the instrumented artifacts, to avoid using them by accident in
+            // follow-up stages.
+            clear_llvm_files(env)?;
 
-        Ok(profile)
-    })?;
+            Ok(Some(profile))
+        })?
+    } else {
+        None
+    };
 
     let bolt_profiles = if env.use_bolt() {
         // Stage 3: Build BOLT instrumented LLVM
@@ -286,37 +299,43 @@ fn execute_pipeline(
         // Note that we don't remove LLVM artifacts after this step, so that they are reused in the final dist build.
         // BOLT instrumentation is performed "on-the-fly" when the LLVM library is copied to the sysroot of rustc,
         // therefore the LLVM artifacts on disk are not "tainted" with BOLT instrumentation and they can be reused.
+        let libdir = env.build_artifacts().join("stage2").join("lib");
         timer.section("Stage 3 (BOLT)", |stage| {
-            stage.section("Build PGO optimized LLVM", |stage| {
-                Bootstrap::build(env)
-                    .with_llvm_bolt_ldflags()
-                    .llvm_pgo_optimize(&llvm_pgo_profile)
-                    .avoid_rustc_rebuild()
-                    .run(stage)
-            })?;
-
-            let libdir = env.build_artifacts().join("stage2").join("lib");
-            // The actual name will be something like libLLVM.so.18.1-rust-dev.
-            let llvm_lib = io::find_file_in_dir(&libdir, "libLLVM.so", "")?;
-
-            log::info!("Optimizing {llvm_lib} with BOLT");
-
-            // FIXME(kobzol): try gather profiles together, at once for LLVM and rustc
-            // Instrument the libraries and gather profiles
-            let llvm_profile = with_bolt_instrumented(&llvm_lib, |llvm_profile_dir| {
-                stage.section("Gather profiles", |_| {
-                    gather_bolt_profiles(env, "LLVM", llvm_benchmarks(env), llvm_profile_dir)
-                })
-            })?;
-            print_free_disk_space()?;
-
-            // Now optimize the library with BOLT. The `libLLVM-XXX.so` library is actually hard-linked
-            // from several places, and this specific path (`llvm_lib`) will *not* be packaged into
-            // the final dist build. However, when BOLT optimizes an artifact, it does so *in-place*,
-            // therefore it will actually optimize all the hard links, which means that the final
-            // packaged `libLLVM.so` file *will* be BOLT optimized.
-            bolt_optimize(&llvm_lib, &llvm_profile, env)
-                .context("Could not optimize LLVM with BOLT")?;
+            let llvm_profile = if env.build_llvm() {
+                stage.section("Build PGO optimized LLVM", |stage| {
+                    Bootstrap::build(env)
+                        .with_llvm_bolt_ldflags()
+                        .llvm_pgo_optimize(llvm_pgo_profile.as_ref())
+                        .avoid_rustc_rebuild()
+                        .run(stage)
+                })?;
+
+                // The actual name will be something like libLLVM.so.18.1-rust-dev.
+                let llvm_lib = io::find_file_in_dir(&libdir, "libLLVM.so", "")?;
+
+                log::info!("Optimizing {llvm_lib} with BOLT");
+
+                // FIXME(kobzol): try gather profiles together, at once for LLVM and rustc
+                // Instrument the libraries and gather profiles
+                let llvm_profile = with_bolt_instrumented(&llvm_lib, |llvm_profile_dir| {
+                    stage.section("Gather profiles", |_| {
+                        gather_bolt_profiles(env, "LLVM", llvm_benchmarks(env), llvm_profile_dir)
+                    })
+                })?;
+                print_free_disk_space()?;
+
+                // Now optimize the library with BOLT. The `libLLVM-XXX.so` library is actually hard-linked
+                // from several places, and this specific path (`llvm_lib`) will *not* be packaged into
+                // the final dist build. However, when BOLT optimizes an artifact, it does so *in-place*,
+                // therefore it will actually optimize all the hard links, which means that the final
+                // packaged `libLLVM.so` file *will* be BOLT optimized.
+                bolt_optimize(&llvm_lib, &llvm_profile, env)
+                    .context("Could not optimize LLVM with BOLT")?;
+
+                Some(llvm_profile)
+            } else {
+                None
+            };
 
             let rustc_lib = io::find_file_in_dir(&libdir, "librustc_driver", ".so")?;
 
@@ -334,15 +353,16 @@ fn execute_pipeline(
             bolt_optimize(&rustc_lib, &rustc_profile, env)
                 .context("Could not optimize rustc with BOLT")?;
 
-            // LLVM is not being cleared here, we want to use the BOLT-optimized LLVM
-            Ok(vec![llvm_profile, rustc_profile])
+            // LLVM is not being cleared here. Either we built it and we want to use the BOLT-optimized LLVM, or we
+            // didn't build it, so we don't want to remove it.
+            Ok(vec![llvm_profile, Some(rustc_profile)])
         })?
     } else {
         vec![]
     };
 
     let mut dist = Bootstrap::dist(env, &dist_args)
-        .llvm_pgo_optimize(&llvm_pgo_profile)
+        .llvm_pgo_optimize(llvm_pgo_profile.as_ref())
         .rustc_pgo_optimize(&rustc_pgo_profile)
         .avoid_rustc_rebuild();
 
diff --git a/src/tools/opt-dist/src/training.rs b/src/tools/opt-dist/src/training.rs
index 36a7d6a7cba..ae062d5c60c 100644
--- a/src/tools/opt-dist/src/training.rs
+++ b/src/tools/opt-dist/src/training.rs
@@ -163,7 +163,9 @@ pub fn gather_rustc_profiles(
     let merged_profile = env.artifact_dir().join("rustc-pgo.profdata");
     log::info!("Merging Rustc PGO profiles to {merged_profile}");
 
-    merge_llvm_profiles(env, &merged_profile, profile_root, LlvmProfdata::Target)?;
+    let llvm_profdata = if env.build_llvm() { LlvmProfdata::Target } else { LlvmProfdata::Host };
+
+    merge_llvm_profiles(env, &merged_profile, profile_root, llvm_profdata)?;
     log_profile_stats("Rustc", &merged_profile, profile_root)?;
 
     // We don't need the individual .profraw files now that they have been merged
diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock
index 7432a82080d..e55cd80943d 100644
--- a/src/tools/rust-analyzer/Cargo.lock
+++ b/src/tools/rust-analyzer/Cargo.lock
@@ -154,6 +154,22 @@ dependencies = [
 ]
 
 [[package]]
+name = "cargo-util-schemas"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7dc1a6f7b5651af85774ae5a34b4e8be397d9cf4bc063b7e6dbd99a841837830"
+dependencies = [
+ "semver",
+ "serde",
+ "serde-untagged",
+ "serde-value",
+ "thiserror 2.0.12",
+ "toml",
+ "unicode-xid",
+ "url",
+]
+
+[[package]]
 name = "cargo_metadata"
 version = "0.20.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -161,7 +177,22 @@ checksum = "4f7835cfc6135093070e95eb2b53e5d9b5c403dc3a6be6040ee026270aa82502"
 dependencies = [
  "camino",
  "cargo-platform",
- "cargo-util-schemas",
+ "cargo-util-schemas 0.2.0",
+ "semver",
+ "serde",
+ "serde_json",
+ "thiserror 2.0.12",
+]
+
+[[package]]
+name = "cargo_metadata"
+version = "0.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5cfca2aaa699835ba88faf58a06342a314a950d2b9686165e038286c30316868"
+dependencies = [
+ "camino",
+ "cargo-platform",
+ "cargo-util-schemas 0.8.2",
  "semver",
  "serde",
  "serde_json",
@@ -1190,13 +1221,16 @@ checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
 name = "lsp-server"
 version = "0.7.8"
 dependencies = [
+ "anyhow",
  "crossbeam-channel",
  "ctrlc",
  "log",
  "lsp-types",
+ "rustc-hash 2.1.1",
  "serde",
  "serde_derive",
  "serde_json",
+ "toolchain",
 ]
 
 [[package]]
@@ -1471,7 +1505,7 @@ dependencies = [
  "edition",
  "expect-test",
  "ra-ap-rustc_lexer",
- "rustc-literal-escaper 0.0.4",
+ "rustc-literal-escaper",
  "stdx",
  "tracing",
 ]
@@ -1599,7 +1633,7 @@ dependencies = [
 name = "proc-macro-test"
 version = "0.0.0"
 dependencies = [
- "cargo_metadata",
+ "cargo_metadata 0.20.0",
 ]
 
 [[package]]
@@ -1640,7 +1674,7 @@ version = "0.0.0"
 dependencies = [
  "anyhow",
  "base-db",
- "cargo_metadata",
+ "cargo_metadata 0.21.0",
  "cfg",
  "expect-test",
  "intern",
@@ -1722,9 +1756,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_abi"
-version = "0.116.0"
+version = "0.121.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a967e3a9cd3e38b543f503978e0eccee461e3aea3f7b10e944959bff41dbe612"
+checksum = "3ee51482d1c9d3e538acda8cce723db8eea1a81540544bf362bf4c3d841b2329"
 dependencies = [
  "bitflags 2.9.1",
  "ra-ap-rustc_hashes",
@@ -1734,18 +1768,18 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_hashes"
-version = "0.116.0"
+version = "0.121.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ea4c755ecbbffa5743c251344f484ebe571ec7bc5b36d80b2a8ae775d1a7a40"
+checksum = "19c8f1e0c28e24e1b4c55dc08058c6c9829df2204497d4034259f491d348c204"
 dependencies = [
  "rustc-stable-hash",
 ]
 
 [[package]]
 name = "ra-ap-rustc_index"
-version = "0.116.0"
+version = "0.121.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aca7ad7cf911538c619caa2162339fe98637e9e46f11bb0484ef96735df4d64a"
+checksum = "5f33f429cec6b92fa2c7243883279fb29dd233fdc3e94099aff32aa91aa87f50"
 dependencies = [
  "ra-ap-rustc_index_macros",
  "smallvec",
@@ -1753,9 +1787,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_index_macros"
-version = "0.116.0"
+version = "0.121.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8767ba551c9355bc3031be072cc4bb0381106e5e7cd275e72b7a8c76051c4070"
+checksum = "b9b55910dbe1fe7ef34bdc1d1bcb41e99b377eb680ea58a1218d95d6b4152257"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1764,9 +1798,9 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_lexer"
-version = "0.116.0"
+version = "0.121.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6101374afb267e6c27e4e2eb0b1352e9f3504c1a8f716f619cd39244e2ed92ab"
+checksum = "22944e31fb91e9b3e75bcbc91e37d958b8c0825a6160927f2856831d2ce83b36"
 dependencies = [
  "memchr",
  "unicode-properties",
@@ -1775,19 +1809,19 @@ dependencies = [
 
 [[package]]
 name = "ra-ap-rustc_parse_format"
-version = "0.116.0"
+version = "0.121.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ecd88a19f00da4f43e6727d5013444cbc399804b5046dfa2bbcd28ebed3970ce"
+checksum = "81057891bc2063ad9e353f29462fbc47a0f5072560af34428ae9313aaa5e9d97"
 dependencies = [
  "ra-ap-rustc_lexer",
- "rustc-literal-escaper 0.0.2",
+ "rustc-literal-escaper",
 ]
 
 [[package]]
 name = "ra-ap-rustc_pattern_analysis"
-version = "0.116.0"
+version = "0.121.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb332dd32d7850a799862533b1c021e6062558861a4ad57817bf522499fbb892"
+checksum = "fe21a3542980d56d2435e96c2720773cac1c63fd4db666417e414729da192eb3"
 dependencies = [
  "ra-ap-rustc_index",
  "rustc-hash 2.1.1",
@@ -1855,7 +1889,7 @@ version = "0.0.0"
 dependencies = [
  "anyhow",
  "base64",
- "cargo_metadata",
+ "cargo_metadata 0.21.0",
  "cfg",
  "crossbeam-channel",
  "dirs",
@@ -1934,12 +1968,6 @@ checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d"
 
 [[package]]
 name = "rustc-literal-escaper"
-version = "0.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0041b6238913c41fe704213a4a9329e2f685a156d1781998128b4149c230ad04"
-
-[[package]]
-name = "rustc-literal-escaper"
 version = "0.0.4"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ab03008eb631b703dd16978282ae36c73282e7922fe101a4bd072a40ecea7b8b"
@@ -2231,7 +2259,7 @@ dependencies = [
  "rayon",
  "rowan",
  "rustc-hash 2.1.1",
- "rustc-literal-escaper 0.0.4",
+ "rustc-literal-escaper",
  "rustc_apfloat",
  "smol_str",
  "stdx",
diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml
index d268ce5b0bb..41fa06a76a7 100644
--- a/src/tools/rust-analyzer/Cargo.toml
+++ b/src/tools/rust-analyzer/Cargo.toml
@@ -4,7 +4,7 @@ exclude = ["crates/proc-macro-srv/proc-macro-test/imp"]
 resolver = "2"
 
 [workspace.package]
-rust-version = "1.86"
+rust-version = "1.88"
 edition = "2024"
 license = "MIT OR Apache-2.0"
 authors = ["rust-analyzer team"]
@@ -89,11 +89,11 @@ vfs-notify = { path = "./crates/vfs-notify", version = "0.0.0" }
 vfs = { path = "./crates/vfs", version = "0.0.0" }
 edition = { path = "./crates/edition", version = "0.0.0" }
 
-ra-ap-rustc_lexer = { version = "0.116", default-features = false }
-ra-ap-rustc_parse_format = { version = "0.116", default-features = false }
-ra-ap-rustc_index = { version = "0.116", default-features = false }
-ra-ap-rustc_abi = { version = "0.116", default-features = false }
-ra-ap-rustc_pattern_analysis = { version = "0.116", default-features = false }
+ra-ap-rustc_lexer = { version = "0.121", default-features = false }
+ra-ap-rustc_parse_format = { version = "0.121", default-features = false }
+ra-ap-rustc_index = { version = "0.121", default-features = false }
+ra-ap-rustc_abi = { version = "0.121", default-features = false }
+ra-ap-rustc_pattern_analysis = { version = "0.121", default-features = false }
 
 # local crates that aren't published to crates.io. These should not have versions.
 
@@ -106,7 +106,7 @@ lsp-server = { version = "0.7.8" }
 anyhow = "1.0.98"
 arrayvec = "0.7.6"
 bitflags = "2.9.1"
-cargo_metadata = "0.20.0"
+cargo_metadata = "0.21.0"
 camino = "1.1.10"
 chalk-solve = { version = "0.103.0", default-features = false }
 chalk-ir = "0.103.0"
@@ -138,7 +138,11 @@ 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.23.0", default-features = true, features = ["rayon","salsa_unstable", "macros"] }
+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" }
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 51612f341a1..d3dfc05eb29 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
@@ -22,6 +22,7 @@ use rustc_hash::FxHashMap;
 use smallvec::SmallVec;
 use span::{Edition, SyntaxContext};
 use syntax::{AstPtr, SyntaxNodePtr, ast};
+use thin_vec::ThinVec;
 use triomphe::Arc;
 use tt::TextRange;
 
@@ -93,17 +94,17 @@ pub type TypeSource = InFile<TypePtr>;
 pub type LifetimePtr = AstPtr<ast::Lifetime>;
 pub type LifetimeSource = InFile<LifetimePtr>;
 
+// We split the store into types-only and expressions, because most stores (e.g. generics)
+// don't store any expressions and this saves memory. Same thing for the source map.
 #[derive(Debug, PartialEq, Eq)]
-pub struct ExpressionStore {
-    pub exprs: Arena<Expr>,
-    pub pats: Arena<Pat>,
-    pub bindings: Arena<Binding>,
-    pub labels: Arena<Label>,
-    pub types: Arena<TypeRef>,
-    pub lifetimes: Arena<LifetimeRef>,
+struct ExpressionOnlyStore {
+    exprs: Arena<Expr>,
+    pats: Arena<Pat>,
+    bindings: Arena<Binding>,
+    labels: Arena<Label>,
     /// Id of the closure/coroutine that owns the corresponding binding. If a binding is owned by the
     /// top level expression, it will not be listed in here.
-    pub binding_owners: FxHashMap<BindingId, ExprId>,
+    binding_owners: FxHashMap<BindingId, ExprId>,
     /// Block expressions in this store that may contain inner items.
     block_scopes: Box<[BlockId]>,
 
@@ -114,8 +115,15 @@ pub struct ExpressionStore {
     ident_hygiene: FxHashMap<ExprOrPatId, HygieneId>,
 }
 
+#[derive(Debug, PartialEq, Eq)]
+pub struct ExpressionStore {
+    expr_only: Option<Box<ExpressionOnlyStore>>,
+    pub types: Arena<TypeRef>,
+    pub lifetimes: Arena<LifetimeRef>,
+}
+
 #[derive(Debug, Eq, Default)]
-pub struct ExpressionStoreSourceMap {
+struct ExpressionOnlySourceMap {
     // 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).
     expr_map: FxHashMap<ExprSource, ExprOrPatId>,
@@ -127,12 +135,6 @@ pub struct ExpressionStoreSourceMap {
     label_map: FxHashMap<LabelSource, LabelId>,
     label_map_back: ArenaMap<LabelId, LabelSource>,
 
-    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>()]>>,
 
@@ -143,14 +145,17 @@ pub struct ExpressionStoreSourceMap {
 
     template_map: Option<Box<FormatTemplate>>,
 
-    pub expansions: FxHashMap<InFile<MacroCallPtr>, MacroCallId>,
+    expansions: FxHashMap<InFile<MacroCallPtr>, MacroCallId>,
 
     /// Diagnostics accumulated during lowering. These contain `AstPtr`s and so are stored in
     /// the source map (since they're just as volatile).
-    pub diagnostics: Vec<ExpressionStoreDiagnostics>,
+    //
+    // We store diagnostics on the `ExpressionOnlySourceMap` because diagnostics are rare (except
+    // maybe for cfgs, and they are also not common in type places).
+    diagnostics: ThinVec<ExpressionStoreDiagnostics>,
 }
 
-impl PartialEq for ExpressionStoreSourceMap {
+impl PartialEq for ExpressionOnlySourceMap {
     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
@@ -162,10 +167,6 @@ impl PartialEq for ExpressionStoreSourceMap {
             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
@@ -179,14 +180,40 @@ impl PartialEq for ExpressionStoreSourceMap {
         *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
     }
 }
 
+#[derive(Debug, Eq, Default)]
+pub struct ExpressionStoreSourceMap {
+    expr_only: Option<Box<ExpressionOnlySourceMap>>,
+
+    types_map_back: ArenaMap<TypeRefId, TypeSource>,
+    types_map: FxHashMap<TypeSource, TypeRefId>,
+
+    lifetime_map_back: ArenaMap<LifetimeRefId, LifetimeSource>,
+    #[expect(
+        unused,
+        reason = "this is here for completeness, and maybe we'll need it in the future"
+    )]
+    lifetime_map: FxHashMap<LifetimeSource, LifetimeRefId>,
+}
+
+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_only, types_map_back, types_map: _, lifetime_map_back, lifetime_map: _ } =
+            self;
+        *expr_only == other.expr_only
+            && *types_map_back == other.types_map_back
+            && *lifetime_map_back == other.lifetime_map_back
+    }
+}
+
 /// The body of an item (function, const etc.).
 #[derive(Debug, Eq, PartialEq, Default)]
 pub struct ExpressionStoreBuilder {
@@ -199,6 +226,42 @@ pub struct ExpressionStoreBuilder {
     pub types: Arena<TypeRef>,
     block_scopes: Vec<BlockId>,
     ident_hygiene: FxHashMap<ExprOrPatId, HygieneId>,
+
+    // 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).
+    expr_map: FxHashMap<ExprSource, ExprOrPatId>,
+    expr_map_back: ArenaMap<ExprId, ExprOrPatSource>,
+
+    pat_map: FxHashMap<PatSource, ExprOrPatId>,
+    pat_map_back: ArenaMap<PatId, ExprOrPatSource>,
+
+    label_map: FxHashMap<LabelSource, LabelId>,
+    label_map_back: ArenaMap<LabelId, LabelSource>,
+
+    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>>,
+
+    expansions: FxHashMap<InFile<MacroCallPtr>, MacroCallId>,
+
+    /// Diagnostics accumulated during lowering. These contain `AstPtr`s and so are stored in
+    /// the source map (since they're just as volatile).
+    //
+    // We store diagnostics on the `ExpressionOnlySourceMap` because diagnostics are rare (except
+    // maybe for cfgs, and they are also not common in type places).
+    pub(crate) diagnostics: Vec<ExpressionStoreDiagnostics>,
 }
 
 #[derive(Default, Debug, Eq, PartialEq)]
@@ -226,7 +289,7 @@ pub enum ExpressionStoreDiagnostics {
 }
 
 impl ExpressionStoreBuilder {
-    pub fn finish(self) -> ExpressionStore {
+    pub fn finish(self) -> (ExpressionStore, ExpressionStoreSourceMap) {
         let Self {
             block_scopes,
             mut exprs,
@@ -237,6 +300,23 @@ impl ExpressionStoreBuilder {
             mut ident_hygiene,
             mut types,
             mut lifetimes,
+
+            mut expr_map,
+            mut expr_map_back,
+            mut pat_map,
+            mut pat_map_back,
+            mut label_map,
+            mut label_map_back,
+            mut types_map_back,
+            mut types_map,
+            mut lifetime_map_back,
+            mut lifetime_map,
+            mut binding_definitions,
+            mut field_map_back,
+            mut pat_field_map_back,
+            mut template_map,
+            mut expansions,
+            diagnostics,
         } = self;
         exprs.shrink_to_fit();
         labels.shrink_to_fit();
@@ -247,24 +327,90 @@ impl ExpressionStoreBuilder {
         types.shrink_to_fit();
         lifetimes.shrink_to_fit();
 
-        ExpressionStore {
-            exprs,
-            pats,
-            bindings,
-            labels,
-            binding_owners,
-            types,
-            lifetimes,
-            block_scopes: block_scopes.into_boxed_slice(),
-            ident_hygiene,
+        expr_map.shrink_to_fit();
+        expr_map_back.shrink_to_fit();
+        pat_map.shrink_to_fit();
+        pat_map_back.shrink_to_fit();
+        label_map.shrink_to_fit();
+        label_map_back.shrink_to_fit();
+        types_map_back.shrink_to_fit();
+        types_map.shrink_to_fit();
+        lifetime_map_back.shrink_to_fit();
+        lifetime_map.shrink_to_fit();
+        binding_definitions.shrink_to_fit();
+        field_map_back.shrink_to_fit();
+        pat_field_map_back.shrink_to_fit();
+        if let Some(template_map) = &mut template_map {
+            let FormatTemplate {
+                format_args_to_captures,
+                asm_to_captures,
+                implicit_capture_to_source,
+            } = &mut **template_map;
+            format_args_to_captures.shrink_to_fit();
+            asm_to_captures.shrink_to_fit();
+            implicit_capture_to_source.shrink_to_fit();
         }
+        expansions.shrink_to_fit();
+
+        let has_exprs =
+            !exprs.is_empty() || !labels.is_empty() || !pats.is_empty() || !bindings.is_empty();
+
+        let store = {
+            let expr_only = if has_exprs {
+                Some(Box::new(ExpressionOnlyStore {
+                    exprs,
+                    pats,
+                    bindings,
+                    labels,
+                    binding_owners,
+                    block_scopes: block_scopes.into_boxed_slice(),
+                    ident_hygiene,
+                }))
+            } else {
+                None
+            };
+            ExpressionStore { expr_only, types, lifetimes }
+        };
+
+        let source_map = {
+            let expr_only = if has_exprs || !expansions.is_empty() || !diagnostics.is_empty() {
+                Some(Box::new(ExpressionOnlySourceMap {
+                    expr_map,
+                    expr_map_back,
+                    pat_map,
+                    pat_map_back,
+                    label_map,
+                    label_map_back,
+                    binding_definitions,
+                    field_map_back,
+                    pat_field_map_back,
+                    template_map,
+                    expansions,
+                    diagnostics: ThinVec::from_iter(diagnostics),
+                }))
+            } else {
+                None
+            };
+            ExpressionStoreSourceMap {
+                expr_only,
+                types_map_back,
+                types_map,
+                lifetime_map_back,
+                lifetime_map,
+            }
+        };
+
+        (store, source_map)
     }
 }
 
 impl ExpressionStore {
-    pub fn empty_singleton() -> Arc<Self> {
-        static EMPTY: LazyLock<Arc<ExpressionStore>> =
-            LazyLock::new(|| Arc::new(ExpressionStoreBuilder::default().finish()));
+    pub fn empty_singleton() -> (Arc<ExpressionStore>, Arc<ExpressionStoreSourceMap>) {
+        static EMPTY: LazyLock<(Arc<ExpressionStore>, Arc<ExpressionStoreSourceMap>)> =
+            LazyLock::new(|| {
+                let (store, source_map) = ExpressionStoreBuilder::default().finish();
+                (Arc::new(store), Arc::new(source_map))
+            });
         EMPTY.clone()
     }
 
@@ -273,7 +419,12 @@ impl ExpressionStore {
         &'a self,
         db: &'a dyn DefDatabase,
     ) -> impl Iterator<Item = (BlockId, &'a DefMap)> + 'a {
-        self.block_scopes.iter().map(move |&block| (block, block_def_map(db, block)))
+        self.expr_only
+            .as_ref()
+            .map(|it| &*it.block_scopes)
+            .unwrap_or_default()
+            .iter()
+            .map(move |&block| (block, block_def_map(db, block)))
     }
 
     pub fn walk_bindings_in_pat(&self, pat_id: PatId, mut f: impl FnMut(BindingId)) {
@@ -320,7 +471,8 @@ impl ExpressionStore {
     }
 
     pub fn is_binding_upvar(&self, binding: BindingId, relative_to: ExprId) -> bool {
-        match self.binding_owners.get(&binding) {
+        let Some(expr_only) = &self.expr_only else { return false };
+        match expr_only.binding_owners.get(&binding) {
             Some(it) => {
                 // We assign expression ids in a way that outer closures will receive
                 // a lower id
@@ -330,6 +482,11 @@ impl ExpressionStore {
         }
     }
 
+    #[inline]
+    pub fn binding_owner(&self, id: BindingId) -> Option<ExprId> {
+        self.expr_only.as_ref()?.binding_owners.get(&id).copied()
+    }
+
     /// Walks the immediate children expressions and calls `f` for each child expression.
     ///
     /// Note that this does not walk const blocks.
@@ -601,16 +758,22 @@ impl ExpressionStore {
         });
     }
 
+    #[inline]
+    #[track_caller]
+    fn assert_expr_only(&self) -> &ExpressionOnlyStore {
+        self.expr_only.as_ref().expect("should have `ExpressionStore::expr_only`")
+    }
+
     fn binding_hygiene(&self, binding: BindingId) -> HygieneId {
-        self.bindings[binding].hygiene
+        self.assert_expr_only().bindings[binding].hygiene
     }
 
     pub fn expr_path_hygiene(&self, expr: ExprId) -> HygieneId {
-        self.ident_hygiene.get(&expr.into()).copied().unwrap_or(HygieneId::ROOT)
+        self.assert_expr_only().ident_hygiene.get(&expr.into()).copied().unwrap_or(HygieneId::ROOT)
     }
 
     pub fn pat_path_hygiene(&self, pat: PatId) -> HygieneId {
-        self.ident_hygiene.get(&pat.into()).copied().unwrap_or(HygieneId::ROOT)
+        self.assert_expr_only().ident_hygiene.get(&pat.into()).copied().unwrap_or(HygieneId::ROOT)
     }
 
     pub fn expr_or_pat_path_hygiene(&self, id: ExprOrPatId) -> HygieneId {
@@ -619,43 +782,72 @@ impl ExpressionStore {
             ExprOrPatId::PatId(id) => self.pat_path_hygiene(id),
         }
     }
+
+    #[inline]
+    pub fn exprs(&self) -> impl Iterator<Item = (ExprId, &Expr)> {
+        match &self.expr_only {
+            Some(it) => it.exprs.iter(),
+            None => const { &Arena::new() }.iter(),
+        }
+    }
+
+    #[inline]
+    pub fn pats(&self) -> impl Iterator<Item = (PatId, &Pat)> {
+        match &self.expr_only {
+            Some(it) => it.pats.iter(),
+            None => const { &Arena::new() }.iter(),
+        }
+    }
+
+    #[inline]
+    pub fn bindings(&self) -> impl Iterator<Item = (BindingId, &Binding)> {
+        match &self.expr_only {
+            Some(it) => it.bindings.iter(),
+            None => const { &Arena::new() }.iter(),
+        }
+    }
 }
 
 impl Index<ExprId> for ExpressionStore {
     type Output = Expr;
 
+    #[inline]
     fn index(&self, expr: ExprId) -> &Expr {
-        &self.exprs[expr]
+        &self.assert_expr_only().exprs[expr]
     }
 }
 
 impl Index<PatId> for ExpressionStore {
     type Output = Pat;
 
+    #[inline]
     fn index(&self, pat: PatId) -> &Pat {
-        &self.pats[pat]
+        &self.assert_expr_only().pats[pat]
     }
 }
 
 impl Index<LabelId> for ExpressionStore {
     type Output = Label;
 
+    #[inline]
     fn index(&self, label: LabelId) -> &Label {
-        &self.labels[label]
+        &self.assert_expr_only().labels[label]
     }
 }
 
 impl Index<BindingId> for ExpressionStore {
     type Output = Binding;
 
+    #[inline]
     fn index(&self, b: BindingId) -> &Binding {
-        &self.bindings[b]
+        &self.assert_expr_only().bindings[b]
     }
 }
 
 impl Index<TypeRefId> for ExpressionStore {
     type Output = TypeRef;
 
+    #[inline]
     fn index(&self, b: TypeRefId) -> &TypeRef {
         &self.types[b]
     }
@@ -664,6 +856,7 @@ impl Index<TypeRefId> for ExpressionStore {
 impl Index<LifetimeRefId> for ExpressionStore {
     type Output = LifetimeRef;
 
+    #[inline]
     fn index(&self, b: LifetimeRefId) -> &LifetimeRef {
         &self.lifetimes[b]
     }
@@ -684,12 +877,6 @@ impl Index<PathId> for ExpressionStore {
 // FIXME: Change `node_` prefix to something more reasonable.
 // Perhaps `expr_syntax` and `expr_id`?
 impl ExpressionStoreSourceMap {
-    pub fn empty_singleton() -> Arc<Self> {
-        static EMPTY: LazyLock<Arc<ExpressionStoreSourceMap>> =
-            LazyLock::new(|| Arc::new(ExpressionStoreSourceMap::default()));
-        EMPTY.clone()
-    }
-
     pub fn expr_or_pat_syntax(&self, id: ExprOrPatId) -> Result<ExprOrPatSource, SyntheticSyntax> {
         match id {
             ExprOrPatId::ExprId(id) => self.expr_syntax(id),
@@ -697,30 +884,46 @@ impl ExpressionStoreSourceMap {
         }
     }
 
+    #[inline]
+    fn expr_or_synthetic(&self) -> Result<&ExpressionOnlySourceMap, SyntheticSyntax> {
+        self.expr_only.as_deref().ok_or(SyntheticSyntax)
+    }
+
+    #[inline]
+    fn expr_only(&self) -> Option<&ExpressionOnlySourceMap> {
+        self.expr_only.as_deref()
+    }
+
+    #[inline]
+    #[track_caller]
+    fn assert_expr_only(&self) -> &ExpressionOnlySourceMap {
+        self.expr_only.as_ref().expect("should have `ExpressionStoreSourceMap::expr_only`")
+    }
+
     pub fn expr_syntax(&self, expr: ExprId) -> Result<ExprOrPatSource, SyntheticSyntax> {
-        self.expr_map_back.get(expr).cloned().ok_or(SyntheticSyntax)
+        self.expr_or_synthetic()?.expr_map_back.get(expr).cloned().ok_or(SyntheticSyntax)
     }
 
     pub fn node_expr(&self, node: InFile<&ast::Expr>) -> Option<ExprOrPatId> {
         let src = node.map(AstPtr::new);
-        self.expr_map.get(&src).cloned()
+        self.expr_only()?.expr_map.get(&src).cloned()
     }
 
     pub fn node_macro_file(&self, node: InFile<&ast::MacroCall>) -> Option<MacroCallId> {
         let src = node.map(AstPtr::new);
-        self.expansions.get(&src).cloned()
+        self.expr_only()?.expansions.get(&src).cloned()
     }
 
     pub fn macro_calls(&self) -> impl Iterator<Item = (InFile<MacroCallPtr>, MacroCallId)> + '_ {
-        self.expansions.iter().map(|(&a, &b)| (a, b))
+        self.expr_only().into_iter().flat_map(|it| it.expansions.iter().map(|(&a, &b)| (a, b)))
     }
 
     pub fn pat_syntax(&self, pat: PatId) -> Result<ExprOrPatSource, SyntheticSyntax> {
-        self.pat_map_back.get(pat).cloned().ok_or(SyntheticSyntax)
+        self.expr_or_synthetic()?.pat_map_back.get(pat).cloned().ok_or(SyntheticSyntax)
     }
 
     pub fn node_pat(&self, node: InFile<&ast::Pat>) -> Option<ExprOrPatId> {
-        self.pat_map.get(&node.map(AstPtr::new)).cloned()
+        self.expr_only()?.pat_map.get(&node.map(AstPtr::new)).cloned()
     }
 
     pub fn type_syntax(&self, id: TypeRefId) -> Result<TypeSource, SyntheticSyntax> {
@@ -732,49 +935,50 @@ impl ExpressionStoreSourceMap {
     }
 
     pub fn label_syntax(&self, label: LabelId) -> LabelSource {
-        self.label_map_back[label]
+        self.assert_expr_only().label_map_back[label]
     }
 
     pub fn patterns_for_binding(&self, binding: BindingId) -> &[PatId] {
-        self.binding_definitions.get(binding).map_or(&[], Deref::deref)
+        self.assert_expr_only().binding_definitions.get(binding).map_or(&[], Deref::deref)
     }
 
     pub fn node_label(&self, node: InFile<&ast::Label>) -> Option<LabelId> {
         let src = node.map(AstPtr::new);
-        self.label_map.get(&src).cloned()
+        self.expr_only()?.label_map.get(&src).cloned()
     }
 
     pub fn field_syntax(&self, expr: ExprId) -> FieldSource {
-        self.field_map_back[&expr]
+        self.assert_expr_only().field_map_back[&expr]
     }
 
     pub fn pat_field_syntax(&self, pat: PatId) -> PatFieldSource {
-        self.pat_field_map_back[&pat]
+        self.assert_expr_only().pat_field_map_back[&pat]
     }
 
     pub fn macro_expansion_expr(&self, node: InFile<&ast::MacroExpr>) -> Option<ExprOrPatId> {
         let src = node.map(AstPtr::new).map(AstPtr::upcast::<ast::MacroExpr>).map(AstPtr::upcast);
-        self.expr_map.get(&src).copied()
+        self.expr_only()?.expr_map.get(&src).copied()
     }
 
     pub fn expansions(&self) -> impl Iterator<Item = (&InFile<MacroCallPtr>, &MacroCallId)> {
-        self.expansions.iter()
+        self.expr_only().into_iter().flat_map(|it| it.expansions.iter())
     }
 
     pub fn expansion(&self, node: InFile<&ast::MacroCall>) -> Option<MacroCallId> {
-        self.expansions.get(&node.map(AstPtr::new)).copied()
+        self.expr_only()?.expansions.get(&node.map(AstPtr::new)).copied()
     }
 
     pub fn implicit_format_args(
         &self,
         node: InFile<&ast::FormatArgsExpr>,
     ) -> Option<(HygieneId, &[(syntax::TextRange, Name)])> {
+        let expr_only = self.expr_only()?;
         let src = node.map(AstPtr::new).map(AstPtr::upcast::<ast::Expr>);
-        let (hygiene, names) = self
+        let (hygiene, names) = expr_only
             .template_map
             .as_ref()?
             .format_args_to_captures
-            .get(&self.expr_map.get(&src)?.as_expr()?)?;
+            .get(&expr_only.expr_map.get(&src)?.as_expr()?)?;
         Some((*hygiene, &**names))
     }
 
@@ -782,67 +986,28 @@ impl ExpressionStoreSourceMap {
         &self,
         capture_expr: ExprId,
     ) -> Option<InFile<(ExprPtr, TextRange)>> {
-        self.template_map.as_ref()?.implicit_capture_to_source.get(&capture_expr).copied()
+        self.expr_only()?
+            .template_map
+            .as_ref()?
+            .implicit_capture_to_source
+            .get(&capture_expr)
+            .copied()
     }
 
     pub fn asm_template_args(
         &self,
         node: InFile<&ast::AsmExpr>,
     ) -> Option<(ExprId, &[Vec<(syntax::TextRange, usize)>])> {
+        let expr_only = self.expr_only()?;
         let src = node.map(AstPtr::new).map(AstPtr::upcast::<ast::Expr>);
-        let expr = self.expr_map.get(&src)?.as_expr()?;
-        Some(expr)
-            .zip(self.template_map.as_ref()?.asm_to_captures.get(&expr).map(std::ops::Deref::deref))
+        let expr = expr_only.expr_map.get(&src)?.as_expr()?;
+        Some(expr).zip(
+            expr_only.template_map.as_ref()?.asm_to_captures.get(&expr).map(std::ops::Deref::deref),
+        )
     }
 
     /// Get a reference to the source map's diagnostics.
     pub fn diagnostics(&self) -> &[ExpressionStoreDiagnostics] {
-        &self.diagnostics
-    }
-
-    fn shrink_to_fit(&mut self) {
-        let Self {
-            expr_map,
-            expr_map_back,
-            pat_map,
-            pat_map_back,
-            label_map,
-            label_map_back,
-            field_map_back,
-            pat_field_map_back,
-            expansions,
-            template_map,
-            diagnostics,
-            binding_definitions,
-            types_map,
-            types_map_back,
-            lifetime_map_back,
-            lifetime_map,
-        } = self;
-        if let Some(template_map) = template_map {
-            let FormatTemplate {
-                format_args_to_captures,
-                asm_to_captures,
-                implicit_capture_to_source,
-            } = &mut **template_map;
-            format_args_to_captures.shrink_to_fit();
-            asm_to_captures.shrink_to_fit();
-            implicit_capture_to_source.shrink_to_fit();
-        }
-        expr_map.shrink_to_fit();
-        expr_map_back.shrink_to_fit();
-        pat_map.shrink_to_fit();
-        pat_map_back.shrink_to_fit();
-        label_map.shrink_to_fit();
-        label_map_back.shrink_to_fit();
-        field_map_back.shrink_to_fit();
-        pat_field_map_back.shrink_to_fit();
-        expansions.shrink_to_fit();
-        diagnostics.shrink_to_fit();
-        binding_definitions.shrink_to_fit();
-        types_map.shrink_to_fit();
-        types_map_back.shrink_to_fit();
-        lifetime_map.shrink_to_fit();
-        lifetime_map_back.shrink_to_fit();
+        self.expr_only().map(|it| &*it.diagnostics).unwrap_or_default()
     }
 }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs
index fb6d931e0e4..c955393b9cf 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/body.rs
@@ -36,6 +36,7 @@ pub struct Body {
 impl ops::Deref for Body {
     type Target = ExpressionStore;
 
+    #[inline]
     fn deref(&self) -> &Self::Target {
         &self.store
     }
@@ -61,6 +62,7 @@ pub struct BodySourceMap {
 impl ops::Deref for BodySourceMap {
     type Target = ExpressionStoreSourceMap;
 
+    #[inline]
     fn deref(&self) -> &Self::Target {
         &self.store
     }
@@ -102,9 +104,7 @@ impl Body {
             }
         };
         let module = def.module(db);
-        let (body, mut source_map) =
-            lower_body(db, def, file_id, module, params, body, is_async_fn);
-        source_map.store.shrink_to_fit();
+        let (body, source_map) = lower_body(db, def, file_id, module, params, body, is_async_fn);
 
         (Arc::new(body), Arc::new(source_map))
     }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs
index c0e51b338b4..4e877748ca2 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower.rs
@@ -121,14 +121,10 @@ pub(super) fn lower_body(
             params = (0..count).map(|_| collector.missing_pat()).collect();
         };
         let body_expr = collector.missing_expr();
+        let (store, source_map) = collector.store.finish();
         return (
-            Body {
-                store: collector.store.finish(),
-                params: params.into_boxed_slice(),
-                self_param,
-                body_expr,
-            },
-            BodySourceMap { self_param: source_map_self_param, store: collector.source_map },
+            Body { store, params: params.into_boxed_slice(), self_param, body_expr },
+            BodySourceMap { self_param: source_map_self_param, store: source_map },
         );
     }
 
@@ -171,14 +167,10 @@ pub(super) fn lower_body(
         },
     );
 
+    let (store, source_map) = collector.store.finish();
     (
-        Body {
-            store: collector.store.finish(),
-            params: params.into_boxed_slice(),
-            self_param,
-            body_expr,
-        },
-        BodySourceMap { self_param: source_map_self_param, store: collector.source_map },
+        Body { store, params: params.into_boxed_slice(), self_param, body_expr },
+        BodySourceMap { self_param: source_map_self_param, store: source_map },
     )
 }
 
@@ -190,7 +182,8 @@ pub(crate) fn lower_type_ref(
     let mut expr_collector = ExprCollector::new(db, module, type_ref.file_id);
     let type_ref =
         expr_collector.lower_type_ref_opt(type_ref.value, &mut ExprCollector::impl_trait_allocator);
-    (expr_collector.store.finish(), expr_collector.source_map, type_ref)
+    let (store, source_map) = expr_collector.store.finish();
+    (store, source_map, type_ref)
 }
 
 pub(crate) fn lower_generic_params(
@@ -205,7 +198,8 @@ pub(crate) fn lower_generic_params(
     let mut collector = generics::GenericParamsCollector::new(def);
     collector.lower(&mut expr_collector, param_list, where_clause);
     let params = collector.finish();
-    (Arc::new(expr_collector.store.finish()), params, expr_collector.source_map)
+    let (store, source_map) = expr_collector.store.finish();
+    (Arc::new(store), params, source_map)
 }
 
 pub(crate) fn lower_impl(
@@ -232,7 +226,8 @@ pub(crate) fn lower_impl(
         impl_syntax.value.where_clause(),
     );
     let params = collector.finish();
-    (expr_collector.store.finish(), expr_collector.source_map, self_ty, trait_, params)
+    let (store, source_map) = expr_collector.store.finish();
+    (store, source_map, self_ty, trait_, params)
 }
 
 pub(crate) fn lower_trait(
@@ -253,7 +248,8 @@ pub(crate) fn lower_trait(
         trait_syntax.value.where_clause(),
     );
     let params = collector.finish();
-    (expr_collector.store.finish(), expr_collector.source_map, params)
+    let (store, source_map) = expr_collector.store.finish();
+    (store, source_map, params)
 }
 
 pub(crate) fn lower_trait_alias(
@@ -274,7 +270,8 @@ pub(crate) fn lower_trait_alias(
         trait_syntax.value.where_clause(),
     );
     let params = collector.finish();
-    (expr_collector.store.finish(), expr_collector.source_map, params)
+    let (store, source_map) = expr_collector.store.finish();
+    (store, source_map, params)
 }
 
 pub(crate) fn lower_type_alias(
@@ -313,7 +310,8 @@ pub(crate) fn lower_type_alias(
         .value
         .ty()
         .map(|ty| expr_collector.lower_type_ref(ty, &mut ExprCollector::impl_trait_allocator));
-    (expr_collector.store.finish(), expr_collector.source_map, params, bounds, type_ref)
+    let (store, source_map) = expr_collector.store.finish();
+    (store, source_map, params, bounds, type_ref)
 }
 
 pub(crate) fn lower_function(
@@ -421,9 +419,10 @@ pub(crate) fn lower_function(
     } else {
         return_type
     };
+    let (store, source_map) = expr_collector.store.finish();
     (
-        expr_collector.store.finish(),
-        expr_collector.source_map,
+        store,
+        source_map,
         generics,
         params.into_boxed_slice(),
         return_type,
@@ -440,7 +439,6 @@ pub struct ExprCollector<'db> {
     local_def_map: &'db LocalDefMap,
     module: ModuleId,
     pub store: ExpressionStoreBuilder,
-    pub(crate) source_map: ExpressionStoreSourceMap,
 
     // state stuff
     // Prevent nested impl traits like `impl Foo<impl Bar>`.
@@ -551,7 +549,6 @@ impl ExprCollector<'_> {
             module,
             def_map,
             local_def_map,
-            source_map: ExpressionStoreSourceMap::default(),
             store: ExpressionStoreBuilder::default(),
             expander,
             current_try_block_label: None,
@@ -698,7 +695,7 @@ impl ExprCollector<'_> {
                     let id = self.collect_macro_call(mcall, macro_ptr, true, |this, expansion| {
                         this.lower_type_ref_opt(expansion, impl_trait_lower_fn)
                     });
-                    self.source_map.types_map.insert(src, id);
+                    self.store.types_map.insert(src, id);
                     return id;
                 }
                 None => TypeRef::Error,
@@ -732,8 +729,8 @@ impl ExprCollector<'_> {
     fn alloc_type_ref(&mut self, type_ref: TypeRef, node: TypePtr) -> TypeRefId {
         let id = self.store.types.alloc(type_ref);
         let ptr = self.expander.in_file(node);
-        self.source_map.types_map_back.insert(id, ptr);
-        self.source_map.types_map.insert(ptr, id);
+        self.store.types_map_back.insert(id, ptr);
+        self.store.types_map.insert(ptr, id);
         id
     }
 
@@ -744,8 +741,8 @@ impl ExprCollector<'_> {
     ) -> LifetimeRefId {
         let id = self.store.lifetimes.alloc(lifetime_ref);
         let ptr = self.expander.in_file(node);
-        self.source_map.lifetime_map_back.insert(id, ptr);
-        self.source_map.lifetime_map.insert(ptr, id);
+        self.store.lifetime_map_back.insert(id, ptr);
+        self.store.lifetime_map.insert(ptr, id);
         id
     }
 
@@ -1190,14 +1187,14 @@ impl ExprCollector<'_> {
             }
             ast::Expr::ContinueExpr(e) => {
                 let label = self.resolve_label(e.lifetime()).unwrap_or_else(|e| {
-                    self.source_map.diagnostics.push(e);
+                    self.store.diagnostics.push(e);
                     None
                 });
                 self.alloc_expr(Expr::Continue { label }, syntax_ptr)
             }
             ast::Expr::BreakExpr(e) => {
                 let label = self.resolve_label(e.lifetime()).unwrap_or_else(|e| {
-                    self.source_map.diagnostics.push(e);
+                    self.store.diagnostics.push(e);
                     None
                 });
                 let expr = e.expr().map(|e| self.collect_expr(e));
@@ -1207,7 +1204,7 @@ impl ExprCollector<'_> {
                 let inner = self.collect_expr_opt(e.expr());
                 // make the paren expr point to the inner expression as well for IDE resolution
                 let src = self.expander.in_file(syntax_ptr);
-                self.source_map.expr_map.insert(src, inner.into());
+                self.store.expr_map.insert(src, inner.into());
                 inner
             }
             ast::Expr::ReturnExpr(e) => {
@@ -1248,7 +1245,7 @@ impl ExprCollector<'_> {
                                 None => self.missing_expr(),
                             };
                             let src = self.expander.in_file(AstPtr::new(&field));
-                            self.source_map.field_map_back.insert(expr, src);
+                            self.store.field_map_back.insert(expr, src);
                             Some(RecordLitField { name, expr })
                         })
                         .collect();
@@ -1271,12 +1268,10 @@ impl ExprCollector<'_> {
             ast::Expr::AwaitExpr(e) => {
                 let expr = self.collect_expr_opt(e.expr());
                 if let Awaitable::No(location) = self.is_lowering_awaitable_block() {
-                    self.source_map.diagnostics.push(
-                        ExpressionStoreDiagnostics::AwaitOutsideOfAsync {
-                            node: self.expander.in_file(AstPtr::new(&e)),
-                            location: location.to_string(),
-                        },
-                    );
+                    self.store.diagnostics.push(ExpressionStoreDiagnostics::AwaitOutsideOfAsync {
+                        node: self.expander.in_file(AstPtr::new(&e)),
+                        location: location.to_string(),
+                    });
                 }
                 self.alloc_expr(Expr::Await { expr }, syntax_ptr)
             }
@@ -1442,7 +1437,7 @@ impl ExprCollector<'_> {
                         // Make the macro-call point to its expanded expression so we can query
                         // semantics on syntax pointers to the macro
                         let src = self.expander.in_file(syntax_ptr);
-                        self.source_map.expr_map.insert(src, id.into());
+                        self.store.expr_map.insert(src, id.into());
                         id
                     }
                     None => self.alloc_expr(Expr::Missing, syntax_ptr),
@@ -1486,7 +1481,7 @@ impl ExprCollector<'_> {
             let expr = self.collect_expr(expr);
             // Do not use `alloc_pat_from_expr()` here, it will override the entry in `expr_map`.
             let id = self.store.pats.alloc(Pat::Expr(expr));
-            self.source_map.pat_map_back.insert(id, src);
+            self.store.pat_map_back.insert(id, src);
             id
         })
     }
@@ -1555,7 +1550,7 @@ impl ExprCollector<'_> {
                 let id = self.collect_macro_call(e, macro_ptr, true, |this, expansion| {
                     this.collect_expr_as_pat_opt(expansion)
                 });
-                self.source_map.expr_map.insert(src, id.into());
+                self.store.expr_map.insert(src, id.into());
                 id
             }
             ast::Expr::RecordExpr(e) => {
@@ -1576,7 +1571,7 @@ impl ExprCollector<'_> {
                         let pat = self.collect_expr_as_pat(field_expr);
                         let name = f.field_name()?.as_name();
                         let src = self.expander.in_file(AstPtr::new(&f).wrap_left());
-                        self.source_map.pat_field_map_back.insert(pat, src);
+                        self.store.pat_field_map_back.insert(pat, src);
                         Some(RecordFieldPat { name, pat })
                     })
                     .collect();
@@ -1622,7 +1617,7 @@ impl ExprCollector<'_> {
                         );
                         if let Either::Left(pat) = pat {
                             let src = this.expander.in_file(AstPtr::new(&expr).wrap_left());
-                            this.source_map.pat_map_back.insert(pat, src);
+                            this.store.pat_map_back.insert(pat, src);
                         }
                         pat
                     }
@@ -1968,7 +1963,7 @@ impl ExprCollector<'_> {
                     self.module.krate(),
                     resolver,
                     &mut |ptr, call| {
-                        _ = self.source_map.expansions.insert(ptr.map(|(it, _)| it), call);
+                        _ = self.store.expansions.insert(ptr.map(|(it, _)| it), call);
                     },
                 )
             }
@@ -1978,19 +1973,17 @@ impl ExprCollector<'_> {
             Ok(res) => res,
             Err(UnresolvedMacro { path }) => {
                 if record_diagnostics {
-                    self.source_map.diagnostics.push(
-                        ExpressionStoreDiagnostics::UnresolvedMacroCall {
-                            node: self.expander.in_file(syntax_ptr),
-                            path,
-                        },
-                    );
+                    self.store.diagnostics.push(ExpressionStoreDiagnostics::UnresolvedMacroCall {
+                        node: self.expander.in_file(syntax_ptr),
+                        path,
+                    });
                 }
                 return collector(self, None);
             }
         };
         if record_diagnostics {
             if let Some(err) = res.err {
-                self.source_map
+                self.store
                     .diagnostics
                     .push(ExpressionStoreDiagnostics::MacroError { node: macro_call_ptr, err });
             }
@@ -2001,7 +1994,7 @@ impl ExprCollector<'_> {
                 // Keep collecting even with expansion errors so we can provide completions and
                 // other services in incomplete macro expressions.
                 if let Some(macro_file) = self.expander.current_file_id().macro_file() {
-                    self.source_map.expansions.insert(macro_call_ptr, macro_file);
+                    self.store.expansions.insert(macro_call_ptr, macro_file);
                 }
 
                 if record_diagnostics {
@@ -2050,7 +2043,7 @@ impl ExprCollector<'_> {
             // Make the macro-call point to its expanded expression so we can query
             // semantics on syntax pointers to the macro
             let src = self.expander.in_file(syntax_ptr);
-            self.source_map.expr_map.insert(src, tail.into());
+            self.store.expr_map.insert(src, tail.into());
         })
     }
 
@@ -2361,7 +2354,7 @@ impl ExprCollector<'_> {
                         let pat = self.collect_pat(ast_pat, binding_list);
                         let name = f.field_name()?.as_name();
                         let src = self.expander.in_file(AstPtr::new(&f).wrap_right());
-                        self.source_map.pat_field_map_back.insert(pat, src);
+                        self.store.pat_field_map_back.insert(pat, src);
                         Some(RecordFieldPat { name, pat })
                     })
                     .collect();
@@ -2424,7 +2417,7 @@ impl ExprCollector<'_> {
                         self.collect_macro_call(call, macro_ptr, true, |this, expanded_pat| {
                             this.collect_pat_opt(expanded_pat, binding_list)
                         });
-                    self.source_map.pat_map.insert(src, pat.into());
+                    self.store.pat_map.insert(src, pat.into());
                     return pat;
                 }
                 None => Pat::Missing,
@@ -2515,7 +2508,7 @@ impl ExprCollector<'_> {
                             }
                         });
                     if let Some(pat) = pat.left() {
-                        self.source_map.pat_map.insert(src, pat.into());
+                        self.store.pat_map.insert(src, pat.into());
                     }
                     pat
                 }
@@ -2537,7 +2530,7 @@ impl ExprCollector<'_> {
         match enabled {
             Ok(()) => true,
             Err(cfg) => {
-                self.source_map.diagnostics.push(ExpressionStoreDiagnostics::InactiveCode {
+                self.store.diagnostics.push(ExpressionStoreDiagnostics::InactiveCode {
                     node: self.expander.in_file(SyntaxNodePtr::new(owner.syntax())),
                     cfg,
                     opts: self.cfg_options.clone(),
@@ -2548,7 +2541,7 @@ impl ExprCollector<'_> {
     }
 
     fn add_definition_to_binding(&mut self, binding_id: BindingId, pat_id: PatId) {
-        self.source_map.binding_definitions.entry(binding_id).or_default().push(pat_id);
+        self.store.binding_definitions.entry(binding_id).or_default().push(pat_id);
     }
 
     // region: labels
@@ -2724,7 +2717,7 @@ impl ExprCollector<'_> {
                     |name, range| {
                         let expr_id = self.alloc_expr_desugared(Expr::Path(Path::from(name)));
                         if let Some(range) = range {
-                            self.source_map
+                            self.store
                                 .template_map
                                 .get_or_insert_with(Default::default)
                                 .implicit_capture_to_source
@@ -2836,7 +2829,7 @@ impl ExprCollector<'_> {
             )
         };
 
-        self.source_map
+        self.store
             .template_map
             .get_or_insert_with(Default::default)
             .format_args_to_captures
@@ -3386,8 +3379,8 @@ impl ExprCollector<'_> {
     fn alloc_expr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId {
         let src = self.expander.in_file(ptr);
         let id = self.store.exprs.alloc(expr);
-        self.source_map.expr_map_back.insert(id, src.map(AstPtr::wrap_left));
-        self.source_map.expr_map.insert(src, id.into());
+        self.store.expr_map_back.insert(id, src.map(AstPtr::wrap_left));
+        self.store.expr_map.insert(src, id.into());
         id
     }
     // FIXME: desugared exprs don't have ptr, that's wrong and should be fixed.
@@ -3398,9 +3391,9 @@ impl ExprCollector<'_> {
     fn alloc_expr_desugared_with_ptr(&mut self, expr: Expr, ptr: ExprPtr) -> ExprId {
         let src = self.expander.in_file(ptr);
         let id = self.store.exprs.alloc(expr);
-        self.source_map.expr_map_back.insert(id, src.map(AstPtr::wrap_left));
+        self.store.expr_map_back.insert(id, src.map(AstPtr::wrap_left));
         // We intentionally don't fill this as it could overwrite a non-desugared entry
-        // self.source_map.expr_map.insert(src, id);
+        // self.store.expr_map.insert(src, id);
         id
     }
     fn missing_expr(&mut self) -> ExprId {
@@ -3423,24 +3416,24 @@ impl ExprCollector<'_> {
     fn alloc_pat_from_expr(&mut self, pat: Pat, ptr: ExprPtr) -> PatId {
         let src = self.expander.in_file(ptr);
         let id = self.store.pats.alloc(pat);
-        self.source_map.expr_map.insert(src, id.into());
-        self.source_map.pat_map_back.insert(id, src.map(AstPtr::wrap_left));
+        self.store.expr_map.insert(src, id.into());
+        self.store.pat_map_back.insert(id, src.map(AstPtr::wrap_left));
         id
     }
 
     fn alloc_expr_from_pat(&mut self, expr: Expr, ptr: PatPtr) -> ExprId {
         let src = self.expander.in_file(ptr);
         let id = self.store.exprs.alloc(expr);
-        self.source_map.pat_map.insert(src, id.into());
-        self.source_map.expr_map_back.insert(id, src.map(AstPtr::wrap_right));
+        self.store.pat_map.insert(src, id.into());
+        self.store.expr_map_back.insert(id, src.map(AstPtr::wrap_right));
         id
     }
 
     fn alloc_pat(&mut self, pat: Pat, ptr: PatPtr) -> PatId {
         let src = self.expander.in_file(ptr);
         let id = self.store.pats.alloc(pat);
-        self.source_map.pat_map_back.insert(id, src.map(AstPtr::wrap_right));
-        self.source_map.pat_map.insert(src, id.into());
+        self.store.pat_map_back.insert(id, src.map(AstPtr::wrap_right));
+        self.store.pat_map.insert(src, id.into());
         id
     }
     // FIXME: desugared pats don't have ptr, that's wrong and should be fixed somehow.
@@ -3454,8 +3447,8 @@ impl ExprCollector<'_> {
     fn alloc_label(&mut self, label: Label, ptr: LabelPtr) -> LabelId {
         let src = self.expander.in_file(ptr);
         let id = self.store.labels.alloc(label);
-        self.source_map.label_map_back.insert(id, src);
-        self.source_map.label_map.insert(src, id);
+        self.store.label_map_back.insert(id, src);
+        self.store.label_map.insert(src, id);
         id
     }
     // FIXME: desugared labels don't have ptr, that's wrong and should be fixed somehow.
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/asm.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/asm.rs
index d36e5205c73..3bc4afb5c8a 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/asm.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/asm.rs
@@ -10,7 +10,7 @@ use tt::TextRange;
 
 use crate::{
     expr_store::lower::{ExprCollector, FxIndexSet},
-    hir::{AsmOperand, AsmOptions, Expr, ExprId, InlineAsm, InlineAsmRegOrRegClass},
+    hir::{AsmOperand, AsmOptions, Expr, ExprId, InlineAsm, InlineAsmKind, InlineAsmRegOrRegClass},
 };
 
 impl ExprCollector<'_> {
@@ -269,11 +269,20 @@ impl ExprCollector<'_> {
                     }
                 })
         };
+
+        let kind = if asm.global_asm_token().is_some() {
+            InlineAsmKind::GlobalAsm
+        } else if asm.naked_asm_token().is_some() {
+            InlineAsmKind::NakedAsm
+        } else {
+            InlineAsmKind::Asm
+        };
+
         let idx = self.alloc_expr(
-            Expr::InlineAsm(InlineAsm { operands: operands.into_boxed_slice(), options }),
+            Expr::InlineAsm(InlineAsm { operands: operands.into_boxed_slice(), options, kind }),
             syntax_ptr,
         );
-        self.source_map
+        self.store
             .template_map
             .get_or_insert_with(Default::default)
             .asm_to_captures
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path/tests.rs
index 8fd81c7b3df..f507841a91b 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/path/tests.rs
@@ -23,7 +23,7 @@ fn lower_path(path: ast::Path) -> (TestDB, ExpressionStore, Option<Path>) {
     let mut ctx =
         ExprCollector::new(&db, crate_def_map(&db, krate).root_module_id(), file_id.into());
     let lowered_path = ctx.lower_path(path, &mut ExprCollector::impl_trait_allocator);
-    let store = ctx.store.finish();
+    let (store, _) = ctx.store.finish();
     (db, store, lowered_path)
 }
 
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs
index 87bcd33ed7b..f1b011333d9 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs
@@ -902,7 +902,7 @@ impl Printer<'_> {
                         let mut same_name = false;
                         if let Pat::Bind { id, subpat: None } = &self.store[arg.pat] {
                             if let Binding { name, mode: BindingAnnotation::Unannotated, .. } =
-                                &self.store.bindings[*id]
+                                &self.store.assert_expr_only().bindings[*id]
                             {
                                 if name.as_str() == field_name {
                                     same_name = true;
@@ -1063,7 +1063,7 @@ impl Printer<'_> {
     }
 
     fn print_binding(&mut self, id: BindingId) {
-        let Binding { name, mode, .. } = &self.store.bindings[id];
+        let Binding { name, mode, .. } = &self.store.assert_expr_only().bindings[id];
         let mode = match mode {
             BindingAnnotation::Unannotated => "",
             BindingAnnotation::Mutable => "mut ",
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 2dd0b9bdb86..1952dae9d71 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
@@ -106,7 +106,9 @@ impl ExprScopes {
         let mut scopes = ExprScopes {
             scopes: Arena::default(),
             scope_entries: Arena::default(),
-            scope_by_expr: ArenaMap::with_capacity(body.exprs.len()),
+            scope_by_expr: ArenaMap::with_capacity(
+                body.expr_only.as_ref().map_or(0, |it| it.exprs.len()),
+            ),
         };
         let mut root = scopes.root_scope();
         if let Some(self_param) = body.self_param {
@@ -179,7 +181,7 @@ impl ExprScopes {
         binding: BindingId,
         hygiene: HygieneId,
     ) {
-        let Binding { name, .. } = &store.bindings[binding];
+        let Binding { name, .. } = &store[binding];
         let entry = self.scope_entries.alloc(ScopeEntry { name: name.clone(), binding, hygiene });
         self.scopes[scope].entries =
             IdxRange::new_inclusive(self.scopes[scope].entries.start()..=entry);
@@ -251,7 +253,7 @@ fn compute_expr_scopes(
     scope: &mut ScopeId,
 ) {
     let make_label =
-        |label: &Option<LabelId>| label.map(|label| (label, store.labels[label].name.clone()));
+        |label: &Option<LabelId>| label.map(|label| (label, store[label].name.clone()));
 
     let compute_expr_scopes = |scopes: &mut ExprScopes, expr: ExprId, scope: &mut ScopeId| {
         compute_expr_scopes(expr, store, scopes, scope)
@@ -534,9 +536,8 @@ 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())
-            .unwrap();
+        let pat_src =
+            source_map.pat_syntax(source_map.patterns_for_binding(resolved.binding())[0]).unwrap();
 
         let local_name = pat_src.value.syntax_node_ptr().to_node(file.syntax());
         assert_eq!(local_name.text_range(), expected_name.syntax().text_range());
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs
index 927e280d739..c31428be28f 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs
@@ -508,9 +508,9 @@ fn f() {
 }
     "#,
     );
-    assert_eq!(body.bindings.len(), 1, "should have a binding for `B`");
+    assert_eq!(body.assert_expr_only().bindings.len(), 1, "should have a binding for `B`");
     assert_eq!(
-        body.bindings[BindingId::from_raw(RawIdx::from_u32(0))].name.as_str(),
+        body[BindingId::from_raw(RawIdx::from_u32(0))].name.as_str(),
         "B",
         "should have a binding for `B`",
     );
@@ -566,6 +566,7 @@ const fn f(x: i32) -> i32 {
     );
 
     let mtch_arms = body
+        .assert_expr_only()
         .exprs
         .iter()
         .find_map(|(_, expr)| {
@@ -578,10 +579,10 @@ const fn f(x: i32) -> i32 {
         .unwrap();
 
     let MatchArm { pat, .. } = mtch_arms[1];
-    match body.pats[pat] {
+    match body[pat] {
         Pat::Range { start, end } => {
-            let hir_start = &body.exprs[start.unwrap()];
-            let hir_end = &body.exprs[end.unwrap()];
+            let hir_start = &body[start.unwrap()];
+            let hir_end = &body[end.unwrap()];
 
             assert!(matches!(hir_start, Expr::Path { .. }));
             assert!(matches!(hir_end, Expr::Path { .. }));
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs
index 0fc7857d978..e70cd2cd6c5 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs
@@ -332,6 +332,17 @@ pub struct OffsetOf {
 pub struct InlineAsm {
     pub operands: Box<[(Option<Name>, AsmOperand)]>,
     pub options: AsmOptions,
+    pub kind: InlineAsmKind,
+}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub enum InlineAsmKind {
+    /// `asm!()`.
+    Asm,
+    /// `global_asm!()`.
+    GlobalAsm,
+    /// `naked_asm!()`.
+    NakedAsm,
 }
 
 #[derive(Clone, Copy, PartialEq, Eq, Hash)]
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs
index f3273667158..5ab61c89394 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/lower.rs
@@ -143,6 +143,8 @@ impl<'a> Ctx<'a> {
             ast::Item::MacroRules(ast) => self.lower_macro_rules(ast)?.into(),
             ast::Item::MacroDef(ast) => self.lower_macro_def(ast)?.into(),
             ast::Item::ExternBlock(ast) => self.lower_extern_block(ast).into(),
+            // FIXME: Handle `global_asm!()`.
+            ast::Item::AsmExpr(_) => return None,
         };
         let attrs = RawAttrs::new(self.db, item, self.span_map());
         self.add_attrs(mod_item.ast_id(), attrs);
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs
index 5923b3ea491..91b42bef8f7 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/item_tree/tests.rs
@@ -35,10 +35,10 @@ use a::{c, d::{e}};
             #![no_std]
             #![doc = " another file comment"]
 
-            // AstId: ExternCrate[5A82, 0]
+            // AstId: ExternCrate[070B, 0]
             pub(self) extern crate self as renamed;
 
-            // AstId: ExternCrate[7E1C, 0]
+            // AstId: ExternCrate[1EA5, 0]
             pub(in super) extern crate bli;
 
             // AstId: Use[0000, 0]
@@ -78,15 +78,15 @@ extern "C" {
             // AstId: ExternBlock[0000, 0]
             extern {
                 #[on_extern_type]
-                // AstId: TypeAlias[9FDF, 0]
+                // AstId: TypeAlias[A09C, 0]
                 pub(self) type ExType;
 
                 #[on_extern_static]
-                // AstId: Static[43C1, 0]
+                // AstId: Static[D85E, 0]
                 pub(self) static EX_STATIC = _;
 
                 #[on_extern_fn]
-                // AstId: Fn[452D, 0]
+                // AstId: Fn[B240, 0]
                 pub(self) fn ex_fn;
             }
         "#]],
@@ -124,20 +124,20 @@ enum E {
 }
         "#,
         expect![[r#"
-            // AstId: Struct[DFF3, 0]
+            // AstId: Struct[ED35, 0]
             pub(self) struct Unit;
 
             #[derive(Debug)]
-            // AstId: Struct[C7A1, 0]
+            // AstId: Struct[A47C, 0]
             pub(self) struct Struct { ... }
 
-            // AstId: Struct[DAC2, 0]
+            // AstId: Struct[C8C9, 0]
             pub(self) struct Tuple(...);
 
-            // AstId: Union[2DBB, 0]
+            // AstId: Union[2797, 0]
             pub(self) union Ize { ... }
 
-            // AstId: Enum[7FF8, 0]
+            // AstId: Enum[7D23, 0]
             pub(self) enum E { ... }
         "#]],
     );
@@ -162,18 +162,18 @@ trait Tr: SuperTrait + 'lifetime {
 }
         "#,
         expect![[r#"
-            // AstId: Static[B393, 0]
+            // AstId: Static[F7C1, 0]
             pub static ST = _;
 
-            // AstId: Const[B309, 0]
+            // AstId: Const[84BB, 0]
             pub(self) const _ = _;
 
             #[attr]
             #[inner_attr_in_fn]
-            // AstId: Fn[75E3, 0]
+            // AstId: Fn[BE8F, 0]
             pub(self) fn f;
 
-            // AstId: Trait[2998, 0]
+            // AstId: Trait[9320, 0]
             pub(self) trait Tr { ... }
         "#]],
     );
@@ -197,16 +197,16 @@ mod outline;
         expect![[r##"
             #[doc = " outer"]
             #[doc = " inner"]
-            // AstId: Module[CF93, 0]
+            // AstId: Module[03AE, 0]
             pub(self) mod inline {
                 // AstId: Use[0000, 0]
                 pub(self) use super::*;
 
-                // AstId: Fn[1B26, 0]
+                // AstId: Fn[2A78, 0]
                 pub(self) fn fn_in_module;
             }
 
-            // AstId: Module[8994, 0]
+            // AstId: Module[C08B, 0]
             pub(self) mod outline;
         "##]],
     );
@@ -225,13 +225,13 @@ pub macro m2() {}
 m!();
         "#,
         expect![[r#"
-            // AstId: MacroRules[88CE, 0]
+            // AstId: MacroRules[7E68, 0]
             macro_rules! m { ... }
 
-            // AstId: MacroDef[DC34, 0]
+            // AstId: MacroDef[1C1E, 0]
             pub macro m2 { ... }
 
-            // AstId: MacroCall[612F, 0], SyntaxContextId: ROOT2024, ExpandTo: Items
+            // AstId: MacroCall[7E68, 0], SyntaxContextId: ROOT2024, ExpandTo: Items
             m!(...);
         "#]],
     );
@@ -244,7 +244,7 @@ fn pub_self() {
 pub(self) struct S;
         "#,
         expect![[r#"
-            // AstId: Struct[42E2, 0]
+            // AstId: Struct[5024, 0]
             pub(self) struct S;
         "#]],
     )
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
index 293868df613..1c3af47d522 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/builtin_fn_macro.rs
@@ -28,6 +28,19 @@ fn test_asm_expand() {
         r#"
 #[rustc_builtin_macro]
 macro_rules! asm {() => {}}
+#[rustc_builtin_macro]
+macro_rules! global_asm {() => {}}
+#[rustc_builtin_macro]
+macro_rules! naked_asm {() => {}}
+
+global_asm! {
+    ""
+}
+
+#[unsafe(naked)]
+extern "C" fn foo() {
+    naked_asm!("");
+}
 
 fn main() {
     let i: u64 = 3;
@@ -45,6 +58,17 @@ fn main() {
         expect![[r##"
 #[rustc_builtin_macro]
 macro_rules! asm {() => {}}
+#[rustc_builtin_macro]
+macro_rules! global_asm {() => {}}
+#[rustc_builtin_macro]
+macro_rules! naked_asm {() => {}}
+
+builtin #global_asm ("")
+
+#[unsafe(naked)]
+extern "C" fn foo() {
+    builtin #naked_asm ("");
+}
 
 fn main() {
     let i: u64 = 3;
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs
index c6d901ec93b..c489c1f7c1d 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs
@@ -35,9 +35,9 @@ macro_rules! f {
     };
 }
 
-struct#0:MacroRules[8C8E, 0]@58..64#14336# MyTraitMap2#0:MacroCall[D499, 0]@31..42#ROOT2024# {#0:MacroRules[8C8E, 0]@72..73#14336#
-    map#0:MacroRules[8C8E, 0]@86..89#14336#:#0:MacroRules[8C8E, 0]@89..90#14336# #0:MacroRules[8C8E, 0]@89..90#14336#::#0:MacroRules[8C8E, 0]@91..93#14336#std#0:MacroRules[8C8E, 0]@93..96#14336#::#0:MacroRules[8C8E, 0]@96..98#14336#collections#0:MacroRules[8C8E, 0]@98..109#14336#::#0:MacroRules[8C8E, 0]@109..111#14336#HashSet#0:MacroRules[8C8E, 0]@111..118#14336#<#0:MacroRules[8C8E, 0]@118..119#14336#(#0:MacroRules[8C8E, 0]@119..120#14336#)#0:MacroRules[8C8E, 0]@120..121#14336#>#0:MacroRules[8C8E, 0]@121..122#14336#,#0:MacroRules[8C8E, 0]@122..123#14336#
-}#0:MacroRules[8C8E, 0]@132..133#14336#
+struct#0:MacroRules[BE8F, 0]@58..64#14336# MyTraitMap2#0:MacroCall[BE8F, 0]@31..42#ROOT2024# {#0:MacroRules[BE8F, 0]@72..73#14336#
+    map#0:MacroRules[BE8F, 0]@86..89#14336#:#0:MacroRules[BE8F, 0]@89..90#14336# #0:MacroRules[BE8F, 0]@89..90#14336#::#0:MacroRules[BE8F, 0]@91..93#14336#std#0:MacroRules[BE8F, 0]@93..96#14336#::#0:MacroRules[BE8F, 0]@96..98#14336#collections#0:MacroRules[BE8F, 0]@98..109#14336#::#0:MacroRules[BE8F, 0]@109..111#14336#HashSet#0:MacroRules[BE8F, 0]@111..118#14336#<#0:MacroRules[BE8F, 0]@118..119#14336#(#0:MacroRules[BE8F, 0]@119..120#14336#)#0:MacroRules[BE8F, 0]@120..121#14336#>#0:MacroRules[BE8F, 0]@121..122#14336#,#0:MacroRules[BE8F, 0]@122..123#14336#
+}#0:MacroRules[BE8F, 0]@132..133#14336#
 "#]],
     );
 }
@@ -75,12 +75,12 @@ macro_rules! f {
     };
 }
 
-fn#0:MacroCall[D499, 0]@30..32#ROOT2024# main#0:MacroCall[D499, 0]@33..37#ROOT2024#(#0:MacroCall[D499, 0]@37..38#ROOT2024#)#0:MacroCall[D499, 0]@38..39#ROOT2024# {#0:MacroCall[D499, 0]@40..41#ROOT2024#
-    1#0:MacroCall[D499, 0]@50..51#ROOT2024#;#0:MacroCall[D499, 0]@51..52#ROOT2024#
-    1.0#0:MacroCall[D499, 0]@61..64#ROOT2024#;#0:MacroCall[D499, 0]@64..65#ROOT2024#
-    (#0:MacroCall[D499, 0]@74..75#ROOT2024#(#0:MacroCall[D499, 0]@75..76#ROOT2024#1#0:MacroCall[D499, 0]@76..77#ROOT2024#,#0:MacroCall[D499, 0]@77..78#ROOT2024# )#0:MacroCall[D499, 0]@78..79#ROOT2024#,#0:MacroCall[D499, 0]@79..80#ROOT2024# )#0:MacroCall[D499, 0]@80..81#ROOT2024#.#0:MacroCall[D499, 0]@81..82#ROOT2024#0#0:MacroCall[D499, 0]@82..85#ROOT2024#.#0:MacroCall[D499, 0]@82..85#ROOT2024#0#0:MacroCall[D499, 0]@82..85#ROOT2024#;#0:MacroCall[D499, 0]@85..86#ROOT2024#
-    let#0:MacroCall[D499, 0]@95..98#ROOT2024# x#0:MacroCall[D499, 0]@99..100#ROOT2024# =#0:MacroCall[D499, 0]@101..102#ROOT2024# 1#0:MacroCall[D499, 0]@103..104#ROOT2024#;#0:MacroCall[D499, 0]@104..105#ROOT2024#
-}#0:MacroCall[D499, 0]@110..111#ROOT2024#
+fn#0:MacroCall[BE8F, 0]@30..32#ROOT2024# main#0:MacroCall[BE8F, 0]@33..37#ROOT2024#(#0:MacroCall[BE8F, 0]@37..38#ROOT2024#)#0:MacroCall[BE8F, 0]@38..39#ROOT2024# {#0:MacroCall[BE8F, 0]@40..41#ROOT2024#
+    1#0:MacroCall[BE8F, 0]@50..51#ROOT2024#;#0:MacroCall[BE8F, 0]@51..52#ROOT2024#
+    1.0#0:MacroCall[BE8F, 0]@61..64#ROOT2024#;#0:MacroCall[BE8F, 0]@64..65#ROOT2024#
+    (#0:MacroCall[BE8F, 0]@74..75#ROOT2024#(#0:MacroCall[BE8F, 0]@75..76#ROOT2024#1#0:MacroCall[BE8F, 0]@76..77#ROOT2024#,#0:MacroCall[BE8F, 0]@77..78#ROOT2024# )#0:MacroCall[BE8F, 0]@78..79#ROOT2024#,#0:MacroCall[BE8F, 0]@79..80#ROOT2024# )#0:MacroCall[BE8F, 0]@80..81#ROOT2024#.#0:MacroCall[BE8F, 0]@81..82#ROOT2024#0#0:MacroCall[BE8F, 0]@82..85#ROOT2024#.#0:MacroCall[BE8F, 0]@82..85#ROOT2024#0#0:MacroCall[BE8F, 0]@82..85#ROOT2024#;#0:MacroCall[BE8F, 0]@85..86#ROOT2024#
+    let#0:MacroCall[BE8F, 0]@95..98#ROOT2024# x#0:MacroCall[BE8F, 0]@99..100#ROOT2024# =#0:MacroCall[BE8F, 0]@101..102#ROOT2024# 1#0:MacroCall[BE8F, 0]@103..104#ROOT2024#;#0:MacroCall[BE8F, 0]@104..105#ROOT2024#
+}#0:MacroCall[BE8F, 0]@110..111#ROOT2024#
 
 
 "#]],
@@ -171,7 +171,7 @@ fn main(foo: ()) {
     }
 
     fn main(foo: ()) {
-        /* error: unresolved macro unresolved */"helloworld!"#0:Fn[B9C7, 0]@236..321#ROOT2024#;
+        /* error: unresolved macro unresolved */"helloworld!"#0:Fn[15AE, 0]@236..321#ROOT2024#;
     }
 }
 
@@ -197,7 +197,7 @@ macro_rules! mk_struct {
 #[macro_use]
 mod foo;
 
-struct#1:MacroRules[E572, 0]@59..65#14336# Foo#0:MacroCall[BDD3, 0]@32..35#ROOT2024#(#1:MacroRules[E572, 0]@70..71#14336#u32#0:MacroCall[BDD3, 0]@41..44#ROOT2024#)#1:MacroRules[E572, 0]@74..75#14336#;#1:MacroRules[E572, 0]@75..76#14336#
+struct#1:MacroRules[DB0C, 0]@59..65#14336# Foo#0:MacroCall[DB0C, 0]@32..35#ROOT2024#(#1:MacroRules[DB0C, 0]@70..71#14336#u32#0:MacroCall[DB0C, 0]@41..44#ROOT2024#)#1:MacroRules[DB0C, 0]@74..75#14336#;#1:MacroRules[DB0C, 0]@75..76#14336#
 "#]],
     );
 }
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs
index 1c69b37f164..5e95b061399 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mod.rs
@@ -20,13 +20,14 @@ use base_db::RootQueryDb;
 use expect_test::Expect;
 use hir_expand::{
     AstId, InFile, MacroCallId, MacroCallKind, MacroKind,
+    builtin::quote::quote,
     db::ExpandDatabase,
     proc_macro::{ProcMacro, ProcMacroExpander, ProcMacroExpansionError, ProcMacroKind},
     span_map::SpanMapRef,
 };
-use intern::Symbol;
+use intern::{Symbol, sym};
 use itertools::Itertools;
-use span::{Edition, Span};
+use span::{Edition, ROOT_ERASED_FILE_AST_ID, Span, SpanAnchor, SyntaxContext};
 use stdx::{format_to, format_to_acc};
 use syntax::{
     AstNode, AstPtr,
@@ -34,7 +35,9 @@ use syntax::{
     SyntaxNode, T,
     ast::{self, edit::IndentLevel},
 };
+use syntax_bridge::token_tree_to_syntax_node;
 use test_fixture::WithFixture;
+use tt::{TextRange, TextSize};
 
 use crate::{
     AdtId, Lookup, ModuleDefId,
@@ -386,3 +389,38 @@ impl ProcMacroExpander for IdentityWhenValidProcMacroExpander {
         other.type_id() == TypeId::of::<Self>()
     }
 }
+
+#[test]
+fn regression_20171() {
+    // This really isn't the appropriate place to put this test, but it's convenient with access to `quote!`.
+    let span = Span {
+        range: TextRange::empty(TextSize::new(0)),
+        anchor: SpanAnchor {
+            file_id: span::EditionedFileId::current_edition(span::FileId::from_raw(0)),
+            ast_id: ROOT_ERASED_FILE_AST_ID,
+        },
+        ctx: SyntaxContext::root(Edition::CURRENT),
+    };
+    let close_brace = tt::Punct { char: '}', spacing: tt::Spacing::Alone, span };
+    let dotdot1 = tt::Punct { char: '.', spacing: tt::Spacing::Joint, span };
+    let dotdot2 = tt::Punct { char: '.', spacing: tt::Spacing::Alone, span };
+    let dollar_crate = sym::dollar_crate;
+    let tt = quote! {
+            span => {
+        if !((matches!(
+            drive_parser(&mut parser, data, false),
+            Err(TarParserError::CorruptField {
+                field: CorruptFieldContext::PaxKvLength,
+                error: GeneralParseError::ParseInt(ParseIntError { #dotdot1 #dotdot2 })
+            })
+        #close_brace  ))) {
+        #dollar_crate::panic::panic_2021!();
+    }}
+        };
+    token_tree_to_syntax_node(
+        &tt,
+        syntax_bridge::TopEntryPoint::MacroStmts,
+        &mut |_| Edition::CURRENT,
+        Edition::CURRENT,
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs
index d5ae6f8d885..6952a9da101 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/proc_macros.rs
@@ -181,9 +181,9 @@ fn foo(&self) {
     self.0. 1;
 }
 
-fn#0:Fn[4D85, 0]@45..47#ROOT2024# foo#0:Fn[4D85, 0]@48..51#ROOT2024#(#0:Fn[4D85, 0]@51..52#ROOT2024#&#0:Fn[4D85, 0]@52..53#ROOT2024#self#0:Fn[4D85, 0]@53..57#ROOT2024# )#0:Fn[4D85, 0]@57..58#ROOT2024# {#0:Fn[4D85, 0]@59..60#ROOT2024#
-    self#0:Fn[4D85, 0]@65..69#ROOT2024# .#0:Fn[4D85, 0]@69..70#ROOT2024#0#0:Fn[4D85, 0]@70..71#ROOT2024#.#0:Fn[4D85, 0]@71..72#ROOT2024#1#0:Fn[4D85, 0]@73..74#ROOT2024#;#0:Fn[4D85, 0]@74..75#ROOT2024#
-}#0:Fn[4D85, 0]@76..77#ROOT2024#"#]],
+fn#0:Fn[8A31, 0]@45..47#ROOT2024# foo#0:Fn[8A31, 0]@48..51#ROOT2024#(#0:Fn[8A31, 0]@51..52#ROOT2024#&#0:Fn[8A31, 0]@52..53#ROOT2024#self#0:Fn[8A31, 0]@53..57#ROOT2024# )#0:Fn[8A31, 0]@57..58#ROOT2024# {#0:Fn[8A31, 0]@59..60#ROOT2024#
+    self#0:Fn[8A31, 0]@65..69#ROOT2024# .#0:Fn[8A31, 0]@69..70#ROOT2024#0#0:Fn[8A31, 0]@70..71#ROOT2024#.#0:Fn[8A31, 0]@71..72#ROOT2024#1#0:Fn[8A31, 0]@73..74#ROOT2024#;#0:Fn[8A31, 0]@74..75#ROOT2024#
+}#0:Fn[8A31, 0]@76..77#ROOT2024#"#]],
     );
 }
 
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
index 0837308d5b6..5030585147d 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs
@@ -373,19 +373,14 @@ pub fn crate_def_map(db: &dyn DefDatabase, crate_id: Crate) -> &DefMap {
     crate_local_def_map(db, crate_id).def_map(db)
 }
 
-#[allow(unused_lifetimes)]
-mod __ {
-    use super::*;
-    #[salsa_macros::tracked]
-    pub(crate) struct DefMapPair<'db> {
-        #[tracked]
-        #[returns(ref)]
-        pub(crate) def_map: DefMap,
-        #[returns(ref)]
-        pub(crate) local: LocalDefMap,
-    }
+#[salsa_macros::tracked]
+pub(crate) struct DefMapPair<'db> {
+    #[tracked]
+    #[returns(ref)]
+    pub(crate) def_map: DefMap,
+    #[returns(ref)]
+    pub(crate) local: LocalDefMap,
 }
-pub(crate) use __::DefMapPair;
 
 #[salsa_macros::tracked(returns(ref))]
 pub(crate) fn crate_local_def_map(db: &dyn DefDatabase, crate_id: Crate) -> DefMapPair<'_> {
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
index 6f321980af4..316ad5dae69 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/resolver.rs
@@ -1052,17 +1052,6 @@ impl<'db> Scope<'db> {
     }
 }
 
-pub fn resolver_for_expr(
-    db: &dyn DefDatabase,
-    owner: DefWithBodyId,
-    expr_id: ExprId,
-) -> Resolver<'_> {
-    let r = owner.resolver(db);
-    let scopes = db.expr_scopes(owner);
-    let scope_id = scopes.scope_for(expr_id);
-    resolver_for_scope_(db, scopes, scope_id, r, owner)
-}
-
 pub fn resolver_for_scope(
     db: &dyn DefDatabase,
     owner: DefWithBodyId,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs
index 1958eb6c6a1..92e610b36ac 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs
@@ -779,14 +779,10 @@ impl VariantFields {
                 Arc::new(VariantFields { fields, store: Arc::new(store), shape }),
                 Arc::new(source_map),
             ),
-            None => (
-                Arc::new(VariantFields {
-                    fields: Arena::default(),
-                    store: ExpressionStore::empty_singleton(),
-                    shape,
-                }),
-                ExpressionStoreSourceMap::empty_singleton(),
-            ),
+            None => {
+                let (store, source_map) = ExpressionStore::empty_singleton();
+                (Arc::new(VariantFields { fields: Arena::default(), store, shape }), source_map)
+            }
         }
     }
 
@@ -878,7 +874,7 @@ fn lower_fields<Field: ast::HasAttrs + ast::HasVisibility>(
                 idx += 1;
             }
             Err(cfg) => {
-                col.source_map.diagnostics.push(
+                col.store.diagnostics.push(
                     crate::expr_store::ExpressionStoreDiagnostics::InactiveCode {
                         node: InFile::new(fields.file_id, SyntaxNodePtr::new(field.syntax())),
                         cfg,
@@ -891,9 +887,9 @@ fn lower_fields<Field: ast::HasAttrs + ast::HasVisibility>(
     if !has_fields {
         return None;
     }
-    let store = col.store.finish();
+    let (store, source_map) = col.store.finish();
     arena.shrink_to_fit();
-    Some((arena, store, col.source_map))
+    Some((arena, store, source_map))
 }
 
 #[derive(Debug, PartialEq, Eq)]
@@ -980,7 +976,7 @@ impl EnumVariants {
             if !matches!(variant.shape, FieldsShape::Unit) {
                 let body = db.body(v.into());
                 // A variant with explicit discriminant
-                if body.exprs[body.body_expr] != crate::hir::Expr::Missing {
+                if !matches!(body[body.body_expr], crate::hir::Expr::Missing) {
                     return false;
                 }
             }
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 800b40a9e7e..60fbc660652 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
@@ -125,8 +125,8 @@ register_builtin! {
     (assert, Assert) => assert_expand,
     (stringify, Stringify) => stringify_expand,
     (asm, Asm) => asm_expand,
-    (global_asm, GlobalAsm) => asm_expand,
-    (naked_asm, NakedAsm) => asm_expand,
+    (global_asm, GlobalAsm) => global_asm_expand,
+    (naked_asm, NakedAsm) => naked_asm_expand,
     (cfg, Cfg) => cfg_expand,
     (core_panic, CorePanic) => panic_expand,
     (std_panic, StdPanic) => panic_expand,
@@ -325,6 +325,36 @@ fn asm_expand(
     ExpandResult::ok(expanded)
 }
 
+fn global_asm_expand(
+    _db: &dyn ExpandDatabase,
+    _id: MacroCallId,
+    tt: &tt::TopSubtree,
+    span: Span,
+) -> ExpandResult<tt::TopSubtree> {
+    let mut tt = tt.clone();
+    tt.top_subtree_delimiter_mut().kind = tt::DelimiterKind::Parenthesis;
+    let pound = mk_pound(span);
+    let expanded = quote! {span =>
+        builtin #pound global_asm #tt
+    };
+    ExpandResult::ok(expanded)
+}
+
+fn naked_asm_expand(
+    _db: &dyn ExpandDatabase,
+    _id: MacroCallId,
+    tt: &tt::TopSubtree,
+    span: Span,
+) -> ExpandResult<tt::TopSubtree> {
+    let mut tt = tt.clone();
+    tt.top_subtree_delimiter_mut().kind = tt::DelimiterKind::Parenthesis;
+    let pound = mk_pound(span);
+    let expanded = quote! {span =>
+        builtin #pound naked_asm #tt
+    };
+    ExpandResult::ok(expanded)
+}
+
 fn cfg_expand(
     db: &dyn ExpandDatabase,
     id: MacroCallId,
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs
index d5874f829ba..70c38d4d7c7 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/quote.rs
@@ -129,7 +129,7 @@ macro_rules! quote {
         }
     }
 }
-pub(super) use quote;
+pub use quote;
 
 pub trait ToTokenTree {
     fn to_tokens(self, span: Span, builder: &mut TopSubtreeBuilder);
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 679f61112ad..217d991d110 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/name.rs
@@ -179,10 +179,9 @@ impl Name {
         self.symbol.as_str()
     }
 
-    #[inline]
     pub fn display<'a>(
         &'a self,
-        db: &dyn salsa::Database,
+        db: &dyn crate::db::ExpandDatabase,
         edition: Edition,
     ) -> impl fmt::Display + 'a {
         _ = db;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
index 24530a5f67c..14b9cd203f6 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/consteval.rs
@@ -281,7 +281,7 @@ pub(crate) fn const_eval_discriminant_variant(
     let def = variant_id.into();
     let body = db.body(def);
     let loc = variant_id.lookup(db);
-    if body.exprs[body.body_expr] == Expr::Missing {
+    if matches!(body[body.body_expr], Expr::Missing) {
         let prev_idx = loc.index.checked_sub(1);
         let value = match prev_idx {
             Some(prev_idx) => {
@@ -334,7 +334,7 @@ pub(crate) fn eval_to_const(
         // Type checking clousres need an isolated body (See the above FIXME). Bail out early to prevent panic.
         return unknown_const(infer[expr].clone());
     }
-    if let Expr::Path(p) = &ctx.body.exprs[expr] {
+    if let Expr::Path(p) = &ctx.body[expr] {
         let resolver = &ctx.resolver;
         if let Some(c) =
             path_to_const(db, resolver, p, mode, || ctx.generics(), debruijn, infer[expr].clone())
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 5d3be07f3db..b3d46845c44 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
@@ -273,8 +273,9 @@ 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_fn = crate::variance::variances_of_cycle_fn,
+        // cycle_initial = crate::variance::variances_of_cycle_initial,
+        cycle_result = crate::variance::variances_of_cycle_initial,
     )]
     fn variances_of(&self, def: GenericDefId) -> Option<Arc<[crate::variance::Variance]>>;
 
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs
index 9c0f8f40080..40fe3073cf2 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs
@@ -226,11 +226,10 @@ impl<'a> DeclValidator<'a> {
         let body = self.db.body(func.into());
         let edition = self.edition(func);
         let mut pats_replacements = body
-            .pats
-            .iter()
+            .pats()
             .filter_map(|(pat_id, pat)| match pat {
                 Pat::Bind { id, .. } => {
-                    let bind_name = &body.bindings[*id].name;
+                    let bind_name = &body[*id].name;
                     let mut suggested_text = to_lower_snake_case(bind_name.as_str())?;
                     if is_raw_identifier(&suggested_text, edition) {
                         suggested_text.insert_str(0, "r#");
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs
index 5d56957be6d..5ae6bf6dffd 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs
@@ -101,7 +101,7 @@ impl ExprValidator {
             self.check_for_trailing_return(body.body_expr, &body);
         }
 
-        for (id, expr) in body.exprs.iter() {
+        for (id, expr) in body.exprs() {
             if let Some((variant, missed_fields, true)) =
                 record_literal_missing_fields(db, &self.infer, id, expr)
             {
@@ -132,7 +132,7 @@ impl ExprValidator {
             }
         }
 
-        for (id, pat) in body.pats.iter() {
+        for (id, pat) in body.pats() {
             if let Some((variant, missed_fields, true)) =
                 record_pattern_missing_fields(db, &self.infer, id, pat)
             {
@@ -389,7 +389,7 @@ impl ExprValidator {
         if !self.validate_lints {
             return;
         }
-        match &body.exprs[body_expr] {
+        match &body[body_expr] {
             Expr::Block { statements, tail, .. } => {
                 let last_stmt = tail.or_else(|| match statements.last()? {
                     Statement::Expr { expr, .. } => Some(*expr),
@@ -428,7 +428,7 @@ impl ExprValidator {
             if else_branch.is_none() {
                 return;
             }
-            if let Expr::Block { statements, tail, .. } = &self.body.exprs[*then_branch] {
+            if let Expr::Block { statements, tail, .. } = &self.body[*then_branch] {
                 let last_then_expr = tail.or_else(|| match statements.last()? {
                     Statement::Expr { expr, .. } => Some(*expr),
                     _ => None,
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 c3ab5aff3db..ca132fbdc45 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
@@ -150,7 +150,7 @@ impl<'a> PatCtxt<'a> {
             hir_def::hir::Pat::Bind { id, subpat, .. } => {
                 let bm = self.infer.binding_modes[pat];
                 ty = &self.infer[id];
-                let name = &self.body.bindings[id].name;
+                let name = &self.body[id].name;
                 match (bm, ty.kind(Interner)) {
                     (BindingMode::Ref(_), TyKind::Ref(.., rty)) => ty = rty,
                     (BindingMode::Ref(_), _) => {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
index 20cf3c78115..f6ad3c7aae2 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/unsafe_check.rs
@@ -7,7 +7,7 @@ use either::Either;
 use hir_def::{
     AdtId, DefWithBodyId, FieldId, FunctionId, VariantId,
     expr_store::{Body, path::Path},
-    hir::{AsmOperand, Expr, ExprId, ExprOrPatId, Pat, PatId, Statement, UnaryOp},
+    hir::{AsmOperand, Expr, ExprId, ExprOrPatId, InlineAsmKind, Pat, PatId, Statement, UnaryOp},
     resolver::{HasResolver, ResolveValueResult, Resolver, ValueNs},
     signatures::StaticFlags,
     type_ref::Rawness,
@@ -217,7 +217,7 @@ impl<'db> UnsafeVisitor<'db> {
     }
 
     fn walk_pat(&mut self, current: PatId) {
-        let pat = &self.body.pats[current];
+        let pat = &self.body[current];
 
         if self.inside_union_destructure {
             match pat {
@@ -264,7 +264,7 @@ impl<'db> UnsafeVisitor<'db> {
     }
 
     fn walk_expr(&mut self, current: ExprId) {
-        let expr = &self.body.exprs[current];
+        let expr = &self.body[current];
         let inside_assignment = mem::replace(&mut self.inside_assignment, false);
         match expr {
             &Expr::Call { callee, .. } => {
@@ -284,7 +284,7 @@ impl<'db> UnsafeVisitor<'db> {
                 self.resolver.reset_to_guard(guard);
             }
             Expr::Ref { expr, rawness: Rawness::RawPtr, mutability: _ } => {
-                match self.body.exprs[*expr] {
+                match self.body[*expr] {
                     // Do not report unsafe for `addr_of[_mut]!(EXTERN_OR_MUT_STATIC)`,
                     // see https://github.com/rust-lang/rust/pull/125834.
                     Expr::Path(_) => return,
@@ -315,7 +315,12 @@ impl<'db> UnsafeVisitor<'db> {
                 self.inside_assignment = old_inside_assignment;
             }
             Expr::InlineAsm(asm) => {
-                self.on_unsafe_op(current.into(), UnsafetyReason::InlineAsm);
+                if asm.kind == InlineAsmKind::Asm {
+                    // `naked_asm!()` requires `unsafe` on the attribute (`#[unsafe(naked)]`),
+                    // and `global_asm!()` doesn't require it at all.
+                    self.on_unsafe_op(current.into(), UnsafetyReason::InlineAsm);
+                }
+
                 asm.operands.iter().for_each(|(_, op)| match op {
                     AsmOperand::In { expr, .. }
                     | AsmOperand::Out { expr: Some(expr), .. }
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 810fe76f231..b3760e3a382 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/display.rs
@@ -795,6 +795,14 @@ fn render_const_scalar(
                 let Some(bytes) = memory_map.get(addr, size_one * count) else {
                     return f.write_str("<ref-data-not-available>");
                 };
+                let expected_len = count * size_one;
+                if bytes.len() < expected_len {
+                    never!(
+                        "Memory map size is too small. Expected {expected_len}, got {}",
+                        bytes.len(),
+                    );
+                    return f.write_str("<layout-error>");
+                }
                 f.write_str("&[")?;
                 let mut first = true;
                 for i in 0..count {
@@ -2328,6 +2336,7 @@ impl HirDisplayWithExpressionStore for TypeBound {
                 store[*path].hir_fmt(f, store)
             }
             TypeBound::Use(args) => {
+                write!(f, "use<")?;
                 let edition = f.edition();
                 let last = args.len().saturating_sub(1);
                 for (idx, arg) in args.iter().enumerate() {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs
index d2eaf212365..3f7eba9dd18 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs
@@ -273,7 +273,7 @@ impl InferenceContext<'_> {
     fn pat_bound_mutability(&self, pat: PatId) -> Mutability {
         let mut r = Mutability::Not;
         self.body.walk_bindings_in_pat(pat, |b| {
-            if self.body.bindings[b].mode == BindingAnnotation::RefMut {
+            if self.body[b].mode == BindingAnnotation::RefMut {
                 r = Mutability::Mut;
             }
         });
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
index 99d3b5c7a84..18288b718f7 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/pat.rs
@@ -459,7 +459,7 @@ impl InferenceContext<'_> {
         expected: &Ty,
         decl: Option<DeclContext>,
     ) -> Ty {
-        let Binding { mode, .. } = self.body.bindings[binding];
+        let Binding { mode, .. } = self.body[binding];
         let mode = if mode == BindingAnnotation::Unannotated {
             default_bm
         } else {
@@ -639,7 +639,7 @@ impl InferenceContext<'_> {
 pub(super) fn contains_explicit_ref_binding(body: &Body, pat_id: PatId) -> bool {
     let mut res = false;
     body.walk_pats(pat_id, &mut |pat| {
-        res |= matches!(body[pat], Pat::Bind { id, .. } if body.bindings[id].mode == BindingAnnotation::Ref);
+        res |= matches!(body[pat], Pat::Bind { id, .. } if body[id].mode == BindingAnnotation::Ref);
     });
     res
 }
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 88c33ecccad..82d0ed4f194 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, AddressSpace};
+use rustc_abi::{AddressSpace, AlignFromBytesError, TargetDataLayoutErrors};
 use triomphe::Arc;
 
 use crate::db::HirDatabase;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs
index cc7d74f4fb0..b3bc226ec93 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/layout/tests.rs
@@ -119,8 +119,7 @@ fn eval_expr(
         .unwrap();
     let hir_body = db.body(function_id.into());
     let b = hir_body
-        .bindings
-        .iter()
+        .bindings()
         .find(|x| x.1.name.display_no_db(file_id.edition(&db)).to_smolstr() == "goal")
         .unwrap()
         .0;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs
index 06686b6a164..5c06234fa07 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower/path.rs
@@ -1018,8 +1018,12 @@ fn check_generic_args_len(
     }
 
     let lifetime_args_len = def_generics.len_lifetimes_self();
-    if provided_lifetimes_count == 0 && lifetime_args_len > 0 && !lowering_assoc_type_generics {
-        // In generic associated types, we never allow inferring the lifetimes.
+    if provided_lifetimes_count == 0
+        && lifetime_args_len > 0
+        && (!lowering_assoc_type_generics || infer_args)
+    {
+        // In generic associated types, we never allow inferring the lifetimes, but only in type context, that is
+        // when `infer_args == false`. In expression/pattern context we always allow inferring them, even for GATs.
         match lifetime_elision {
             &LifetimeElisionKind::AnonymousCreateParameter { report_in_path } => {
                 ctx.report_elided_lifetimes_in_path(def, lifetime_args_len as u32, report_in_path);
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs
index bf80ed7967a..482b420279c 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir.rs
@@ -1212,10 +1212,9 @@ impl MirSpan {
         match *self {
             MirSpan::ExprId(expr) => matches!(body[expr], Expr::Ref { .. }),
             // FIXME: Figure out if this is correct wrt. match ergonomics.
-            MirSpan::BindingId(binding) => matches!(
-                body.bindings[binding].mode,
-                BindingAnnotation::Ref | BindingAnnotation::RefMut
-            ),
+            MirSpan::BindingId(binding) => {
+                matches!(body[binding].mode, BindingAnnotation::Ref | BindingAnnotation::RefMut)
+            }
             MirSpan::PatId(_) | MirSpan::SelfParam | MirSpan::Unknown => false,
         }
     }
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 55fada14363..9a97bd6dbe2 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
@@ -31,8 +31,8 @@ use syntax::{SyntaxNodePtr, TextRange};
 use triomphe::Arc;
 
 use crate::{
-    CallableDefId, ClosureId, ComplexMemoryMap, Const, ConstData, ConstScalar, FnDefId, Interner,
-    MemoryMap, Substitution, ToChalk, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind,
+    AliasTy, CallableDefId, ClosureId, ComplexMemoryMap, Const, ConstData, ConstScalar, FnDefId,
+    Interner, MemoryMap, Substitution, ToChalk, TraitEnvironment, Ty, TyBuilder, TyExt, TyKind,
     consteval::{ConstEvalError, intern_const_scalar, try_const_usize},
     db::{HirDatabase, InternedClosure},
     display::{ClosureStyle, DisplayTarget, HirDisplay},
@@ -2195,7 +2195,7 @@ impl Evaluator<'_> {
                         }
                     }
                 }
-                chalk_ir::TyKind::Array(inner, len) => {
+                TyKind::Array(inner, len) => {
                     let len = match try_const_usize(this.db, len) {
                         Some(it) => it as usize,
                         None => not_supported!("non evaluatable array len in patching addresses"),
@@ -2213,7 +2213,7 @@ impl Evaluator<'_> {
                         )?;
                     }
                 }
-                chalk_ir::TyKind::Tuple(_, subst) => {
+                TyKind::Tuple(_, subst) => {
                     let layout = this.layout(ty)?;
                     for (id, ty) in subst.iter(Interner).enumerate() {
                         let ty = ty.assert_ty_ref(Interner); // Tuple only has type argument
@@ -2229,7 +2229,7 @@ impl Evaluator<'_> {
                         )?;
                     }
                 }
-                chalk_ir::TyKind::Adt(adt, subst) => match adt.0 {
+                TyKind::Adt(adt, subst) => match adt.0 {
                     AdtId::StructId(s) => {
                         let data = s.fields(this.db);
                         let layout = this.layout(ty)?;
@@ -2280,6 +2280,10 @@ impl Evaluator<'_> {
                     }
                     AdtId::UnionId(_) => (),
                 },
+                TyKind::Alias(AliasTy::Projection(proj)) => {
+                    let ty = this.db.normalize_projection(proj.clone(), this.trait_env.clone());
+                    rec(this, bytes, &ty, locals, mm, stack_depth_limit - 1)?;
+                }
                 _ => (),
             }
             Ok(())
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
index 845d6b8eae1..07d81472729 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower.rs
@@ -321,7 +321,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
         current: BasicBlockId,
     ) -> Result<Option<(Operand, BasicBlockId)>> {
         if !self.has_adjustments(expr_id) {
-            if let Expr::Literal(l) = &self.body.exprs[expr_id] {
+            if let Expr::Literal(l) = &self.body[expr_id] {
                 let ty = self.expr_ty_without_adjust(expr_id);
                 return Ok(Some((self.lower_literal_to_operand(ty, l)?, current)));
             }
@@ -411,7 +411,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
         place: Place,
         mut current: BasicBlockId,
     ) -> Result<Option<BasicBlockId>> {
-        match &self.body.exprs[expr_id] {
+        match &self.body[expr_id] {
             Expr::OffsetOf(_) => {
                 not_supported!("builtin#offset_of")
             }
@@ -1374,7 +1374,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
     }
 
     fn lower_literal_or_const_to_operand(&mut self, ty: Ty, loc: &ExprId) -> Result<Operand> {
-        match &self.body.exprs[*loc] {
+        match &self.body[*loc] {
             Expr::Literal(l) => self.lower_literal_to_operand(ty, l),
             Expr::Path(c) => {
                 let owner = self.owner;
@@ -1850,7 +1850,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
             self.drop_scopes.last_mut().unwrap().locals.push(local_id);
             if let Pat::Bind { id, subpat: None } = self.body[it] {
                 if matches!(
-                    self.body.bindings[id].mode,
+                    self.body[id].mode,
                     BindingAnnotation::Unannotated | BindingAnnotation::Mutable
                 ) {
                     self.result.binding_locals.insert(id, local_id);
@@ -1859,7 +1859,7 @@ impl<'ctx> MirLowerCtx<'ctx> {
             local_id
         }));
         // and then rest of bindings
-        for (id, _) in self.body.bindings.iter() {
+        for (id, _) in self.body.bindings() {
             if !pick_binding(id) {
                 continue;
             }
@@ -2126,7 +2126,7 @@ pub fn mir_body_for_closure_query(
         .result
         .binding_locals
         .into_iter()
-        .filter(|it| ctx.body.binding_owners.get(&it.0).copied() == Some(expr))
+        .filter(|it| ctx.body.binding_owner(it.0) == Some(expr))
         .collect();
     if let Some(err) = err {
         return Err(MirLowerError::UnresolvedUpvar(err));
@@ -2191,7 +2191,7 @@ pub fn lower_to_mir(
     // 0 is return local
     ctx.result.locals.alloc(Local { ty: ctx.expr_ty_after_adjustments(root_expr) });
     let binding_picker = |b: BindingId| {
-        let owner = ctx.body.binding_owners.get(&b).copied();
+        let owner = ctx.body.binding_owner(b);
         if root_expr == body.body_expr { owner.is_none() } else { owner == Some(root_expr) }
     };
     // 1 to param_len is for params
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs
index e7bffead931..e074c2d558e 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/as_place.rs
@@ -133,7 +133,7 @@ impl MirLowerCtx<'_> {
             }
             this.lower_expr_to_some_place_without_adjust(expr_id, current)
         };
-        match &self.body.exprs[expr_id] {
+        match &self.body[expr_id] {
             Expr::Path(p) => {
                 let resolver_guard =
                     self.resolver.update_to_inner_scope(self.db, self.owner, expr_id);
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs
index 61c0685c48a..3325226b1d3 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/lower/pattern_matching.rs
@@ -130,7 +130,7 @@ impl MirLowerCtx<'_> {
                 .collect::<Vec<_>>()
                 .into(),
         );
-        Ok(match &self.body.pats[pattern] {
+        Ok(match &self.body[pattern] {
             Pat::Missing => return Err(MirLowerError::IncompletePattern),
             Pat::Wild => (current, current_else),
             Pat::Tuple { args, ellipsis } => {
@@ -436,7 +436,7 @@ impl MirLowerCtx<'_> {
                     (next, Some(else_target))
                 }
             },
-            Pat::Lit(l) => match &self.body.exprs[*l] {
+            Pat::Lit(l) => match &self.body[*l] {
                 Expr::Literal(l) => {
                     if mode == MatchingMode::Check {
                         let c = self.lower_literal_to_operand(self.infer[pattern].clone(), l)?;
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs
index 78a69cf4509..aad54f88438 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/pretty.rs
@@ -219,7 +219,7 @@ impl<'a> MirPrettyCtx<'a> {
 
     fn local_name(&self, local: LocalId) -> LocalName {
         match self.local_to_binding.get(local) {
-            Some(b) => LocalName::Binding(self.hir_body.bindings[*b].name.clone(), local),
+            Some(b) => LocalName::Binding(self.hir_body[*b].name.clone(), local),
             None => LocalName::Unknown(local),
         }
     }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
index 79754bc8a09..9605a0b4124 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests.rs
@@ -168,7 +168,7 @@ fn check_impl(
         let inference_result = db.infer(def);
 
         for (pat, mut ty) in inference_result.type_of_pat.iter() {
-            if let Pat::Bind { id, .. } = body.pats[pat] {
+            if let Pat::Bind { id, .. } = body[pat] {
                 ty = &inference_result.type_of_binding[id];
             }
             let node = match pat_node(&body_source_map, pat, &db) {
@@ -316,7 +316,7 @@ fn infer_with_mismatches(content: &str, include_mismatches: bool) -> String {
         }
 
         for (pat, mut ty) in inference_result.type_of_pat.iter() {
-            if let Pat::Bind { id, .. } = body.pats[pat] {
+            if let Pat::Bind { id, .. } = body[pat] {
                 ty = &inference_result.type_of_binding[id];
             }
             let node = match body_source_map.pat_syntax(pat) {
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 87d9df611bd..08a215fecf6 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: covariant, U: covariant, V: covariant]
+                FixedPoint[T: bivariant, U: bivariant, V: bivariant]
             "#]],
         );
     }
diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index 5c6f622e6c3..1b2b76999f7 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -2036,7 +2036,7 @@ impl DefWithBody {
                     )
                 }
                 let mol = &borrowck_result.mutability_of_locals;
-                for (binding_id, binding_data) in body.bindings.iter() {
+                for (binding_id, binding_data) in body.bindings() {
                     if binding_data.problems.is_some() {
                         // We should report specific diagnostics for these problems, not `need-mut` and `unused-mut`.
                         continue;
@@ -3222,7 +3222,8 @@ impl Macro {
         }
     }
 
-    pub fn is_asm_or_global_asm(&self, db: &dyn HirDatabase) -> bool {
+    /// Is this `asm!()`, or a variant of it (e.g. `global_asm!()`)?
+    pub fn is_asm_like(&self, db: &dyn HirDatabase) -> bool {
         match self.id {
             MacroId::Macro2Id(it) => {
                 matches!(it.lookup(db).expander, MacroExpander::BuiltIn(m) if m.is_asm())
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
index 247bb693983..adba59236a4 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
@@ -677,8 +677,7 @@ impl<'db> SemanticsImpl<'db> {
     pub fn rename_conflicts(&self, to_be_renamed: &Local, new_name: &Name) -> Vec<Local> {
         let body = self.db.body(to_be_renamed.parent);
         let resolver = to_be_renamed.parent.resolver(self.db);
-        let starting_expr =
-            body.binding_owners.get(&to_be_renamed.binding_id).copied().unwrap_or(body.body_expr);
+        let starting_expr = body.binding_owner(to_be_renamed.binding_id).unwrap_or(body.body_expr);
         let mut visitor = RenameConflictsVisitor {
             body: &body,
             conflicts: FxHashSet::default(),
@@ -1776,7 +1775,7 @@ impl<'db> SemanticsImpl<'db> {
 
     pub fn is_unsafe_macro_call(&self, macro_call: &ast::MacroCall) -> bool {
         let Some(mac) = self.resolve_macro_call(macro_call) else { return false };
-        if mac.is_asm_or_global_asm(self.db) {
+        if mac.is_asm_like(self.db) {
             return true;
         }
 
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 0662bfddcf8..ecc6e5f3d03 100644
--- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
@@ -242,11 +242,7 @@ impl<'db> SourceAnalyzer<'db> {
 
     fn binding_id_of_pat(&self, pat: &ast::IdentPat) -> Option<BindingId> {
         let pat_id = self.pat_id(&pat.clone().into())?;
-        if let Pat::Bind { id, .. } = self.store()?.pats[pat_id.as_pat()?] {
-            Some(id)
-        } else {
-            None
-        }
+        if let Pat::Bind { id, .. } = self.store()?[pat_id.as_pat()?] { Some(id) } else { None }
     }
 
     pub(crate) fn expr_adjustments(&self, expr: &ast::Expr) -> Option<&[Adjustment]> {
@@ -995,7 +991,7 @@ impl<'db> SourceAnalyzer<'db> {
         let parent_hir_path = path
             .parent_path()
             .and_then(|p| collector.lower_path(p, &mut ExprCollector::impl_trait_error_allocator));
-        let store = collector.store.finish();
+        let (store, _) = collector.store.finish();
 
         // Case where path is a qualifier of a use tree, e.g. foo::bar::{Baz, Qux} where we are
         // trying to resolve foo::bar.
@@ -1204,7 +1200,7 @@ impl<'db> SourceAnalyzer<'db> {
         let mut collector = ExprCollector::new(db, self.resolver.module(), self.file_id);
         let hir_path =
             collector.lower_path(path.clone(), &mut ExprCollector::impl_trait_error_allocator)?;
-        let store = collector.store.finish();
+        let (store, _) = collector.store.finish();
         Some(resolve_hir_path_(
             db,
             &self.resolver,
@@ -1439,9 +1435,11 @@ fn scope_for(
 ) -> Option<ScopeId> {
     node.ancestors_with_macros(db)
         .take_while(|it| {
-            !ast::Item::can_cast(it.kind())
-                || ast::MacroCall::can_cast(it.kind())
-                || ast::Use::can_cast(it.kind())
+            let kind = it.kind();
+            !ast::Item::can_cast(kind)
+                || ast::MacroCall::can_cast(kind)
+                || ast::Use::can_cast(kind)
+                || ast::AsmExpr::can_cast(kind)
         })
         .filter_map(|it| it.map(ast::Expr::cast).transpose())
         .filter_map(|it| source_map.node_expr(it.as_ref())?.as_expr())
diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs
index 756650891d4..dca10193e29 100644
--- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs
@@ -125,6 +125,13 @@ impl<'a> SymbolCollector<'a> {
                 }
                 ModuleDefId::AdtId(AdtId::EnumId(id)) => {
                     this.push_decl(id, name, false, None);
+                    let enum_name = this.db.enum_signature(id).name.as_str().to_smolstr();
+                    this.with_container_name(Some(enum_name), |this| {
+                        let variants = id.enum_variants(this.db);
+                        for (variant_id, variant_name, _) in &variants.variants {
+                            this.push_decl(*variant_id, variant_name, true, None);
+                        }
+                    });
                 }
                 ModuleDefId::AdtId(AdtId::UnionId(id)) => {
                     this.push_decl(id, name, false, None);
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs
index efcbcef00e9..9126e869b9a 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_match_to_let_else.rs
@@ -1,8 +1,8 @@
 use ide_db::defs::{Definition, NameRefClass};
 use syntax::{
     AstNode, SyntaxNode,
-    ast::{self, HasName, Name},
-    ted,
+    ast::{self, HasName, Name, syntax_factory::SyntaxFactory},
+    syntax_editor::SyntaxEditor,
 };
 
 use crate::{
@@ -121,34 +121,36 @@ fn find_extracted_variable(ctx: &AssistContext<'_>, arm: &ast::MatchArm) -> Opti
 
 // Rename `extracted` with `binding` in `pat`.
 fn rename_variable(pat: &ast::Pat, extracted: &[Name], binding: ast::Pat) -> SyntaxNode {
-    let syntax = pat.syntax().clone_for_update();
+    let syntax = pat.syntax().clone_subtree();
+    let mut editor = SyntaxEditor::new(syntax.clone());
+    let make = SyntaxFactory::with_mappings();
     let extracted = extracted
         .iter()
-        .map(|e| syntax.covering_element(e.syntax().text_range()))
+        .map(|e| e.syntax().text_range() - pat.syntax().text_range().start())
+        .map(|r| syntax.covering_element(r))
         .collect::<Vec<_>>();
     for extracted_syntax in extracted {
         // If `extracted` variable is a record field, we should rename it to `binding`,
         // otherwise we just need to replace `extracted` with `binding`.
-
         if let Some(record_pat_field) =
             extracted_syntax.ancestors().find_map(ast::RecordPatField::cast)
         {
             if let Some(name_ref) = record_pat_field.field_name() {
-                ted::replace(
+                editor.replace(
                     record_pat_field.syntax(),
-                    ast::make::record_pat_field(
-                        ast::make::name_ref(&name_ref.text()),
-                        binding.clone(),
+                    make.record_pat_field(
+                        make.name_ref(&name_ref.text()),
+                        binding.clone_for_update(),
                     )
-                    .syntax()
-                    .clone_for_update(),
+                    .syntax(),
                 );
             }
         } else {
-            ted::replace(extracted_syntax, binding.clone().syntax().clone_for_update());
+            editor.replace(extracted_syntax, binding.syntax().clone_for_update());
         }
     }
-    syntax
+    editor.add_mappings(make.finish_with_mappings());
+    editor.finish().new_root().clone()
 }
 
 #[cfg(test)]
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs
index 32c4ae2e869..8d27574eb2c 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_named_struct_to_tuple_struct.rs
@@ -4,7 +4,8 @@ use itertools::Itertools;
 use syntax::{
     SyntaxKind,
     ast::{self, AstNode, HasAttrs, HasGenericParams, HasVisibility},
-    match_ast, ted,
+    match_ast,
+    syntax_editor::{Position, SyntaxEditor},
 };
 
 use crate::{AssistContext, AssistId, Assists, assist_context::SourceChangeBuilder};
@@ -97,11 +98,14 @@ fn edit_struct_def(
     // Note that we don't need to consider macro files in this function because this is
     // currently not triggered for struct definitions inside macro calls.
     let tuple_fields = record_fields.fields().filter_map(|f| {
-        let field = ast::make::tuple_field(f.visibility(), f.ty()?).clone_for_update();
-        ted::insert_all(
-            ted::Position::first_child_of(field.syntax()),
+        let field = ast::make::tuple_field(f.visibility(), f.ty()?);
+        let mut editor = SyntaxEditor::new(field.syntax().clone());
+        editor.insert_all(
+            Position::first_child_of(field.syntax()),
             f.attrs().map(|attr| attr.syntax().clone_subtree().clone_for_update().into()).collect(),
         );
+        let field_syntax = editor.finish().new_root().clone();
+        let field = ast::TupleField::cast(field_syntax)?;
         Some(field)
     });
     let tuple_fields = ast::make::tuple_field_list(tuple_fields);
@@ -1086,8 +1090,7 @@ pub struct $0Foo {
 }
 "#,
             r#"
-pub struct Foo(#[my_custom_attr]
-u32);
+pub struct Foo(#[my_custom_attr]u32);
 "#,
         );
     }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_new.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_new.rs
index 79a78ab3698..47233fb399d 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_new.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_default_from_new.rs
@@ -2,7 +2,7 @@ use ide_db::famous_defs::FamousDefs;
 use stdx::format_to;
 use syntax::{
     AstNode,
-    ast::{self, HasGenericParams, HasName, Impl, make},
+    ast::{self, HasGenericParams, HasName, HasTypeBounds, Impl, make},
 };
 
 use crate::{
@@ -88,20 +88,19 @@ fn generate_trait_impl_text_from_impl(
     let generic_params = impl_.generic_param_list().map(|generic_params| {
         let lifetime_params =
             generic_params.lifetime_params().map(ast::GenericParam::LifetimeParam);
-        let ty_or_const_params = generic_params.type_or_const_params().map(|param| {
+        let ty_or_const_params = generic_params.type_or_const_params().filter_map(|param| {
             // remove defaults since they can't be specified in impls
-            match param {
+            let param = match param {
                 ast::TypeOrConstParam::Type(param) => {
-                    let param = param.clone_for_update();
-                    param.remove_default();
+                    let param = make::type_param(param.name()?, param.type_bound_list());
                     ast::GenericParam::TypeParam(param)
                 }
                 ast::TypeOrConstParam::Const(param) => {
-                    let param = param.clone_for_update();
-                    param.remove_default();
+                    let param = make::const_param(param.name()?, param.ty()?);
                     ast::GenericParam::ConstParam(param)
                 }
-            }
+            };
+            Some(param)
         });
 
         make::generic_param_list(itertools::chain(lifetime_params, ty_or_const_params))
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs
index c7e5e41aac4..20ee9253d37 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_getter_or_setter.rs
@@ -294,7 +294,7 @@ fn generate_setter_from_info(info: &AssistInfo, record_field_info: &RecordFieldI
     let self_expr = make::ext::expr_self();
     let lhs = make::expr_field(self_expr, field_name);
     let rhs = make::expr_path(make::ext::ident_path(field_name));
-    let assign_stmt = make::expr_stmt(make::expr_assignment(lhs, rhs));
+    let assign_stmt = make::expr_stmt(make::expr_assignment(lhs, rhs).into());
     let body = make::block_expr([assign_stmt.into()], None);
 
     // Make the setter fn
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs
index 2862e6d5afb..14601ca0207 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_impl.rs
@@ -1,14 +1,14 @@
 use syntax::{
     ast::{self, AstNode, HasName, edit_in_place::Indent, make},
-    ted,
+    syntax_editor::{Position, SyntaxEditor},
 };
 
 use crate::{AssistContext, AssistId, Assists, utils};
 
-fn insert_impl(impl_: ast::Impl, nominal: &ast::Adt) {
+fn insert_impl(editor: &mut SyntaxEditor, impl_: &ast::Impl, nominal: &ast::Adt) {
     let indent = nominal.indent_level();
-    ted::insert_all_raw(
-        ted::Position::after(nominal.syntax()),
+    editor.insert_all(
+        Position::after(nominal.syntax()),
         vec![
             // Add a blank line after the ADT, and indentation for the impl to match the ADT
             make::tokens::whitespace(&format!("\n\n{indent}")).into(),
@@ -51,14 +51,17 @@ pub(crate) fn generate_impl(acc: &mut Assists, ctx: &AssistContext<'_>) -> Optio
             // Generate the impl
             let impl_ = utils::generate_impl(&nominal);
 
+            let mut editor = edit.make_editor(nominal.syntax());
             // Add a tabstop after the left curly brace
             if let Some(cap) = ctx.config.snippet_cap {
                 if let Some(l_curly) = impl_.assoc_item_list().and_then(|it| it.l_curly_token()) {
-                    edit.add_tabstop_after_token(cap, l_curly);
+                    let tabstop = edit.make_tabstop_after(cap);
+                    editor.add_annotation(l_curly, tabstop);
                 }
             }
 
-            insert_impl(impl_, &edit.make_mut(nominal));
+            insert_impl(&mut editor, &impl_, &nominal);
+            edit.add_file_edits(ctx.vfs_file_id(), editor);
         },
     )
 }
@@ -97,18 +100,22 @@ pub(crate) fn generate_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>) ->
             // Generate the impl
             let impl_ = utils::generate_trait_impl_intransitive(&nominal, make::ty_placeholder());
 
+            let mut editor = edit.make_editor(nominal.syntax());
             // Make the trait type a placeholder snippet
             if let Some(cap) = ctx.config.snippet_cap {
                 if let Some(trait_) = impl_.trait_() {
-                    edit.add_placeholder_snippet(cap, trait_);
+                    let placeholder = edit.make_placeholder_snippet(cap);
+                    editor.add_annotation(trait_.syntax(), placeholder);
                 }
 
                 if let Some(l_curly) = impl_.assoc_item_list().and_then(|it| it.l_curly_token()) {
-                    edit.add_tabstop_after_token(cap, l_curly);
+                    let tabstop = edit.make_tabstop_after(cap);
+                    editor.add_annotation(l_curly, tabstop);
                 }
             }
 
-            insert_impl(impl_, &edit.make_mut(nominal));
+            insert_impl(&mut editor, &impl_, &nominal);
+            edit.add_file_edits(ctx.vfs_file_id(), editor);
         },
     )
 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs
index bab2ccf3f33..4ddab2cfad0 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_mut_trait_impl.rs
@@ -1,6 +1,6 @@
-use ide_db::famous_defs::FamousDefs;
+use ide_db::{famous_defs::FamousDefs, traits::resolve_target_trait};
 use syntax::{
-    AstNode,
+    AstNode, T,
     ast::{self, edit_in_place::Indent, make},
     ted,
 };
@@ -32,7 +32,7 @@ use crate::{AssistContext, AssistId, Assists};
 //
 // $0impl<T> core::ops::IndexMut<Axis> for [T; 3] {
 //     fn index_mut(&mut self, index: Axis) -> &mut Self::Output {
-//         &self[index as usize]
+//         &mut self[index as usize]
 //     }
 // }
 //
@@ -48,36 +48,34 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>
     let impl_def = ctx.find_node_at_offset::<ast::Impl>()?.clone_for_update();
     let indent = impl_def.indent_level();
 
-    let trait_ = impl_def.trait_()?;
-    if let ast::Type::PathType(trait_path) = trait_ {
-        let trait_type = ctx.sema.resolve_trait(&trait_path.path()?)?;
-        let scope = ctx.sema.scope(trait_path.syntax())?;
-        if trait_type != FamousDefs(&ctx.sema, scope.krate()).core_convert_Index()? {
-            return None;
-        }
-    }
+    let ast::Type::PathType(path) = impl_def.trait_()? else {
+        return None;
+    };
+    let trait_name = path.path()?.segment()?.name_ref()?;
+
+    let scope = ctx.sema.scope(impl_def.trait_()?.syntax())?;
+    let famous = FamousDefs(&ctx.sema, scope.krate());
+
+    let trait_ = resolve_target_trait(&ctx.sema, &impl_def)?;
+    let trait_new = get_trait_mut(&trait_, famous)?;
 
     // Index -> IndexMut
-    let index_trait = impl_def
-        .syntax()
-        .descendants()
-        .filter_map(ast::NameRef::cast)
-        .find(|it| it.text() == "Index")?;
-    ted::replace(
-        index_trait.syntax(),
-        make::path_segment(make::name_ref("IndexMut")).clone_for_update().syntax(),
-    );
+    ted::replace(trait_name.syntax(), make::name_ref(trait_new).clone_for_update().syntax());
 
     // index -> index_mut
-    let trait_method_name = impl_def
+    let (trait_method_name, new_trait_method_name) = impl_def
         .syntax()
         .descendants()
         .filter_map(ast::Name::cast)
-        .find(|it| it.text() == "index")?;
-    ted::replace(trait_method_name.syntax(), make::name("index_mut").clone_for_update().syntax());
+        .find_map(process_method_name)?;
+    ted::replace(
+        trait_method_name.syntax(),
+        make::name(new_trait_method_name).clone_for_update().syntax(),
+    );
 
-    let type_alias = impl_def.syntax().descendants().find_map(ast::TypeAlias::cast)?;
-    ted::remove(type_alias.syntax());
+    if let Some(type_alias) = impl_def.syntax().descendants().find_map(ast::TypeAlias::cast) {
+        ted::remove(type_alias.syntax());
+    }
 
     // &self -> &mut self
     let mut_self_param = make::mut_self_param();
@@ -87,15 +85,14 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>
 
     // &Self::Output -> &mut Self::Output
     let ret_type = impl_def.syntax().descendants().find_map(ast::RetType::cast)?;
-    ted::replace(
-        ret_type.syntax(),
-        make::ret_type(make::ty("&mut Self::Output")).clone_for_update().syntax(),
-    );
+    let new_ret_type = process_ret_type(&ret_type)?;
+    ted::replace(ret_type.syntax(), make::ret_type(new_ret_type).clone_for_update().syntax());
 
     let fn_ = impl_def.assoc_item_list()?.assoc_items().find_map(|it| match it {
         ast::AssocItem::Fn(f) => Some(f),
         _ => None,
     })?;
+    let _ = process_ref_mut(&fn_);
 
     let assoc_list = make::assoc_item_list().clone_for_update();
     ted::replace(impl_def.assoc_item_list()?.syntax(), assoc_list.syntax());
@@ -104,7 +101,7 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>
     let target = impl_def.syntax().text_range();
     acc.add(
         AssistId::generate("generate_mut_trait_impl"),
-        "Generate `IndexMut` impl from this `Index` trait",
+        format!("Generate `{trait_new}` impl from this `{trait_name}` trait"),
         target,
         |edit| {
             edit.insert(target.start(), format!("$0{impl_def}\n\n{indent}"));
@@ -112,6 +109,52 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_>
     )
 }
 
+fn process_ref_mut(fn_: &ast::Fn) -> Option<()> {
+    let expr = fn_.body()?.tail_expr()?;
+    match &expr {
+        ast::Expr::RefExpr(ref_expr) if ref_expr.mut_token().is_none() => {
+            ted::insert_all_raw(
+                ted::Position::after(ref_expr.amp_token()?),
+                vec![make::token(T![mut]).into(), make::tokens::whitespace(" ").into()],
+            );
+        }
+        _ => {}
+    }
+    None
+}
+
+fn get_trait_mut(apply_trait: &hir::Trait, famous: FamousDefs<'_, '_>) -> Option<&'static str> {
+    let trait_ = Some(apply_trait);
+    if trait_ == famous.core_convert_Index().as_ref() {
+        return Some("IndexMut");
+    }
+    if trait_ == famous.core_convert_AsRef().as_ref() {
+        return Some("AsMut");
+    }
+    if trait_ == famous.core_borrow_Borrow().as_ref() {
+        return Some("BorrowMut");
+    }
+    None
+}
+
+fn process_method_name(name: ast::Name) -> Option<(ast::Name, &'static str)> {
+    let new_name = match &*name.text() {
+        "index" => "index_mut",
+        "as_ref" => "as_mut",
+        "borrow" => "borrow_mut",
+        _ => return None,
+    };
+    Some((name, new_name))
+}
+
+fn process_ret_type(ref_ty: &ast::RetType) -> Option<ast::Type> {
+    let ty = ref_ty.ty()?;
+    let ast::Type::RefType(ref_type) = ty else {
+        return None;
+    };
+    Some(make::ty_ref(ref_type.ty()?, true))
+}
+
 #[cfg(test)]
 mod tests {
     use crate::tests::{check_assist, check_assist_not_applicable};
@@ -139,7 +182,7 @@ pub enum Axis { X = 0, Y = 1, Z = 2 }
 
 $0impl<T> core::ops::IndexMut<Axis> for [T; 3] {
     fn index_mut(&mut self, index: Axis) -> &mut Self::Output {
-        &self[index as usize]
+        &mut self[index as usize]
     }
 }
 
@@ -188,6 +231,35 @@ impl<T> core::ops::Index<Axis> for [T; 3] where T: Copy {
 }
 "#,
         );
+
+        check_assist(
+            generate_mut_trait_impl,
+            r#"
+//- minicore: as_ref
+struct Foo(i32);
+
+impl core::convert::AsRef$0<i32> for Foo {
+    fn as_ref(&self) -> &i32 {
+        &self.0
+    }
+}
+"#,
+            r#"
+struct Foo(i32);
+
+$0impl core::convert::AsMut<i32> for Foo {
+    fn as_mut(&mut self) -> &mut i32 {
+        &mut self.0
+    }
+}
+
+impl core::convert::AsRef<i32> for Foo {
+    fn as_ref(&self) -> &i32 {
+        &self.0
+    }
+}
+"#,
+        );
     }
 
     #[test]
@@ -287,5 +359,13 @@ pub trait Index<Idx: ?Sized> {}
 impl<T> Index$0<i32> for [T; 3] {}
 "#,
         );
+        check_assist_not_applicable(
+            generate_mut_trait_impl,
+            r#"
+pub trait AsRef<T: ?Sized> {}
+
+impl AsRef$0<i32> for [T; 3] {}
+"#,
+        );
     }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs
index 4837f92f934..51c2f65e025 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_new.rs
@@ -1,5 +1,6 @@
 use ide_db::{
-    imports::import_assets::item_for_path_search, use_trivial_constructor::use_trivial_constructor,
+    imports::import_assets::item_for_path_search, syntax_helpers::suggest_name::NameGenerator,
+    use_trivial_constructor::use_trivial_constructor,
 };
 use syntax::{
     ast::{self, AstNode, HasName, HasVisibility, StructKind, edit_in_place::Indent, make},
@@ -35,10 +36,30 @@ use crate::{
 pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
     let strukt = ctx.find_node_at_offset::<ast::Struct>()?;
 
-    // We want to only apply this to non-union structs with named fields
     let field_list = match strukt.kind() {
-        StructKind::Record(named) => named,
-        _ => return None,
+        StructKind::Record(named) => {
+            named.fields().filter_map(|f| Some((f.name()?, f.ty()?))).collect::<Vec<_>>()
+        }
+        StructKind::Tuple(tuple) => {
+            let mut name_generator = NameGenerator::default();
+            tuple
+                .fields()
+                .enumerate()
+                .filter_map(|(i, f)| {
+                    let ty = f.ty()?;
+                    let name = match name_generator.for_type(
+                        &ctx.sema.resolve_type(&ty)?,
+                        ctx.db(),
+                        ctx.edition(),
+                    ) {
+                        Some(name) => name,
+                        None => name_generator.suggest_name(&format!("_{i}")),
+                    };
+                    Some((make::name(name.as_str()), f.ty()?))
+                })
+                .collect::<Vec<_>>()
+        }
+        StructKind::Unit => return None,
     };
 
     // Return early if we've found an existing new fn
@@ -50,11 +71,9 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
     let target = strukt.syntax().text_range();
     acc.add(AssistId::generate("generate_new"), "Generate `new`", target, |builder| {
         let trivial_constructors = field_list
-            .fields()
-            .map(|f| {
-                let name = f.name()?;
-
-                let ty = ctx.sema.resolve_type(&f.ty()?)?;
+            .iter()
+            .map(|(name, ty)| {
+                let ty = ctx.sema.resolve_type(ty)?;
 
                 let item_in_ns = hir::ItemInNs::from(hir::ModuleDef::from(ty.as_adt()?));
 
@@ -73,34 +92,44 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
                     edition,
                 )?;
 
-                Some(make::record_expr_field(make::name_ref(&name.text()), Some(expr)))
+                Some((make::name_ref(&name.text()), Some(expr)))
             })
             .collect::<Vec<_>>();
 
-        let params = field_list.fields().enumerate().filter_map(|(i, f)| {
+        let params = field_list.iter().enumerate().filter_map(|(i, (name, ty))| {
             if trivial_constructors[i].is_none() {
-                let name = f.name()?;
-                let ty = f.ty()?;
-
-                Some(make::param(make::ident_pat(false, false, name).into(), ty))
+                Some(make::param(make::ident_pat(false, false, name.clone()).into(), ty.clone()))
             } else {
                 None
             }
         });
         let params = make::param_list(None, params);
 
-        let fields = field_list.fields().enumerate().filter_map(|(i, f)| {
-            let constructor = trivial_constructors[i].clone();
-            if constructor.is_some() {
+        let fields = field_list.iter().enumerate().map(|(i, (name, _))| {
+            if let Some(constructor) = trivial_constructors[i].clone() {
                 constructor
             } else {
-                Some(make::record_expr_field(make::name_ref(&f.name()?.text()), None))
+                (make::name_ref(&name.text()), None)
             }
         });
-        let fields = make::record_expr_field_list(fields);
 
-        let record_expr = make::record_expr(make::ext::ident_path("Self"), fields);
-        let body = make::block_expr(None, Some(record_expr.into()));
+        let tail_expr: ast::Expr = match strukt.kind() {
+            StructKind::Record(_) => {
+                let fields = fields.map(|(name, expr)| make::record_expr_field(name, expr));
+                let fields = make::record_expr_field_list(fields);
+                make::record_expr(make::ext::ident_path("Self"), fields).into()
+            }
+            StructKind::Tuple(_) => {
+                let args = fields.map(|(arg, expr)| {
+                    let arg = || make::expr_path(make::path_unqualified(make::path_segment(arg)));
+                    expr.unwrap_or_else(arg)
+                });
+                let arg_list = make::arg_list(args);
+                make::expr_call(make::expr_path(make::ext::ident_path("Self")), arg_list).into()
+            }
+            StructKind::Unit => unreachable!(),
+        };
+        let body = make::block_expr(None, tail_expr.into());
 
         let ret_type = make::ret_type(make::ty_path(make::ext::ident_path("Self")));
 
@@ -120,8 +149,35 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
         .clone_for_update();
         fn_.indent(1.into());
 
-        // Add a tabstop before the name
         if let Some(cap) = ctx.config.snippet_cap {
+            match strukt.kind() {
+                StructKind::Tuple(_) => {
+                    let struct_args = fn_
+                        .body()
+                        .unwrap()
+                        .syntax()
+                        .descendants()
+                        .filter(|it| syntax::ast::ArgList::can_cast(it.kind()))
+                        .flat_map(|args| args.children())
+                        .filter(|it| syntax::ast::PathExpr::can_cast(it.kind()))
+                        .enumerate()
+                        .filter_map(|(i, node)| {
+                            if trivial_constructors[i].is_none() { Some(node) } else { None }
+                        });
+                    if let Some(fn_params) = fn_.param_list() {
+                        for (struct_arg, fn_param) in struct_args.zip(fn_params.params()) {
+                            if let Some(fn_pat) = fn_param.pat() {
+                                let fn_pat = fn_pat.syntax().clone();
+                                builder
+                                    .add_placeholder_snippet_group(cap, vec![struct_arg, fn_pat]);
+                            }
+                        }
+                    }
+                }
+                _ => {}
+            }
+
+            // Add a tabstop before the name
             if let Some(name) = fn_.name() {
                 builder.add_tabstop_before(cap, name);
             }
@@ -157,7 +213,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option
 }
 
 #[cfg(test)]
-mod tests {
+mod record_tests {
     use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
 
     use super::*;
@@ -695,3 +751,308 @@ impl<T> Source<T> {
         );
     }
 }
+
+#[cfg(test)]
+mod tuple_tests {
+    use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};
+
+    use super::*;
+
+    #[test]
+    fn test_generate_new_with_zst_fields() {
+        check_assist(
+            generate_new,
+            r#"
+struct Empty;
+
+struct Foo(Empty$0);
+"#,
+            r#"
+struct Empty;
+
+struct Foo(Empty);
+
+impl Foo {
+    fn $0new() -> Self {
+        Self(Empty)
+    }
+}
+"#,
+        );
+        check_assist(
+            generate_new,
+            r#"
+struct Empty;
+
+struct Foo(String, Empty$0);
+"#,
+            r#"
+struct Empty;
+
+struct Foo(String, Empty);
+
+impl Foo {
+    fn $0new(${1:_0}: String) -> Self {
+        Self(${1:_0}, Empty)
+    }
+}
+"#,
+        );
+        check_assist(
+            generate_new,
+            r#"
+enum Empty { Bar }
+
+struct Foo(Empty$0);
+"#,
+            r#"
+enum Empty { Bar }
+
+struct Foo(Empty);
+
+impl Foo {
+    fn $0new() -> Self {
+        Self(Empty::Bar)
+    }
+}
+"#,
+        );
+
+        // make sure the assist only works on unit variants
+        check_assist(
+            generate_new,
+            r#"
+struct Empty {}
+
+struct Foo(Empty$0);
+"#,
+            r#"
+struct Empty {}
+
+struct Foo(Empty);
+
+impl Foo {
+    fn $0new(${1:empty}: Empty) -> Self {
+        Self(${1:empty})
+    }
+}
+"#,
+        );
+        check_assist(
+            generate_new,
+            r#"
+enum Empty { Bar {} }
+
+struct Foo(Empty$0);
+"#,
+            r#"
+enum Empty { Bar {} }
+
+struct Foo(Empty);
+
+impl Foo {
+    fn $0new(${1:empty}: Empty) -> Self {
+        Self(${1:empty})
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_generate_new() {
+        check_assist(
+            generate_new,
+            r#"
+struct Foo($0);
+"#,
+            r#"
+struct Foo();
+
+impl Foo {
+    fn $0new() -> Self {
+        Self()
+    }
+}
+"#,
+        );
+        check_assist(
+            generate_new,
+            r#"
+struct Foo<T: Clone>($0);
+"#,
+            r#"
+struct Foo<T: Clone>();
+
+impl<T: Clone> Foo<T> {
+    fn $0new() -> Self {
+        Self()
+    }
+}
+"#,
+        );
+        check_assist(
+            generate_new,
+            r#"
+struct Foo<'a, T: Foo<'a>>($0);
+"#,
+            r#"
+struct Foo<'a, T: Foo<'a>>();
+
+impl<'a, T: Foo<'a>> Foo<'a, T> {
+    fn $0new() -> Self {
+        Self()
+    }
+}
+"#,
+        );
+        check_assist(
+            generate_new,
+            r#"
+struct Foo(String$0);
+"#,
+            r#"
+struct Foo(String);
+
+impl Foo {
+    fn $0new(${1:_0}: String) -> Self {
+        Self(${1:_0})
+    }
+}
+"#,
+        );
+        check_assist(
+            generate_new,
+            r#"
+struct Vec<T> { };
+struct Foo(String, Vec<i32>$0);
+"#,
+            r#"
+struct Vec<T> { };
+struct Foo(String, Vec<i32>);
+
+impl Foo {
+    fn $0new(${1:_0}: String, ${2:items}: Vec<i32>) -> Self {
+        Self(${1:_0}, ${2:items})
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn check_that_visibility_modifiers_dont_get_brought_in() {
+        check_assist(
+            generate_new,
+            r#"
+struct Vec<T> { };
+struct Foo(pub String, pub Vec<i32>$0);
+"#,
+            r#"
+struct Vec<T> { };
+struct Foo(pub String, pub Vec<i32>);
+
+impl Foo {
+    fn $0new(${1:_0}: String, ${2:items}: Vec<i32>) -> Self {
+        Self(${1:_0}, ${2:items})
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn generate_new_not_applicable_if_fn_exists() {
+        check_assist_not_applicable(
+            generate_new,
+            r#"
+struct Foo($0);
+
+impl Foo {
+    fn new() -> Self {
+        Self
+    }
+}
+"#,
+        );
+
+        check_assist_not_applicable(
+            generate_new,
+            r#"
+struct Foo($0);
+
+impl Foo {
+    fn New() -> Self {
+        Self
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn generate_new_target() {
+        check_assist_target(
+            generate_new,
+            r#"
+struct SomeThingIrrelevant;
+/// Has a lifetime parameter
+struct Foo<'a, T: Foo<'a>>($0);
+struct EvenMoreIrrelevant;
+"#,
+            "/// Has a lifetime parameter
+struct Foo<'a, T: Foo<'a>>();",
+        );
+    }
+
+    #[test]
+    fn test_unrelated_new() {
+        check_assist(
+            generate_new,
+            r#"
+pub struct AstId<N: AstNode> {
+    file_id: HirFileId,
+    file_ast_id: FileAstId<N>,
+}
+
+impl<N: AstNode> AstId<N> {
+    pub fn new(file_id: HirFileId, file_ast_id: FileAstId<N>) -> AstId<N> {
+        AstId { file_id, file_ast_id }
+    }
+}
+
+pub struct Source<T>(pub HirFileId,$0 pub T);
+
+impl<T> Source<T> {
+    pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
+        Source(self.file_id, f(self.ast))
+    }
+}
+"#,
+            r#"
+pub struct AstId<N: AstNode> {
+    file_id: HirFileId,
+    file_ast_id: FileAstId<N>,
+}
+
+impl<N: AstNode> AstId<N> {
+    pub fn new(file_id: HirFileId, file_ast_id: FileAstId<N>) -> AstId<N> {
+        AstId { file_id, file_ast_id }
+    }
+}
+
+pub struct Source<T>(pub HirFileId, pub T);
+
+impl<T> Source<T> {
+    pub fn $0new(${1:_0}: HirFileId, ${2:_1}: T) -> Self {
+        Self(${1:_0}, ${2:_1})
+    }
+
+    pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
+        Source(self.file_id, f(self.ast))
+    }
+}
+"#,
+        );
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs
new file mode 100644
index 00000000000..4e95ceb2e85
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/generate_single_field_struct_from.rs
@@ -0,0 +1,1000 @@
+use ast::make;
+use hir::{HasCrate, ModuleDef, Semantics};
+use ide_db::{
+    RootDatabase, famous_defs::FamousDefs, helpers::mod_path_to_ast,
+    imports::import_assets::item_for_path_search, use_trivial_constructor::use_trivial_constructor,
+};
+use syntax::{
+    TokenText,
+    ast::{self, AstNode, HasGenericParams, HasName, edit, edit_in_place::Indent},
+};
+
+use crate::{
+    AssistId,
+    assist_context::{AssistContext, Assists},
+    utils::add_cfg_attrs_to,
+};
+
+// Assist: generate_single_field_struct_from
+//
+// Implement From for a single field structure, ignore trivial types.
+//
+// ```
+// # //- minicore: from, phantom_data
+// use core::marker::PhantomData;
+// struct $0Foo<T> {
+//     id: i32,
+//     _phantom_data: PhantomData<T>,
+// }
+// ```
+// ->
+// ```
+// use core::marker::PhantomData;
+// struct Foo<T> {
+//     id: i32,
+//     _phantom_data: PhantomData<T>,
+// }
+//
+// impl<T> From<i32> for Foo<T> {
+//     fn from(id: i32) -> Self {
+//         Self { id, _phantom_data: PhantomData }
+//     }
+// }
+// ```
+pub(crate) fn generate_single_field_struct_from(
+    acc: &mut Assists,
+    ctx: &AssistContext<'_>,
+) -> Option<()> {
+    let strukt_name = ctx.find_node_at_offset::<ast::Name>()?;
+    let adt = ast::Adt::cast(strukt_name.syntax().parent()?)?;
+    let ast::Adt::Struct(strukt) = adt else {
+        return None;
+    };
+
+    let sema = &ctx.sema;
+    let (names, types) = get_fields(&strukt)?;
+
+    let module = sema.scope(strukt.syntax())?.module();
+    let constructors = make_constructors(ctx, module, &types);
+
+    if constructors.iter().filter(|expr| expr.is_none()).count() != 1 {
+        return None;
+    }
+    let main_field_i = constructors.iter().position(Option::is_none)?;
+    if from_impl_exists(&strukt, main_field_i, &ctx.sema).is_some() {
+        return None;
+    }
+
+    let main_field_name =
+        names.as_ref().map_or(TokenText::borrowed("value"), |names| names[main_field_i].text());
+    let main_field_ty = types[main_field_i].clone();
+
+    acc.add(
+        AssistId::generate("generate_single_field_struct_from"),
+        "Generate single field `From`",
+        strukt.syntax().text_range(),
+        |builder| {
+            let indent = strukt.indent_level();
+            let ty_where_clause = strukt.where_clause();
+            let type_gen_params = strukt.generic_param_list();
+            let type_gen_args = type_gen_params.as_ref().map(|params| params.to_generic_args());
+            let trait_gen_args = Some(make::generic_arg_list([ast::GenericArg::TypeArg(
+                make::type_arg(main_field_ty.clone()),
+            )]));
+
+            let ty = make::ty(&strukt_name.text());
+
+            let constructor =
+                make_adt_constructor(names.as_deref(), constructors, &main_field_name);
+            let body = make::block_expr([], Some(constructor));
+
+            let fn_ = make::fn_(
+                None,
+                make::name("from"),
+                None,
+                None,
+                make::param_list(
+                    None,
+                    [make::param(
+                        make::path_pat(make::path_from_text(&main_field_name)),
+                        main_field_ty,
+                    )],
+                ),
+                body,
+                Some(make::ret_type(make::ty("Self"))),
+                false,
+                false,
+                false,
+                false,
+            )
+            .clone_for_update();
+
+            fn_.indent(1.into());
+
+            let impl_ = make::impl_trait(
+                false,
+                None,
+                trait_gen_args,
+                type_gen_params,
+                type_gen_args,
+                false,
+                make::ty("From"),
+                ty.clone(),
+                None,
+                ty_where_clause.map(|wc| edit::AstNodeEdit::reset_indent(&wc)),
+                None,
+            )
+            .clone_for_update();
+
+            impl_.get_or_create_assoc_item_list().add_item(fn_.into());
+
+            add_cfg_attrs_to(&strukt, &impl_);
+
+            impl_.reindent_to(indent);
+
+            builder.insert(strukt.syntax().text_range().end(), format!("\n\n{indent}{impl_}"));
+        },
+    )
+}
+
+fn make_adt_constructor(
+    names: Option<&[ast::Name]>,
+    constructors: Vec<Option<ast::Expr>>,
+    main_field_name: &TokenText<'_>,
+) -> ast::Expr {
+    if let Some(names) = names {
+        let fields = make::record_expr_field_list(names.iter().zip(constructors).map(
+            |(name, initializer)| {
+                make::record_expr_field(make::name_ref(&name.text()), initializer)
+            },
+        ));
+        make::record_expr(make::path_from_text("Self"), fields).into()
+    } else {
+        let arg_list = make::arg_list(constructors.into_iter().map(|expr| {
+            expr.unwrap_or_else(|| make::expr_path(make::path_from_text(main_field_name)))
+        }));
+        make::expr_call(make::expr_path(make::path_from_text("Self")), arg_list).into()
+    }
+}
+
+fn make_constructors(
+    ctx: &AssistContext<'_>,
+    module: hir::Module,
+    types: &[ast::Type],
+) -> Vec<Option<ast::Expr>> {
+    let (db, sema) = (ctx.db(), &ctx.sema);
+    types
+        .iter()
+        .map(|ty| {
+            let ty = sema.resolve_type(ty)?;
+            if ty.is_unit() {
+                return Some(make::expr_tuple([]).into());
+            }
+            let item_in_ns = ModuleDef::Adt(ty.as_adt()?).into();
+            let edition = module.krate().edition(db);
+
+            let ty_path = module.find_path(
+                db,
+                item_for_path_search(db, item_in_ns)?,
+                ctx.config.import_path_config(),
+            )?;
+
+            use_trivial_constructor(db, mod_path_to_ast(&ty_path, edition), &ty, edition)
+        })
+        .collect()
+}
+
+fn get_fields(strukt: &ast::Struct) -> Option<(Option<Vec<ast::Name>>, Vec<ast::Type>)> {
+    Some(match strukt.kind() {
+        ast::StructKind::Unit => return None,
+        ast::StructKind::Record(fields) => {
+            let names = fields.fields().map(|field| field.name()).collect::<Option<_>>()?;
+            let types = fields.fields().map(|field| field.ty()).collect::<Option<_>>()?;
+            (Some(names), types)
+        }
+        ast::StructKind::Tuple(fields) => {
+            (None, fields.fields().map(|field| field.ty()).collect::<Option<_>>()?)
+        }
+    })
+}
+
+fn from_impl_exists(
+    strukt: &ast::Struct,
+    main_field_i: usize,
+    sema: &Semantics<'_, RootDatabase>,
+) -> Option<()> {
+    let db = sema.db;
+    let strukt = sema.to_def(strukt)?;
+    let krate = strukt.krate(db);
+    let from_trait = FamousDefs(sema, krate).core_convert_From()?;
+    let ty = strukt.fields(db).get(main_field_i)?.ty(db);
+
+    strukt.ty(db).impls_trait(db, from_trait, &[ty]).then_some(())
+}
+
+#[cfg(test)]
+mod tests {
+    use crate::tests::{check_assist, check_assist_not_applicable};
+
+    use super::generate_single_field_struct_from;
+
+    #[test]
+    fn works() {
+        check_assist(
+            generate_single_field_struct_from,
+            r#"
+            //- minicore: from
+            struct $0Foo {
+                foo: i32,
+            }
+            "#,
+            r#"
+            struct Foo {
+                foo: i32,
+            }
+
+            impl From<i32> for Foo {
+                fn from(foo: i32) -> Self {
+                    Self { foo }
+                }
+            }
+            "#,
+        );
+        check_assist(
+            generate_single_field_struct_from,
+            r#"
+            //- minicore: from, phantom_data
+            struct $0Foo {
+                b1: (),
+                b2: core::marker::PhantomData,
+                foo: i32,
+                a1: (),
+                a2: core::marker::PhantomData,
+            }
+            "#,
+            r#"
+            struct Foo {
+                b1: (),
+                b2: core::marker::PhantomData,
+                foo: i32,
+                a1: (),
+                a2: core::marker::PhantomData,
+            }
+
+            impl From<i32> for Foo {
+                fn from(foo: i32) -> Self {
+                    Self { b1: (), b2: core::marker::PhantomData, foo, a1: (), a2: core::marker::PhantomData }
+                }
+            }
+            "#,
+        );
+    }
+
+    #[test]
+    fn cfgs() {
+        check_assist(
+            generate_single_field_struct_from,
+            r#"
+            //- minicore: from
+            #[cfg(feature = "foo")]
+            #[cfg(test)]
+            struct $0Foo {
+                foo: i32,
+            }
+            "#,
+            r#"
+            #[cfg(feature = "foo")]
+            #[cfg(test)]
+            struct Foo {
+                foo: i32,
+            }
+
+            #[cfg(feature = "foo")]
+            #[cfg(test)]
+            impl From<i32> for Foo {
+                fn from(foo: i32) -> Self {
+                    Self { foo }
+                }
+            }
+            "#,
+        );
+    }
+
+    #[test]
+    fn indent() {
+        check_assist(
+            generate_single_field_struct_from,
+            r#"
+            //- minicore: from
+            mod foo {
+                struct $0Foo {
+                    foo: i32,
+                }
+            }
+            "#,
+            r#"
+            mod foo {
+                struct Foo {
+                    foo: i32,
+                }
+
+                impl From<i32> for Foo {
+                    fn from(foo: i32) -> Self {
+                        Self { foo }
+                    }
+                }
+            }
+            "#,
+        );
+        check_assist(
+            generate_single_field_struct_from,
+            r#"
+            //- minicore: from
+            mod foo {
+                mod bar {
+                    struct $0Foo {
+                        foo: i32,
+                    }
+                }
+            }
+            "#,
+            r#"
+            mod foo {
+                mod bar {
+                    struct Foo {
+                        foo: i32,
+                    }
+
+                    impl From<i32> for Foo {
+                        fn from(foo: i32) -> Self {
+                            Self { foo }
+                        }
+                    }
+                }
+            }
+            "#,
+        );
+    }
+
+    #[test]
+    fn where_clause_indent() {
+        check_assist(
+            generate_single_field_struct_from,
+            r#"
+            //- minicore: from
+            mod foo {
+                mod bar {
+                    trait Trait {}
+                    struct $0Foo<T>
+                    where
+                        T: Trait,
+                    {
+                        foo: T,
+                    }
+                }
+            }
+            "#,
+            r#"
+            mod foo {
+                mod bar {
+                    trait Trait {}
+                    struct Foo<T>
+                    where
+                        T: Trait,
+                    {
+                        foo: T,
+                    }
+
+                    impl<T> From<T> for Foo<T>
+                    where
+                        T: Trait,
+                    {
+                        fn from(foo: T) -> Self {
+                            Self { foo }
+                        }
+                    }
+                }
+            }
+            "#,
+        );
+        check_assist(
+            generate_single_field_struct_from,
+            r#"
+            //- minicore: from
+            mod foo {
+                mod bar {
+                    trait Trait<const B: bool> {}
+                    struct $0Foo<T>
+                    where
+                        T: Trait<{
+                            true
+                        }>
+                    {
+                        foo: T,
+                    }
+                }
+            }
+            "#,
+            r#"
+            mod foo {
+                mod bar {
+                    trait Trait<const B: bool> {}
+                    struct Foo<T>
+                    where
+                        T: Trait<{
+                            true
+                        }>
+                    {
+                        foo: T,
+                    }
+
+                    impl<T> From<T> for Foo<T>
+                    where
+                        T: Trait<{
+                            true
+                        }>
+                    {
+                        fn from(foo: T) -> Self {
+                            Self { foo }
+                        }
+                    }
+                }
+            }
+            "#,
+        );
+    }
+
+    #[test]
+    fn generics() {
+        check_assist(
+            generate_single_field_struct_from,
+            r#"
+            //- minicore: from
+            struct $0Foo<T> {
+                foo: T,
+            }
+            "#,
+            r#"
+            struct Foo<T> {
+                foo: T,
+            }
+
+            impl<T> From<T> for Foo<T> {
+                fn from(foo: T) -> Self {
+                    Self { foo }
+                }
+            }
+            "#,
+        );
+        check_assist(
+            generate_single_field_struct_from,
+            r#"
+            //- minicore: from
+            struct $0Foo<T: Send> {
+                foo: T,
+            }
+            "#,
+            r#"
+            struct Foo<T: Send> {
+                foo: T,
+            }
+
+            impl<T: Send> From<T> for Foo<T> {
+                fn from(foo: T) -> Self {
+                    Self { foo }
+                }
+            }
+            "#,
+        );
+        check_assist(
+            generate_single_field_struct_from,
+            r#"
+            //- minicore: from
+            struct $0Foo<T: Send> where T: Sync,{
+                foo: T,
+            }
+            "#,
+            r#"
+            struct Foo<T: Send> where T: Sync,{
+                foo: T,
+            }
+
+            impl<T: Send> From<T> for Foo<T>
+            where T: Sync,
+            {
+                fn from(foo: T) -> Self {
+                    Self { foo }
+                }
+            }
+            "#,
+        );
+        check_assist(
+            generate_single_field_struct_from,
+            r#"
+            //- minicore: from
+            struct $0Foo<T: Send> where T: Sync {
+                foo: T,
+            }
+            "#,
+            r#"
+            struct Foo<T: Send> where T: Sync {
+                foo: T,
+            }
+
+            impl<T: Send> From<T> for Foo<T>
+            where T: Sync
+            {
+                fn from(foo: T) -> Self {
+                    Self { foo }
+                }
+            }
+            "#,
+        );
+        check_assist(
+            generate_single_field_struct_from,
+            r#"
+            //- minicore: from
+            struct $0Foo<T: Send> where T: Sync, Self: Send {
+                foo: T,
+            }
+            "#,
+            r#"
+            struct Foo<T: Send> where T: Sync, Self: Send {
+                foo: T,
+            }
+
+            impl<T: Send> From<T> for Foo<T>
+            where T: Sync, Self: Send
+            {
+                fn from(foo: T) -> Self {
+                    Self { foo }
+                }
+            }
+            "#,
+        );
+        check_assist(
+            generate_single_field_struct_from,
+            r#"
+            //- minicore: from
+            struct $0Foo<T: Send>
+            where T: Sync, Self: Send
+            {
+                foo: T,
+            }
+            "#,
+            r#"
+            struct Foo<T: Send>
+            where T: Sync, Self: Send
+            {
+                foo: T,
+            }
+
+            impl<T: Send> From<T> for Foo<T>
+            where T: Sync, Self: Send
+            {
+                fn from(foo: T) -> Self {
+                    Self { foo }
+                }
+            }
+            "#,
+        );
+        check_assist(
+            generate_single_field_struct_from,
+            r#"
+            //- minicore: from
+            struct $0Foo<T: Send>
+            where T: Sync, Self: Send,
+            {
+                foo: T,
+            }
+            "#,
+            r#"
+            struct Foo<T: Send>
+            where T: Sync, Self: Send,
+            {
+                foo: T,
+            }
+
+            impl<T: Send> From<T> for Foo<T>
+            where T: Sync, Self: Send,
+            {
+                fn from(foo: T) -> Self {
+                    Self { foo }
+                }
+            }
+            "#,
+        );
+        check_assist(
+            generate_single_field_struct_from,
+            r#"
+            //- minicore: from
+            struct $0Foo<T: Send>
+            where T: Sync,
+                  Self: Send,
+            {
+                foo: T,
+            }
+            "#,
+            r#"
+            struct Foo<T: Send>
+            where T: Sync,
+                  Self: Send,
+            {
+                foo: T,
+            }
+
+            impl<T: Send> From<T> for Foo<T>
+            where T: Sync,
+                  Self: Send,
+            {
+                fn from(foo: T) -> Self {
+                    Self { foo }
+                }
+            }
+            "#,
+        );
+        check_assist(
+            generate_single_field_struct_from,
+            r#"
+            //- minicore: from
+            struct $0Foo<T: Send>
+            where
+                T: Sync,
+                Self: Send,
+            {
+                foo: T,
+            }
+            "#,
+            r#"
+            struct Foo<T: Send>
+            where
+                T: Sync,
+                Self: Send,
+            {
+                foo: T,
+            }
+
+            impl<T: Send> From<T> for Foo<T>
+            where
+                T: Sync,
+                Self: Send,
+            {
+                fn from(foo: T) -> Self {
+                    Self { foo }
+                }
+            }
+            "#,
+        );
+        check_assist(
+            generate_single_field_struct_from,
+            r#"
+            //- minicore: from
+            struct $0Foo<T: Send + Sync>
+            where
+                T: Sync,
+                Self: Send,
+            {
+                foo: T,
+            }
+            "#,
+            r#"
+            struct Foo<T: Send + Sync>
+            where
+                T: Sync,
+                Self: Send,
+            {
+                foo: T,
+            }
+
+            impl<T: Send + Sync> From<T> for Foo<T>
+            where
+                T: Sync,
+                Self: Send,
+            {
+                fn from(foo: T) -> Self {
+                    Self { foo }
+                }
+            }
+            "#,
+        );
+    }
+
+    #[test]
+    fn tuple() {
+        check_assist(
+            generate_single_field_struct_from,
+            r#"
+            //- minicore: from
+            struct $0Foo(i32);
+            "#,
+            r#"
+            struct Foo(i32);
+
+            impl From<i32> for Foo {
+                fn from(value: i32) -> Self {
+                    Self(value)
+                }
+            }
+            "#,
+        );
+        check_assist(
+            generate_single_field_struct_from,
+            r#"
+            //- minicore: from
+            struct $0Foo<T>(T);
+            "#,
+            r#"
+            struct Foo<T>(T);
+
+            impl<T> From<T> for Foo<T> {
+                fn from(value: T) -> Self {
+                    Self(value)
+                }
+            }
+            "#,
+        );
+    }
+
+    #[test]
+    fn trivial() {
+        check_assist(
+            generate_single_field_struct_from,
+            r#"
+            //- minicore: from, phantom_data
+            use core::marker::PhantomData;
+            struct $0Foo(i32, PhantomData<i32>);
+            "#,
+            r#"
+            use core::marker::PhantomData;
+            struct Foo(i32, PhantomData<i32>);
+
+            impl From<i32> for Foo {
+                fn from(value: i32) -> Self {
+                    Self(value, PhantomData)
+                }
+            }
+            "#,
+        );
+        check_assist(
+            generate_single_field_struct_from,
+            r#"
+            //- minicore: from, phantom_data
+            use core::marker::PhantomData;
+            struct $0Foo(i32, PhantomData<()>);
+            "#,
+            r#"
+            use core::marker::PhantomData;
+            struct Foo(i32, PhantomData<()>);
+
+            impl From<i32> for Foo {
+                fn from(value: i32) -> Self {
+                    Self(value, PhantomData)
+                }
+            }
+            "#,
+        );
+        check_assist(
+            generate_single_field_struct_from,
+            r#"
+            //- minicore: from, phantom_data
+            use core::marker::PhantomData;
+            struct $0Foo(PhantomData<()>, i32, PhantomData<()>);
+            "#,
+            r#"
+            use core::marker::PhantomData;
+            struct Foo(PhantomData<()>, i32, PhantomData<()>);
+
+            impl From<i32> for Foo {
+                fn from(value: i32) -> Self {
+                    Self(PhantomData, value, PhantomData)
+                }
+            }
+            "#,
+        );
+        check_assist(
+            generate_single_field_struct_from,
+            r#"
+            //- minicore: from, phantom_data
+            use core::marker::PhantomData;
+            struct $0Foo<T>(PhantomData<T>, i32, PhantomData<()>);
+            "#,
+            r#"
+            use core::marker::PhantomData;
+            struct Foo<T>(PhantomData<T>, i32, PhantomData<()>);
+
+            impl<T> From<i32> for Foo<T> {
+                fn from(value: i32) -> Self {
+                    Self(PhantomData, value, PhantomData)
+                }
+            }
+            "#,
+        );
+    }
+
+    #[test]
+    fn unit() {
+        check_assist(
+            generate_single_field_struct_from,
+            r#"
+            //- minicore: from
+            struct $0Foo(i32, ());
+            "#,
+            r#"
+            struct Foo(i32, ());
+
+            impl From<i32> for Foo {
+                fn from(value: i32) -> Self {
+                    Self(value, ())
+                }
+            }
+            "#,
+        );
+        check_assist(
+            generate_single_field_struct_from,
+            r#"
+            //- minicore: from
+            struct $0Foo((), i32, ());
+            "#,
+            r#"
+            struct Foo((), i32, ());
+
+            impl From<i32> for Foo {
+                fn from(value: i32) -> Self {
+                    Self((), value, ())
+                }
+            }
+            "#,
+        );
+        check_assist(
+            generate_single_field_struct_from,
+            r#"
+            //- minicore: from
+            struct $0Foo((), (), i32, ());
+            "#,
+            r#"
+            struct Foo((), (), i32, ());
+
+            impl From<i32> for Foo {
+                fn from(value: i32) -> Self {
+                    Self((), (), value, ())
+                }
+            }
+            "#,
+        );
+    }
+
+    #[test]
+    fn invalid_multiple_main_field() {
+        check_assist_not_applicable(
+            generate_single_field_struct_from,
+            r#"
+            //- minicore: from
+            struct $0Foo(i32, i32);
+            "#,
+        );
+        check_assist_not_applicable(
+            generate_single_field_struct_from,
+            r#"
+            //- minicore: from
+            struct $0Foo<T>(i32, T);
+            "#,
+        );
+        check_assist_not_applicable(
+            generate_single_field_struct_from,
+            r#"
+            //- minicore: from
+            struct $0Foo<T>(T, T);
+            "#,
+        );
+        check_assist_not_applicable(
+            generate_single_field_struct_from,
+            r#"
+            //- minicore: from
+            struct $0Foo<T> { foo: T, bar: i32 }
+            "#,
+        );
+        check_assist_not_applicable(
+            generate_single_field_struct_from,
+            r#"
+            //- minicore: from
+            struct $0Foo { foo: i32, bar: i64 }
+            "#,
+        );
+    }
+
+    #[test]
+    fn exists_other_from() {
+        check_assist(
+            generate_single_field_struct_from,
+            r#"
+            //- minicore: from
+            struct $0Foo(i32);
+
+            impl From<&i32> for Foo {
+                fn from(value: &i32) -> Self {
+                    todo!()
+                }
+            }
+            "#,
+            r#"
+            struct Foo(i32);
+
+            impl From<i32> for Foo {
+                fn from(value: i32) -> Self {
+                    Self(value)
+                }
+            }
+
+            impl From<&i32> for Foo {
+                fn from(value: &i32) -> Self {
+                    todo!()
+                }
+            }
+            "#,
+        );
+        check_assist(
+            generate_single_field_struct_from,
+            r#"
+            //- minicore: from
+            struct $0Foo(i32);
+
+            type X = i32;
+
+            impl From<&X> for Foo {
+                fn from(value: &X) -> Self {
+                    todo!()
+                }
+            }
+            "#,
+            r#"
+            struct Foo(i32);
+
+            impl From<i32> for Foo {
+                fn from(value: i32) -> Self {
+                    Self(value)
+                }
+            }
+
+            type X = i32;
+
+            impl From<&X> for Foo {
+                fn from(value: &X) -> Self {
+                    todo!()
+                }
+            }
+            "#,
+        );
+    }
+
+    #[test]
+    fn exists_from() {
+        check_assist_not_applicable(
+            generate_single_field_struct_from,
+            r#"
+            //- minicore: from
+            struct $0Foo(i32);
+
+            impl From<i32> for Foo {
+                fn from(_: i32) -> Self {
+                    todo!()
+                }
+            }
+            "#,
+        );
+        check_assist_not_applicable(
+            generate_single_field_struct_from,
+            r#"
+            //- minicore: from
+            struct $0Foo(i32);
+
+            type X = i32;
+
+            impl From<X> for Foo {
+                fn from(_: X) -> Self {
+                    todo!()
+                }
+            }
+            "#,
+        );
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs
index 5f626d29571..1b0c3139353 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/pull_assignment_up.rs
@@ -1,7 +1,8 @@
 use syntax::{
     AstNode,
-    ast::{self, make},
-    ted,
+    algo::find_node_at_range,
+    ast::{self, syntax_factory::SyntaxFactory},
+    syntax_editor::SyntaxEditor,
 };
 
 use crate::{
@@ -66,33 +67,51 @@ pub(crate) fn pull_assignment_up(acc: &mut Assists, ctx: &AssistContext<'_>) ->
             return None;
         }
     }
-
+    let target = tgt.syntax().text_range();
+
+    let edit_tgt = tgt.syntax().clone_subtree();
+    let assignments: Vec<_> = collector
+        .assignments
+        .into_iter()
+        .filter_map(|(stmt, rhs)| {
+            Some((
+                find_node_at_range::<ast::BinExpr>(
+                    &edit_tgt,
+                    stmt.syntax().text_range() - target.start(),
+                )?,
+                find_node_at_range::<ast::Expr>(
+                    &edit_tgt,
+                    rhs.syntax().text_range() - target.start(),
+                )?,
+            ))
+        })
+        .collect();
+
+    let mut editor = SyntaxEditor::new(edit_tgt);
+    for (stmt, rhs) in assignments {
+        let mut stmt = stmt.syntax().clone();
+        if let Some(parent) = stmt.parent() {
+            if ast::ExprStmt::cast(parent.clone()).is_some() {
+                stmt = parent.clone();
+            }
+        }
+        editor.replace(stmt, rhs.syntax());
+    }
+    let new_tgt_root = editor.finish().new_root().clone();
+    let new_tgt = ast::Expr::cast(new_tgt_root)?;
     acc.add(
         AssistId::refactor_extract("pull_assignment_up"),
         "Pull assignment up",
-        tgt.syntax().text_range(),
+        target,
         move |edit| {
-            let assignments: Vec<_> = collector
-                .assignments
-                .into_iter()
-                .map(|(stmt, rhs)| (edit.make_mut(stmt), rhs.clone_for_update()))
-                .collect();
-
-            let tgt = edit.make_mut(tgt);
-
-            for (stmt, rhs) in assignments {
-                let mut stmt = stmt.syntax().clone();
-                if let Some(parent) = stmt.parent() {
-                    if ast::ExprStmt::cast(parent.clone()).is_some() {
-                        stmt = parent.clone();
-                    }
-                }
-                ted::replace(stmt, rhs.syntax());
-            }
-            let assign_expr = make::expr_assignment(collector.common_lhs, tgt.clone());
-            let assign_stmt = make::expr_stmt(assign_expr);
-
-            ted::replace(tgt.syntax(), assign_stmt.syntax().clone_for_update());
+            let make = SyntaxFactory::with_mappings();
+            let mut editor = edit.make_editor(tgt.syntax());
+            let assign_expr = make.expr_assignment(collector.common_lhs, new_tgt.clone());
+            let assign_stmt = make.expr_stmt(assign_expr.into());
+
+            editor.replace(tgt.syntax(), assign_stmt.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/remove_dbg.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs
index 52ace03f3cf..9356d02706c 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_dbg.rs
@@ -1,8 +1,9 @@
 use itertools::Itertools;
 use syntax::{
-    Edition, NodeOrToken, SyntaxElement, T, TextRange, TextSize,
-    ast::{self, AstNode, AstToken, make},
-    match_ast, ted,
+    Edition, NodeOrToken, SyntaxNode, SyntaxToken, T,
+    ast::{self, AstNode, make},
+    match_ast,
+    syntax_editor::{Position, SyntaxEditor},
 };
 
 use crate::{AssistContext, AssistId, Assists};
@@ -40,21 +41,23 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
 
     let replacements =
         macro_calls.into_iter().filter_map(compute_dbg_replacement).collect::<Vec<_>>();
-
-    acc.add(
-        AssistId::quick_fix("remove_dbg"),
-        "Remove dbg!()",
-        replacements.iter().map(|&(range, _)| range).reduce(|acc, range| acc.cover(range))?,
-        |builder| {
-            for (range, expr) in replacements {
-                if let Some(expr) = expr {
-                    builder.replace(range, expr.to_string());
-                } else {
-                    builder.delete(range);
-                }
+    let target = replacements
+        .iter()
+        .flat_map(|(node_or_token, _)| node_or_token.iter())
+        .map(|t| t.text_range())
+        .reduce(|acc, range| acc.cover(range))?;
+    acc.add(AssistId::quick_fix("remove_dbg"), "Remove dbg!()", target, |builder| {
+        let mut editor = builder.make_editor(ctx.source_file().syntax());
+        for (range, expr) in replacements {
+            if let Some(expr) = expr {
+                editor.insert(Position::before(range[0].clone()), expr.syntax().clone_for_update());
+            }
+            for node_or_token in range {
+                editor.delete(node_or_token);
             }
-        },
-    )
+        }
+        builder.add_file_edits(ctx.vfs_file_id(), editor);
+    })
 }
 
 /// Returns `None` when either
@@ -63,7 +66,9 @@ pub(crate) fn remove_dbg(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<(
 /// - (`macro_expr` has no parent - is that possible?)
 ///
 /// Returns `Some(_, None)` when the macro call should just be removed.
-fn compute_dbg_replacement(macro_expr: ast::MacroExpr) -> Option<(TextRange, Option<ast::Expr>)> {
+fn compute_dbg_replacement(
+    macro_expr: ast::MacroExpr,
+) -> Option<(Vec<NodeOrToken<SyntaxNode, SyntaxToken>>, Option<ast::Expr>)> {
     let macro_call = macro_expr.macro_call()?;
     let tt = macro_call.token_tree()?;
     let r_delim = NodeOrToken::Token(tt.right_delimiter_token()?);
@@ -88,22 +93,22 @@ fn compute_dbg_replacement(macro_expr: ast::MacroExpr) -> Option<(TextRange, Opt
             match_ast! {
                 match parent {
                     ast::StmtList(_) => {
-                        let range = macro_expr.syntax().text_range();
-                        let range = match whitespace_start(macro_expr.syntax().prev_sibling_or_token()) {
-                            Some(start) => range.cover_offset(start),
-                            None => range,
-                        };
-                        (range, None)
+                        let mut replace = vec![macro_expr.syntax().clone().into()];
+                        if let Some(prev_sibling) = macro_expr.syntax().prev_sibling_or_token()
+                            && prev_sibling.kind() == syntax::SyntaxKind::WHITESPACE {
+                                replace.push(prev_sibling);
+                        }
+                        (replace, None)
                     },
                     ast::ExprStmt(it) => {
-                        let range = it.syntax().text_range();
-                        let range = match whitespace_start(it.syntax().prev_sibling_or_token()) {
-                            Some(start) => range.cover_offset(start),
-                            None => range,
-                        };
-                        (range, None)
+                        let mut replace = vec![it.syntax().clone().into()];
+                        if let Some(prev_sibling) = it.syntax().prev_sibling_or_token()
+                            && prev_sibling.kind() == syntax::SyntaxKind::WHITESPACE {
+                                replace.push(prev_sibling);
+                        }
+                        (replace, None)
                     },
-                    _ => (macro_call.syntax().text_range(), Some(make::ext::expr_unit())),
+                    _ => (vec![macro_call.syntax().clone().into()], Some(make::ext::expr_unit())),
                 }
             }
         }
@@ -147,13 +152,13 @@ fn compute_dbg_replacement(macro_expr: ast::MacroExpr) -> Option<(TextRange, Opt
             };
             let expr = replace_nested_dbgs(expr.clone());
             let expr = if wrap { make::expr_paren(expr).into() } else { expr.clone_subtree() };
-            (macro_call.syntax().text_range(), Some(expr))
+            (vec![macro_call.syntax().clone().into()], Some(expr))
         }
         // dbg!(expr0, expr1, ...)
         exprs => {
             let exprs = exprs.iter().cloned().map(replace_nested_dbgs);
             let expr = make::expr_tuple(exprs);
-            (macro_call.syntax().text_range(), Some(expr.into()))
+            (vec![macro_call.syntax().clone().into()], Some(expr.into()))
         }
     })
 }
@@ -178,8 +183,8 @@ fn replace_nested_dbgs(expanded: ast::Expr) -> ast::Expr {
         return replaced;
     }
 
-    let expanded = expanded.clone_for_update();
-
+    let expanded = expanded.clone_subtree();
+    let mut editor = SyntaxEditor::new(expanded.syntax().clone());
     // We need to collect to avoid mutation during traversal.
     let macro_exprs: Vec<_> =
         expanded.syntax().descendants().filter_map(ast::MacroExpr::cast).collect();
@@ -191,17 +196,13 @@ fn replace_nested_dbgs(expanded: ast::Expr) -> ast::Expr {
         };
 
         if let Some(expr) = expr_opt {
-            ted::replace(mac.syntax(), expr.syntax().clone_for_update());
+            editor.replace(mac.syntax(), expr.syntax().clone_for_update());
         } else {
-            ted::remove(mac.syntax());
+            editor.delete(mac.syntax());
         }
     }
-
-    expanded
-}
-
-fn whitespace_start(it: Option<SyntaxElement>) -> Option<TextSize> {
-    Some(it?.into_token().and_then(ast::Whitespace::cast)?.syntax().text_range().start())
+    let expanded_syntax = editor.finish().new_root().clone();
+    ast::Expr::cast(expanded_syntax).unwrap()
 }
 
 #[cfg(test)]
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 62914ee7f38..5ef8ba46b9e 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
@@ -64,13 +64,12 @@ pub(crate) fn replace_is_method_with_if_let_method(
                     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() {
-                                let placeholder = edit.make_placeholder_snippet(cap);
-                                editor.add_annotation(first_var.syntax(), placeholder);
-                            }
-                        }
+                    if let Some(cap) = ctx.config.snippet_cap
+                        && let Some(ast::Pat::TupleStructPat(pat)) = let_expr.pat()
+                        && let Some(first_var) = pat.fields().next()
+                    {
+                        let placeholder = edit.make_placeholder_snippet(cap);
+                        editor.add_annotation(first_var.syntax(), placeholder);
                     }
 
                     editor.replace(call_expr.syntax(), let_expr.syntax());
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
index c2604432032..cde0d875e0d 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/lib.rs
@@ -172,6 +172,7 @@ mod handlers {
     mod generate_is_empty_from_len;
     mod generate_mut_trait_impl;
     mod generate_new;
+    mod generate_single_field_struct_from;
     mod generate_trait_from_impl;
     mod inline_call;
     mod inline_const_as_literal;
@@ -305,6 +306,7 @@ mod handlers {
             generate_mut_trait_impl::generate_mut_trait_impl,
             generate_new::generate_new,
             generate_trait_from_impl::generate_trait_from_impl,
+            generate_single_field_struct_from::generate_single_field_struct_from,
             inline_call::inline_call,
             inline_call::inline_into_callers,
             inline_const_as_literal::inline_const_as_literal,
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
index 72f7195cbd7..fc1c6928ff3 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/tests/generated.rs
@@ -1933,7 +1933,7 @@ pub enum Axis { X = 0, Y = 1, Z = 2 }
 
 $0impl<T> core::ops::IndexMut<Axis> for [T; 3] {
     fn index_mut(&mut self, index: Axis) -> &mut Self::Output {
-        &self[index as usize]
+        &mut self[index as usize]
     }
 }
 
@@ -1995,6 +1995,34 @@ impl Person {
 }
 
 #[test]
+fn doctest_generate_single_field_struct_from() {
+    check_doc_test(
+        "generate_single_field_struct_from",
+        r#####"
+//- minicore: from, phantom_data
+use core::marker::PhantomData;
+struct $0Foo<T> {
+    id: i32,
+    _phantom_data: PhantomData<T>,
+}
+"#####,
+        r#####"
+use core::marker::PhantomData;
+struct Foo<T> {
+    id: i32,
+    _phantom_data: PhantomData<T>,
+}
+
+impl<T> From<i32> for Foo<T> {
+    fn from(id: i32) -> Self {
+        Self { id, _phantom_data: PhantomData }
+    }
+}
+"#####,
+    )
+}
+
+#[test]
 fn doctest_generate_trait_from_impl() {
     check_doc_test(
         "generate_trait_from_impl",
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 87a4c2ef758..2c8cb6e4d91 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
@@ -594,12 +594,10 @@ fn generate_impl_text_inner(
     let generic_params = adt.generic_param_list().map(|generic_params| {
         let lifetime_params =
             generic_params.lifetime_params().map(ast::GenericParam::LifetimeParam);
-        let ty_or_const_params = generic_params.type_or_const_params().map(|param| {
-            match param {
+        let ty_or_const_params = generic_params.type_or_const_params().filter_map(|param| {
+            let param = match param {
                 ast::TypeOrConstParam::Type(param) => {
-                    let param = param.clone_for_update();
                     // remove defaults since they can't be specified in impls
-                    param.remove_default();
                     let mut bounds =
                         param.type_bound_list().map_or_else(Vec::new, |it| it.bounds().collect());
                     if let Some(trait_) = trait_text {
@@ -610,17 +608,16 @@ fn generate_impl_text_inner(
                         }
                     };
                     // `{ty_param}: {bounds}`
-                    let param =
-                        make::type_param(param.name().unwrap(), make::type_bound_list(bounds));
+                    let param = make::type_param(param.name()?, make::type_bound_list(bounds));
                     ast::GenericParam::TypeParam(param)
                 }
                 ast::TypeOrConstParam::Const(param) => {
-                    let param = param.clone_for_update();
                     // remove defaults since they can't be specified in impls
-                    param.remove_default();
+                    let param = make::const_param(param.name()?, param.ty()?);
                     ast::GenericParam::ConstParam(param)
                 }
-            }
+            };
+            Some(param)
         });
 
         make::generic_param_list(itertools::chain(lifetime_params, ty_or_const_params))
@@ -695,12 +692,10 @@ fn generate_impl_inner(
     let generic_params = adt.generic_param_list().map(|generic_params| {
         let lifetime_params =
             generic_params.lifetime_params().map(ast::GenericParam::LifetimeParam);
-        let ty_or_const_params = generic_params.type_or_const_params().map(|param| {
-            match param {
+        let ty_or_const_params = generic_params.type_or_const_params().filter_map(|param| {
+            let param = match param {
                 ast::TypeOrConstParam::Type(param) => {
-                    let param = param.clone_for_update();
                     // remove defaults since they can't be specified in impls
-                    param.remove_default();
                     let mut bounds =
                         param.type_bound_list().map_or_else(Vec::new, |it| it.bounds().collect());
                     if let Some(trait_) = &trait_ {
@@ -711,17 +706,16 @@ fn generate_impl_inner(
                         }
                     };
                     // `{ty_param}: {bounds}`
-                    let param =
-                        make::type_param(param.name().unwrap(), make::type_bound_list(bounds));
+                    let param = make::type_param(param.name()?, make::type_bound_list(bounds));
                     ast::GenericParam::TypeParam(param)
                 }
                 ast::TypeOrConstParam::Const(param) => {
-                    let param = param.clone_for_update();
                     // remove defaults since they can't be specified in impls
-                    param.remove_default();
+                    let param = make::const_param(param.name()?, param.ty()?);
                     ast::GenericParam::ConstParam(param)
                 }
-            }
+            };
+            Some(param)
         });
 
         make::generic_param_list(itertools::chain(lifetime_params, ty_or_const_params))
@@ -749,16 +743,23 @@ fn generate_impl_inner(
     .clone_for_update();
 
     // Copy any cfg attrs from the original adt
-    let cfg_attrs = adt
-        .attrs()
-        .filter(|attr| attr.as_simple_call().map(|(name, _arg)| name == "cfg").unwrap_or(false));
-    for attr in cfg_attrs {
-        impl_.add_attr(attr.clone_for_update());
-    }
+    add_cfg_attrs_to(adt, &impl_);
 
     impl_
 }
 
+pub(crate) fn add_cfg_attrs_to<T, U>(from: &T, to: &U)
+where
+    T: HasAttrs,
+    U: AttrsOwnerEdit,
+{
+    let cfg_attrs =
+        from.attrs().filter(|attr| attr.as_simple_call().is_some_and(|(name, _arg)| name == "cfg"));
+    for attr in cfg_attrs {
+        to.add_attr(attr.clone_for_update());
+    }
+}
+
 pub(crate) fn add_method_to_adt(
     builder: &mut SourceChangeBuilder,
     adt: &ast::Adt,
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
index 092219a058a..975c2f02259 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/item_list/trait_impl.rs
@@ -37,6 +37,7 @@ use ide_db::{
     SymbolKind, documentation::HasDocs, path_transform::PathTransform,
     syntax_helpers::prettify_macro_expansion, traits::get_missing_assoc_items,
 };
+use syntax::ast::HasGenericParams;
 use syntax::{
     AstNode, SmolStr, SyntaxElement, SyntaxKind, T, TextRange, ToSmolStr,
     ast::{self, HasGenericArgs, HasTypeBounds, edit_in_place::AttrsOwnerEdit, make},
@@ -390,6 +391,12 @@ fn add_type_alias_impl(
             } else if let Some(end) = transformed_ty.eq_token().map(|tok| tok.text_range().start())
             {
                 end
+            } else if let Some(end) = transformed_ty
+                .where_clause()
+                .and_then(|wc| wc.where_token())
+                .map(|tok| tok.text_range().start())
+            {
+                end
             } else if let Some(end) =
                 transformed_ty.semicolon_token().map(|tok| tok.text_range().start())
             {
@@ -400,17 +407,29 @@ fn add_type_alias_impl(
 
             let len = end - start;
             let mut decl = transformed_ty.syntax().text().slice(..len).to_string();
-            if !decl.ends_with(' ') {
-                decl.push(' ');
-            }
-            decl.push_str("= ");
+            decl.truncate(decl.trim_end().len());
+            decl.push_str(" = ");
+
+            let wc = transformed_ty
+                .where_clause()
+                .map(|wc| {
+                    let ws = wc
+                        .where_token()
+                        .and_then(|it| it.prev_token())
+                        .filter(|token| token.kind() == SyntaxKind::WHITESPACE)
+                        .map(|token| token.to_string())
+                        .unwrap_or_else(|| " ".into());
+                    format!("{ws}{wc}")
+                })
+                .unwrap_or_default();
 
             match ctx.config.snippet_cap {
                 Some(cap) => {
-                    let snippet = format!("{decl}$0;");
+                    let snippet = format!("{decl}$0{wc};");
                     item.snippet_edit(cap, TextEdit::replace(replacement_range, snippet));
                 }
                 None => {
+                    decl.push_str(&wc);
                     item.text_edit(TextEdit::replace(replacement_range, decl));
                 }
             };
@@ -1440,6 +1459,30 @@ impl<'b> Tr<'b> for () {
 "#,
         );
     }
+    #[test]
+    fn includes_where_clause() {
+        check_edit(
+            "type Ty",
+            r#"
+trait Tr {
+    type Ty where Self: Copy;
+}
+
+impl Tr for () {
+    $0
+}
+"#,
+            r#"
+trait Tr {
+    type Ty where Self: Copy;
+}
+
+impl Tr for () {
+    type Ty = $0 where Self: Copy;
+}
+"#,
+        );
+    }
 
     #[test]
     fn strips_comments() {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs
index 179d6693602..ac32649d4ff 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/item_list.rs
@@ -458,6 +458,33 @@ type O = $0;
         r"
 struct A;
 trait B {
+type O<'a>
+where
+Self: 'a;
+}
+impl B for A {
+$0
+}
+",
+        r#"
+struct A;
+trait B {
+type O<'a>
+where
+Self: 'a;
+}
+impl B for A {
+type O<'a> = $0
+where
+Self: 'a;
+}
+"#,
+    );
+    check_edit(
+        "type O",
+        r"
+struct A;
+trait B {
 type O: ?Sized = u32;
 }
 impl B for A {
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt
index de046e70c67..973256c470f 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt
+++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt
@@ -11,6 +11,40 @@
         },
         [
             FileSymbol {
+                name: "A",
+                def: Variant(
+                    Variant {
+                        id: EnumVariantId(
+                            7800,
+                        ),
+                    },
+                ),
+                loc: DeclarationLocation {
+                    hir_file_id: FileId(
+                        EditionedFileId(
+                            Id(2000),
+                        ),
+                    ),
+                    ptr: SyntaxNodePtr {
+                        kind: VARIANT,
+                        range: 201..202,
+                    },
+                    name_ptr: AstPtr(
+                        SyntaxNodePtr {
+                            kind: NAME,
+                            range: 201..202,
+                        },
+                    ),
+                },
+                container_name: Some(
+                    "Enum",
+                ),
+                is_alias: false,
+                is_assoc: true,
+                is_import: false,
+                do_not_complete: Yes,
+            },
+            FileSymbol {
                 name: "Alias",
                 def: TypeAlias(
                     TypeAlias {
@@ -43,6 +77,40 @@
                 do_not_complete: Yes,
             },
             FileSymbol {
+                name: "B",
+                def: Variant(
+                    Variant {
+                        id: EnumVariantId(
+                            7801,
+                        ),
+                    },
+                ),
+                loc: DeclarationLocation {
+                    hir_file_id: FileId(
+                        EditionedFileId(
+                            Id(2000),
+                        ),
+                    ),
+                    ptr: SyntaxNodePtr {
+                        kind: VARIANT,
+                        range: 204..205,
+                    },
+                    name_ptr: AstPtr(
+                        SyntaxNodePtr {
+                            kind: NAME,
+                            range: 204..205,
+                        },
+                    ),
+                },
+                container_name: Some(
+                    "Enum",
+                ),
+                is_alias: false,
+                is_assoc: true,
+                is_import: false,
+                do_not_complete: Yes,
+            },
+            FileSymbol {
                 name: "CONST",
                 def: Const(
                     Const {
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs
index 06f35759420..7402133f747 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_generics_len.rs
@@ -183,4 +183,28 @@ fn main() {
 "#,
         );
     }
+
+    #[test]
+    fn generic_assoc_type_infer_lifetime_in_expr_position() {
+        check_diagnostics(
+            r#"
+//- minicore: sized
+struct Player;
+
+struct Foo<'c, C> {
+    _v: &'c C,
+}
+trait WithSignals: Sized {
+    type SignalCollection<'c, C>;
+    fn __signals_from_external(&self) -> Self::SignalCollection<'_, Self>;
+}
+impl WithSignals for Player {
+    type SignalCollection<'c, C> = Foo<'c, C>;
+    fn __signals_from_external(&self) -> Self::SignalCollection<'_, Self> {
+        Self::SignalCollection { _v: self }
+    }
+}
+        "#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
index d8f6e813d80..17caf630182 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_unsafe.rs
@@ -983,4 +983,19 @@ fn test() {
             "#,
         );
     }
+
+    #[test]
+    fn naked_asm_is_safe() {
+        check_diagnostics(
+            r#"
+#[rustc_builtin_macro]
+macro_rules! naked_asm { () => {} }
+
+#[unsafe(naked)]
+extern "C" fn naked() {
+    naked_asm!("");
+}
+        "#,
+        );
+    }
 }
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 f58202a4213..a5d9a10d2e5 100644
--- a/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/doc_links.rs
@@ -505,7 +505,7 @@ fn map_links<'e>(
         Event::End(Tag::Link(link_type, target, _)) => {
             in_link = false;
             Event::End(Tag::Link(
-                end_link_type.unwrap_or(link_type),
+                end_link_type.take().unwrap_or(link_type),
                 end_link_target.take().unwrap_or(target),
                 CowStr::Borrowed(""),
             ))
@@ -514,7 +514,7 @@ fn map_links<'e>(
             let (link_type, link_target_s, link_name) =
                 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)) {
+            if !matches!(end_link_type, Some(LinkType::Autolink)) && link_type.is_some() {
                 end_link_type = link_type;
             }
             Event::Text(CowStr::Boxed(link_name.into()))
@@ -523,7 +523,7 @@ fn map_links<'e>(
             let (link_type, link_target_s, link_name) =
                 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)) {
+            if !matches!(end_link_type, Some(LinkType::Autolink)) && link_type.is_some() {
                 end_link_type = link_type;
             }
             Event::Code(CowStr::Boxed(link_name.into()))
diff --git a/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs b/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs
index 9bd8504733a..c081796d078 100755
--- a/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/folding_ranges.rs
@@ -23,6 +23,7 @@ pub enum FoldKind {
     WhereClause,
     ReturnType,
     MatchArm,
+    Function,
     // region: item runs
     Modules,
     Consts,
@@ -47,6 +48,7 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
     let mut res = vec![];
     let mut visited_comments = FxHashSet::default();
     let mut visited_nodes = FxHashSet::default();
+    let mut merged_fn_bodies = FxHashSet::default();
 
     // regions can be nested, here is a LIFO buffer
     let mut region_starts: Vec<TextSize> = vec![];
@@ -59,6 +61,32 @@ pub(crate) fn folding_ranges(file: &SourceFile) -> Vec<Fold> {
                 NodeOrToken::Token(token) => token.text().contains('\n'),
             };
             if is_multiline {
+                // for the func with multiline param list
+                if matches!(element.kind(), FN) {
+                    if let NodeOrToken::Node(node) = &element {
+                        if let Some(fn_node) = ast::Fn::cast(node.clone()) {
+                            if !fn_node
+                                .param_list()
+                                .map(|param_list| param_list.syntax().text().contains_char('\n'))
+                                .unwrap_or(false)
+                            {
+                                continue;
+                            }
+
+                            if let Some(body) = fn_node.body() {
+                                res.push(Fold {
+                                    range: TextRange::new(
+                                        node.text_range().start(),
+                                        node.text_range().end(),
+                                    ),
+                                    kind: FoldKind::Function,
+                                });
+                                merged_fn_bodies.insert(body.syntax().text_range());
+                                continue;
+                            }
+                        }
+                    }
+                }
                 res.push(Fold { range: element.text_range(), kind });
                 continue;
             }
@@ -152,6 +180,7 @@ fn fold_kind(kind: SyntaxKind) -> Option<FoldKind> {
         ARG_LIST | PARAM_LIST | GENERIC_ARG_LIST | GENERIC_PARAM_LIST => Some(FoldKind::ArgList),
         ARRAY_EXPR => Some(FoldKind::Array),
         RET_TYPE => Some(FoldKind::ReturnType),
+        FN => Some(FoldKind::Function),
         WHERE_CLAUSE => Some(FoldKind::WhereClause),
         ASSOC_ITEM_LIST
         | RECORD_FIELD_LIST
@@ -291,6 +320,7 @@ mod tests {
 
     use super::*;
 
+    #[track_caller]
     fn check(#[rust_analyzer::rust_fixture] ra_fixture: &str) {
         let (ranges, text) = extract_tags(ra_fixture, "fold");
 
@@ -322,6 +352,7 @@ mod tests {
                 FoldKind::WhereClause => "whereclause",
                 FoldKind::ReturnType => "returntype",
                 FoldKind::MatchArm => "matcharm",
+                FoldKind::Function => "function",
                 FoldKind::TraitAliases => "traitaliases",
                 FoldKind::ExternCrates => "externcrates",
             };
@@ -330,6 +361,23 @@ mod tests {
     }
 
     #[test]
+    fn test_fold_func_with_multiline_param_list() {
+        check(
+            r#"
+<fold function>fn func<fold arglist>(
+    a: i32,
+    b: i32,
+    c: i32,
+)</fold> <fold block>{
+
+
+
+}</fold></fold>
+"#,
+        );
+    }
+
+    #[test]
     fn test_fold_comments() {
         check(
             r#"
@@ -541,10 +589,10 @@ const _: S = S <fold block>{
     fn fold_multiline_params() {
         check(
             r#"
-fn foo<fold arglist>(
+<fold function>fn foo<fold arglist>(
     x: i32,
     y: String,
-)</fold> {}
+)</fold> {}</fold>
 "#,
         )
     }
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 f63499aa0fd..c5480217a91 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
@@ -10958,3 +10958,68 @@ fn bar$0() -> Foo {
         "#]],
     );
 }
+
+#[test]
+fn regression_20190() {
+    check(
+        r#"
+struct Foo;
+
+/// [`foo` bar](Foo).
+fn has_docs$0() {}
+    "#,
+        expect![[r#"
+            *has_docs*
+
+            ```rust
+            ra_test_fixture
+            ```
+
+            ```rust
+            fn has_docs()
+            ```
+
+            ---
+
+            [`foo` bar](https://docs.rs/ra_test_fixture/*/ra_test_fixture/struct.Foo.html).
+        "#]],
+    );
+}
+
+#[test]
+fn regression_20225() {
+    check(
+        r#"
+//- minicore: coerce_unsized
+trait Trait {
+    type Type<'a, T: ?Sized + 'a>;
+}
+
+enum Borrowed {}
+
+impl Trait for Borrowed {
+    type Type<'a, T: ?Sized + 'a> = &'a T;
+}
+
+enum Enum<'a, T: Trait + 'a> {
+    Variant1(T::Type<'a, [Enum<'a, T>]>),
+    Variant2,
+}
+
+impl Enum<'_, Borrowed> {
+    const CONSTANT$0: Self = Self::Variant1(&[Self::Variant2]);
+}
+    "#,
+        expect![[r#"
+            *CONSTANT*
+
+            ```rust
+            ra_test_fixture::Enum
+            ```
+
+            ```rust
+            const CONSTANT: Self = Variant1(&[Variant2])
+            ```
+        "#]],
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs
index bf4688e9d82..d0539abe282 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implicit_drop.rs
@@ -92,7 +92,7 @@ pub(super) fn hints(
                 },
                 MirSpan::Unknown => continue,
             };
-            let binding = &hir.bindings[binding_idx];
+            let binding = &hir[binding_idx];
             let name = binding.name.display_no_db(display_target.edition).to_smolstr();
             if name.starts_with("<ra@") {
                 continue; // Ignore desugared variables
diff --git a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implied_dyn_trait.rs b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implied_dyn_trait.rs
index cd01c075832..0da1785234a 100644
--- a/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implied_dyn_trait.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/inlay_hints/implied_dyn_trait.rs
@@ -17,8 +17,12 @@ pub(super) fn hints(
     let parent = path.syntax().parent()?;
     let range = match path {
         Either::Left(path) => {
-            let paren =
-                parent.ancestors().take_while(|it| ast::ParenType::can_cast(it.kind())).last();
+            let paren = parent
+                .ancestors()
+                .take_while(|it| {
+                    ast::ParenType::can_cast(it.kind()) || ast::ForType::can_cast(it.kind())
+                })
+                .last();
             let parent = paren.as_ref().and_then(|it| it.parent()).unwrap_or(parent);
             if ast::TypeBound::can_cast(parent.kind())
                 || ast::TypeAnchor::can_cast(parent.kind())
@@ -34,7 +38,7 @@ pub(super) fn hints(
                 return None;
             }
             sema.resolve_trait(&path.path()?)?;
-            paren.map_or_else(|| path.syntax().text_range(), |it| it.text_range())
+            path.syntax().text_range()
         }
         Either::Right(dyn_) => {
             if dyn_.dyn_token().is_some() {
@@ -89,7 +93,7 @@ fn foo(_: &T,  _: for<'a> T) {}
 impl T {}
   // ^ dyn
 impl T for (T) {}
-        // ^^^ dyn
+         // ^ dyn
 impl T
 "#,
         );
@@ -112,7 +116,7 @@ fn foo(
     _: &mut (T + T)
     //       ^^^^^ dyn
     _: *mut (T),
-    //      ^^^ dyn
+    //       ^ dyn
 ) {}
 "#,
         );
@@ -136,4 +140,26 @@ fn foo(
             "#]],
         );
     }
+
+    #[test]
+    fn hrtb_bound_does_not_add_dyn() {
+        check(
+            r#"
+//- minicore: fn
+fn test<F>(f: F) where F: for<'a> FnOnce(&'a i32) {}
+     // ^: Sized
+        "#,
+        );
+    }
+
+    #[test]
+    fn with_parentheses() {
+        check(
+            r#"
+trait T {}
+fn foo(v: &(T)) {}
+         // ^ dyn
+        "#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs
index 0ac25da3294..2b4151e3b75 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions.rs
@@ -4,7 +4,7 @@ use crate::grammar::attributes::ATTRIBUTE_FIRST;
 
 use super::*;
 
-pub(super) use atom::{EXPR_RECOVERY_SET, LITERAL_FIRST, literal};
+pub(super) use atom::{EXPR_RECOVERY_SET, LITERAL_FIRST, literal, parse_asm_expr};
 pub(crate) use atom::{block_expr, match_arm_list};
 
 #[derive(PartialEq, Eq)]
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs
index 8ed0fc6729f..76656567e7f 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/expressions/atom.rs
@@ -253,8 +253,7 @@ fn builtin_expr(p: &mut Parser<'_>) -> Option<CompletedMarker> {
     let m = p.start();
     p.bump_remap(T![builtin]);
     p.bump(T![#]);
-    if p.at_contextual_kw(T![offset_of]) {
-        p.bump_remap(T![offset_of]);
+    if p.eat_contextual_kw(T![offset_of]) {
         p.expect(T!['(']);
         type_(p);
         p.expect(T![,]);
@@ -278,8 +277,7 @@ fn builtin_expr(p: &mut Parser<'_>) -> Option<CompletedMarker> {
             p.expect(T![')']);
         }
         Some(m.complete(p, OFFSET_OF_EXPR))
-    } else if p.at_contextual_kw(T![format_args]) {
-        p.bump_remap(T![format_args]);
+    } else if p.eat_contextual_kw(T![format_args]) {
         p.expect(T!['(']);
         expr(p);
         if p.eat(T![,]) {
@@ -302,7 +300,16 @@ fn builtin_expr(p: &mut Parser<'_>) -> Option<CompletedMarker> {
         }
         p.expect(T![')']);
         Some(m.complete(p, FORMAT_ARGS_EXPR))
-    } else if p.at_contextual_kw(T![asm]) {
+    } else if p.eat_contextual_kw(T![asm])
+        || p.eat_contextual_kw(T![global_asm])
+        || p.eat_contextual_kw(T![naked_asm])
+    {
+        // test asm_kinds
+        // fn foo() {
+        //     builtin#asm("");
+        //     builtin#global_asm("");
+        //     builtin#naked_asm("");
+        // }
         parse_asm_expr(p, m)
     } else {
         m.abandon(p);
@@ -321,8 +328,7 @@ fn builtin_expr(p: &mut Parser<'_>) -> Option<CompletedMarker> {
 //         tmp = out(reg) _,
 //     );
 // }
-fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option<CompletedMarker> {
-    p.bump_remap(T![asm]);
+pub(crate) fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option<CompletedMarker> {
     p.expect(T!['(']);
     if expr(p).is_none() {
         p.err_and_bump("expected asm template");
@@ -411,11 +417,10 @@ fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option<CompletedMarker> {
             dir_spec.abandon(p);
             op.abandon(p);
             op_n.abandon(p);
-            p.err_and_bump("expected asm operand");
 
-            // improves error recovery and handles err_and_bump recovering from `{` which gets
-            // the parser stuck here
+            // improves error recovery
             if p.at(T!['{']) {
+                p.error("expected asm operand");
                 // test_err bad_asm_expr
                 // fn foo() {
                 //     builtin#asm(
@@ -423,6 +428,8 @@ fn parse_asm_expr(p: &mut Parser<'_>, m: Marker) -> Option<CompletedMarker> {
                 //     );
                 // }
                 expr(p);
+            } else {
+                p.err_and_bump("expected asm operand");
             }
 
             if p.at(T!['}']) {
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs
index b9f4866574a..8e551b0b961 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/items.rs
@@ -261,6 +261,19 @@ fn opt_item_without_modifiers(p: &mut Parser<'_>, m: Marker) -> Result<(), Marke
         T![const] if (la == IDENT || la == T![_] || la == T![mut]) => consts::konst(p, m),
         T![static] if (la == IDENT || la == T![_] || la == T![mut]) => consts::static_(p, m),
 
+        IDENT
+            if p.at_contextual_kw(T![builtin])
+                && p.nth_at(1, T![#])
+                && p.nth_at_contextual_kw(2, T![global_asm]) =>
+        {
+            p.bump_remap(T![builtin]);
+            p.bump(T![#]);
+            p.bump_remap(T![global_asm]);
+            // test global_asm
+            // builtin#global_asm("")
+            expressions::parse_asm_expr(p, m);
+        }
+
         _ => return Err(m),
     };
     Ok(())
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 bff9acd78fa..8fff1c3db74 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::{
-    unescape_byte, unescape_byte_str, unescape_c_str, unescape_char, unescape_str, EscapeError,
-    Mode,
+    EscapeError, Mode, unescape_byte, unescape_byte_str, unescape_c_str, unescape_char,
+    unescape_str,
 };
 
 use crate::{
diff --git a/src/tools/rust-analyzer/crates/parser/src/parser.rs b/src/tools/rust-analyzer/crates/parser/src/parser.rs
index 36a363afe93..ca02d9fdfde 100644
--- a/src/tools/rust-analyzer/crates/parser/src/parser.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/parser.rs
@@ -29,7 +29,7 @@ pub(crate) struct Parser<'t> {
     edition: Edition,
 }
 
-const PARSER_STEP_LIMIT: usize = 15_000_000;
+const PARSER_STEP_LIMIT: usize = if cfg!(debug_assertions) { 150_000 } else { 15_000_000 };
 
 impl<'t> Parser<'t> {
     pub(super) fn new(inp: &'t Input, edition: Edition) -> Parser<'t> {
@@ -254,7 +254,10 @@ impl<'t> Parser<'t> {
 
     /// Create an error node and consume the next token.
     pub(crate) fn err_and_bump(&mut self, message: &str) {
-        self.err_recover(message, TokenSet::EMPTY);
+        let m = self.start();
+        self.error(message);
+        self.bump_any();
+        m.complete(self, ERROR);
     }
 
     /// Create an error node and consume the next token unless it is in the recovery set.
diff --git a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
index f534546ea07..12a13caa4d9 100644
--- a/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/syntax_kind/generated.rs
@@ -120,12 +120,14 @@ pub enum SyntaxKind {
     DYN_KW,
     FORMAT_ARGS_KW,
     GEN_KW,
+    GLOBAL_ASM_KW,
     INLATEOUT_KW,
     INOUT_KW,
     LABEL_KW,
     LATEOUT_KW,
     MACRO_RULES_KW,
     MAY_UNWIND_KW,
+    NAKED_ASM_KW,
     NOMEM_KW,
     NORETURN_KW,
     NOSTACK_KW,
@@ -599,12 +601,14 @@ impl SyntaxKind {
             DEFAULT_KW => "default",
             DYN_KW => "dyn",
             FORMAT_ARGS_KW => "format_args",
+            GLOBAL_ASM_KW => "global_asm",
             INLATEOUT_KW => "inlateout",
             INOUT_KW => "inout",
             LABEL_KW => "label",
             LATEOUT_KW => "lateout",
             MACRO_RULES_KW => "macro_rules",
             MAY_UNWIND_KW => "may_unwind",
+            NAKED_ASM_KW => "naked_asm",
             NOMEM_KW => "nomem",
             NORETURN_KW => "noreturn",
             NOSTACK_KW => "nostack",
@@ -699,12 +703,14 @@ impl SyntaxKind {
             DEFAULT_KW => true,
             DYN_KW if edition < Edition::Edition2018 => true,
             FORMAT_ARGS_KW => true,
+            GLOBAL_ASM_KW => true,
             INLATEOUT_KW => true,
             INOUT_KW => true,
             LABEL_KW => true,
             LATEOUT_KW => true,
             MACRO_RULES_KW => true,
             MAY_UNWIND_KW => true,
+            NAKED_ASM_KW => true,
             NOMEM_KW => true,
             NORETURN_KW => true,
             NOSTACK_KW => true,
@@ -787,12 +793,14 @@ impl SyntaxKind {
             DEFAULT_KW => true,
             DYN_KW if edition < Edition::Edition2018 => true,
             FORMAT_ARGS_KW => true,
+            GLOBAL_ASM_KW => true,
             INLATEOUT_KW => true,
             INOUT_KW => true,
             LABEL_KW => true,
             LATEOUT_KW => true,
             MACRO_RULES_KW => true,
             MAY_UNWIND_KW => true,
+            NAKED_ASM_KW => true,
             NOMEM_KW => true,
             NORETURN_KW => true,
             NOSTACK_KW => true,
@@ -938,12 +946,14 @@ impl SyntaxKind {
             "default" => DEFAULT_KW,
             "dyn" if edition < Edition::Edition2018 => DYN_KW,
             "format_args" => FORMAT_ARGS_KW,
+            "global_asm" => GLOBAL_ASM_KW,
             "inlateout" => INLATEOUT_KW,
             "inout" => INOUT_KW,
             "label" => LABEL_KW,
             "lateout" => LATEOUT_KW,
             "macro_rules" => MACRO_RULES_KW,
             "may_unwind" => MAY_UNWIND_KW,
+            "naked_asm" => NAKED_ASM_KW,
             "nomem" => NOMEM_KW,
             "noreturn" => NORETURN_KW,
             "nostack" => NOSTACK_KW,
@@ -998,7 +1008,7 @@ impl SyntaxKind {
     }
 }
 #[macro_export]
-macro_rules ! T_ { [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [Self] => { $ crate :: SyntaxKind :: SELF_TYPE_KW } ; [abstract] => { $ crate :: SyntaxKind :: ABSTRACT_KW } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [become] => { $ crate :: SyntaxKind :: BECOME_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [crate] => { $ crate :: SyntaxKind :: CRATE_KW } ; [do] => { $ crate :: SyntaxKind :: DO_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [enum] => { $ crate :: SyntaxKind :: ENUM_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [final] => { $ crate :: SyntaxKind :: FINAL_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [impl] => { $ crate :: SyntaxKind :: IMPL_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [loop] => { $ crate :: SyntaxKind :: LOOP_KW } ; [macro] => { $ crate :: SyntaxKind :: MACRO_KW } ; [match] => { $ crate :: SyntaxKind :: MATCH_KW } ; [mod] => { $ crate :: SyntaxKind :: MOD_KW } ; [move] => { $ crate :: SyntaxKind :: MOVE_KW } ; [mut] => { $ crate :: SyntaxKind :: MUT_KW } ; [override] => { $ crate :: SyntaxKind :: OVERRIDE_KW } ; [priv] => { $ crate :: SyntaxKind :: PRIV_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [static] => { $ crate :: SyntaxKind :: STATIC_KW } ; [struct] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [trait] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [type] => { $ crate :: SyntaxKind :: TYPE_KW } ; [typeof] => { $ crate :: SyntaxKind :: TYPEOF_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [unsized] => { $ crate :: SyntaxKind :: UNSIZED_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [virtual] => { $ crate :: SyntaxKind :: VIRTUAL_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [yield] => { $ crate :: SyntaxKind :: YIELD_KW } ; [asm] => { $ crate :: SyntaxKind :: ASM_KW } ; [att_syntax] => { $ crate :: SyntaxKind :: ATT_SYNTAX_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [builtin] => { $ crate :: SyntaxKind :: BUILTIN_KW } ; [clobber_abi] => { $ crate :: SyntaxKind :: CLOBBER_ABI_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [format_args] => { $ crate :: SyntaxKind :: FORMAT_ARGS_KW } ; [inlateout] => { $ crate :: SyntaxKind :: INLATEOUT_KW } ; [inout] => { $ crate :: SyntaxKind :: INOUT_KW } ; [label] => { $ crate :: SyntaxKind :: LABEL_KW } ; [lateout] => { $ crate :: SyntaxKind :: LATEOUT_KW } ; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW } ; [may_unwind] => { $ crate :: SyntaxKind :: MAY_UNWIND_KW } ; [nomem] => { $ crate :: SyntaxKind :: NOMEM_KW } ; [noreturn] => { $ crate :: SyntaxKind :: NORETURN_KW } ; [nostack] => { $ crate :: SyntaxKind :: NOSTACK_KW } ; [offset_of] => { $ crate :: SyntaxKind :: OFFSET_OF_KW } ; [options] => { $ crate :: SyntaxKind :: OPTIONS_KW } ; [out] => { $ crate :: SyntaxKind :: OUT_KW } ; [preserves_flags] => { $ crate :: SyntaxKind :: PRESERVES_FLAGS_KW } ; [pure] => { $ crate :: SyntaxKind :: PURE_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [readonly] => { $ crate :: SyntaxKind :: READONLY_KW } ; [safe] => { $ crate :: SyntaxKind :: SAFE_KW } ; [sym] => { $ crate :: SyntaxKind :: SYM_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [yeet] => { $ crate :: SyntaxKind :: YEET_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [gen] => { $ crate :: SyntaxKind :: GEN_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_KW } ; [lifetime_ident] => { $ crate :: SyntaxKind :: LIFETIME_IDENT } ; [int_number] => { $ crate :: SyntaxKind :: INT_NUMBER } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [string] => { $ crate :: SyntaxKind :: STRING } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; [frontmatter] => { $ crate :: SyntaxKind :: FRONTMATTER } ; }
+macro_rules ! T_ { [$] => { $ crate :: SyntaxKind :: DOLLAR } ; [;] => { $ crate :: SyntaxKind :: SEMICOLON } ; [,] => { $ crate :: SyntaxKind :: COMMA } ; ['('] => { $ crate :: SyntaxKind :: L_PAREN } ; [')'] => { $ crate :: SyntaxKind :: R_PAREN } ; ['{'] => { $ crate :: SyntaxKind :: L_CURLY } ; ['}'] => { $ crate :: SyntaxKind :: R_CURLY } ; ['['] => { $ crate :: SyntaxKind :: L_BRACK } ; [']'] => { $ crate :: SyntaxKind :: R_BRACK } ; [<] => { $ crate :: SyntaxKind :: L_ANGLE } ; [>] => { $ crate :: SyntaxKind :: R_ANGLE } ; [@] => { $ crate :: SyntaxKind :: AT } ; [#] => { $ crate :: SyntaxKind :: POUND } ; [~] => { $ crate :: SyntaxKind :: TILDE } ; [?] => { $ crate :: SyntaxKind :: QUESTION } ; [&] => { $ crate :: SyntaxKind :: AMP } ; [|] => { $ crate :: SyntaxKind :: PIPE } ; [+] => { $ crate :: SyntaxKind :: PLUS } ; [*] => { $ crate :: SyntaxKind :: STAR } ; [/] => { $ crate :: SyntaxKind :: SLASH } ; [^] => { $ crate :: SyntaxKind :: CARET } ; [%] => { $ crate :: SyntaxKind :: PERCENT } ; [_] => { $ crate :: SyntaxKind :: UNDERSCORE } ; [.] => { $ crate :: SyntaxKind :: DOT } ; [..] => { $ crate :: SyntaxKind :: DOT2 } ; [...] => { $ crate :: SyntaxKind :: DOT3 } ; [..=] => { $ crate :: SyntaxKind :: DOT2EQ } ; [:] => { $ crate :: SyntaxKind :: COLON } ; [::] => { $ crate :: SyntaxKind :: COLON2 } ; [=] => { $ crate :: SyntaxKind :: EQ } ; [==] => { $ crate :: SyntaxKind :: EQ2 } ; [=>] => { $ crate :: SyntaxKind :: FAT_ARROW } ; [!] => { $ crate :: SyntaxKind :: BANG } ; [!=] => { $ crate :: SyntaxKind :: NEQ } ; [-] => { $ crate :: SyntaxKind :: MINUS } ; [->] => { $ crate :: SyntaxKind :: THIN_ARROW } ; [<=] => { $ crate :: SyntaxKind :: LTEQ } ; [>=] => { $ crate :: SyntaxKind :: GTEQ } ; [+=] => { $ crate :: SyntaxKind :: PLUSEQ } ; [-=] => { $ crate :: SyntaxKind :: MINUSEQ } ; [|=] => { $ crate :: SyntaxKind :: PIPEEQ } ; [&=] => { $ crate :: SyntaxKind :: AMPEQ } ; [^=] => { $ crate :: SyntaxKind :: CARETEQ } ; [/=] => { $ crate :: SyntaxKind :: SLASHEQ } ; [*=] => { $ crate :: SyntaxKind :: STAREQ } ; [%=] => { $ crate :: SyntaxKind :: PERCENTEQ } ; [&&] => { $ crate :: SyntaxKind :: AMP2 } ; [||] => { $ crate :: SyntaxKind :: PIPE2 } ; [<<] => { $ crate :: SyntaxKind :: SHL } ; [>>] => { $ crate :: SyntaxKind :: SHR } ; [<<=] => { $ crate :: SyntaxKind :: SHLEQ } ; [>>=] => { $ crate :: SyntaxKind :: SHREQ } ; [Self] => { $ crate :: SyntaxKind :: SELF_TYPE_KW } ; [abstract] => { $ crate :: SyntaxKind :: ABSTRACT_KW } ; [as] => { $ crate :: SyntaxKind :: AS_KW } ; [become] => { $ crate :: SyntaxKind :: BECOME_KW } ; [box] => { $ crate :: SyntaxKind :: BOX_KW } ; [break] => { $ crate :: SyntaxKind :: BREAK_KW } ; [const] => { $ crate :: SyntaxKind :: CONST_KW } ; [continue] => { $ crate :: SyntaxKind :: CONTINUE_KW } ; [crate] => { $ crate :: SyntaxKind :: CRATE_KW } ; [do] => { $ crate :: SyntaxKind :: DO_KW } ; [else] => { $ crate :: SyntaxKind :: ELSE_KW } ; [enum] => { $ crate :: SyntaxKind :: ENUM_KW } ; [extern] => { $ crate :: SyntaxKind :: EXTERN_KW } ; [false] => { $ crate :: SyntaxKind :: FALSE_KW } ; [final] => { $ crate :: SyntaxKind :: FINAL_KW } ; [fn] => { $ crate :: SyntaxKind :: FN_KW } ; [for] => { $ crate :: SyntaxKind :: FOR_KW } ; [if] => { $ crate :: SyntaxKind :: IF_KW } ; [impl] => { $ crate :: SyntaxKind :: IMPL_KW } ; [in] => { $ crate :: SyntaxKind :: IN_KW } ; [let] => { $ crate :: SyntaxKind :: LET_KW } ; [loop] => { $ crate :: SyntaxKind :: LOOP_KW } ; [macro] => { $ crate :: SyntaxKind :: MACRO_KW } ; [match] => { $ crate :: SyntaxKind :: MATCH_KW } ; [mod] => { $ crate :: SyntaxKind :: MOD_KW } ; [move] => { $ crate :: SyntaxKind :: MOVE_KW } ; [mut] => { $ crate :: SyntaxKind :: MUT_KW } ; [override] => { $ crate :: SyntaxKind :: OVERRIDE_KW } ; [priv] => { $ crate :: SyntaxKind :: PRIV_KW } ; [pub] => { $ crate :: SyntaxKind :: PUB_KW } ; [ref] => { $ crate :: SyntaxKind :: REF_KW } ; [return] => { $ crate :: SyntaxKind :: RETURN_KW } ; [self] => { $ crate :: SyntaxKind :: SELF_KW } ; [static] => { $ crate :: SyntaxKind :: STATIC_KW } ; [struct] => { $ crate :: SyntaxKind :: STRUCT_KW } ; [super] => { $ crate :: SyntaxKind :: SUPER_KW } ; [trait] => { $ crate :: SyntaxKind :: TRAIT_KW } ; [true] => { $ crate :: SyntaxKind :: TRUE_KW } ; [type] => { $ crate :: SyntaxKind :: TYPE_KW } ; [typeof] => { $ crate :: SyntaxKind :: TYPEOF_KW } ; [unsafe] => { $ crate :: SyntaxKind :: UNSAFE_KW } ; [unsized] => { $ crate :: SyntaxKind :: UNSIZED_KW } ; [use] => { $ crate :: SyntaxKind :: USE_KW } ; [virtual] => { $ crate :: SyntaxKind :: VIRTUAL_KW } ; [where] => { $ crate :: SyntaxKind :: WHERE_KW } ; [while] => { $ crate :: SyntaxKind :: WHILE_KW } ; [yield] => { $ crate :: SyntaxKind :: YIELD_KW } ; [asm] => { $ crate :: SyntaxKind :: ASM_KW } ; [att_syntax] => { $ crate :: SyntaxKind :: ATT_SYNTAX_KW } ; [auto] => { $ crate :: SyntaxKind :: AUTO_KW } ; [builtin] => { $ crate :: SyntaxKind :: BUILTIN_KW } ; [clobber_abi] => { $ crate :: SyntaxKind :: CLOBBER_ABI_KW } ; [default] => { $ crate :: SyntaxKind :: DEFAULT_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [format_args] => { $ crate :: SyntaxKind :: FORMAT_ARGS_KW } ; [global_asm] => { $ crate :: SyntaxKind :: GLOBAL_ASM_KW } ; [inlateout] => { $ crate :: SyntaxKind :: INLATEOUT_KW } ; [inout] => { $ crate :: SyntaxKind :: INOUT_KW } ; [label] => { $ crate :: SyntaxKind :: LABEL_KW } ; [lateout] => { $ crate :: SyntaxKind :: LATEOUT_KW } ; [macro_rules] => { $ crate :: SyntaxKind :: MACRO_RULES_KW } ; [may_unwind] => { $ crate :: SyntaxKind :: MAY_UNWIND_KW } ; [naked_asm] => { $ crate :: SyntaxKind :: NAKED_ASM_KW } ; [nomem] => { $ crate :: SyntaxKind :: NOMEM_KW } ; [noreturn] => { $ crate :: SyntaxKind :: NORETURN_KW } ; [nostack] => { $ crate :: SyntaxKind :: NOSTACK_KW } ; [offset_of] => { $ crate :: SyntaxKind :: OFFSET_OF_KW } ; [options] => { $ crate :: SyntaxKind :: OPTIONS_KW } ; [out] => { $ crate :: SyntaxKind :: OUT_KW } ; [preserves_flags] => { $ crate :: SyntaxKind :: PRESERVES_FLAGS_KW } ; [pure] => { $ crate :: SyntaxKind :: PURE_KW } ; [raw] => { $ crate :: SyntaxKind :: RAW_KW } ; [readonly] => { $ crate :: SyntaxKind :: READONLY_KW } ; [safe] => { $ crate :: SyntaxKind :: SAFE_KW } ; [sym] => { $ crate :: SyntaxKind :: SYM_KW } ; [union] => { $ crate :: SyntaxKind :: UNION_KW } ; [yeet] => { $ crate :: SyntaxKind :: YEET_KW } ; [async] => { $ crate :: SyntaxKind :: ASYNC_KW } ; [await] => { $ crate :: SyntaxKind :: AWAIT_KW } ; [dyn] => { $ crate :: SyntaxKind :: DYN_KW } ; [gen] => { $ crate :: SyntaxKind :: GEN_KW } ; [try] => { $ crate :: SyntaxKind :: TRY_KW } ; [lifetime_ident] => { $ crate :: SyntaxKind :: LIFETIME_IDENT } ; [int_number] => { $ crate :: SyntaxKind :: INT_NUMBER } ; [ident] => { $ crate :: SyntaxKind :: IDENT } ; [string] => { $ crate :: SyntaxKind :: STRING } ; [shebang] => { $ crate :: SyntaxKind :: SHEBANG } ; [frontmatter] => { $ crate :: SyntaxKind :: FRONTMATTER } ; }
 impl ::core::marker::Copy for SyntaxKind {}
 impl ::core::clone::Clone for SyntaxKind {
     #[inline]
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs
index 6ec4192830b..cef7b0ee239 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs
+++ b/src/tools/rust-analyzer/crates/parser/test_data/generated/runner.rs
@@ -21,6 +21,8 @@ mod ok {
     #[test]
     fn asm_expr() { run_and_expect_no_errors("test_data/parser/inline/ok/asm_expr.rs"); }
     #[test]
+    fn asm_kinds() { run_and_expect_no_errors("test_data/parser/inline/ok/asm_kinds.rs"); }
+    #[test]
     fn asm_label() { run_and_expect_no_errors("test_data/parser/inline/ok/asm_label.rs"); }
     #[test]
     fn assoc_const_eq() {
@@ -298,6 +300,8 @@ mod ok {
         run_and_expect_no_errors("test_data/parser/inline/ok/generic_param_list.rs");
     }
     #[test]
+    fn global_asm() { run_and_expect_no_errors("test_data/parser/inline/ok/global_asm.rs"); }
+    #[test]
     fn half_open_range_pat() {
         run_and_expect_no_errors("test_data/parser/inline/ok/half_open_range_pat.rs");
     }
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_kinds.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_kinds.rast
new file mode 100644
index 00000000000..c337d89aa50
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_kinds.rast
@@ -0,0 +1,48 @@
+SOURCE_FILE
+  FN
+    FN_KW "fn"
+    WHITESPACE " "
+    NAME
+      IDENT "foo"
+    PARAM_LIST
+      L_PAREN "("
+      R_PAREN ")"
+    WHITESPACE " "
+    BLOCK_EXPR
+      STMT_LIST
+        L_CURLY "{"
+        WHITESPACE "\n    "
+        EXPR_STMT
+          ASM_EXPR
+            BUILTIN_KW "builtin"
+            POUND "#"
+            ASM_KW "asm"
+            L_PAREN "("
+            LITERAL
+              STRING "\"\""
+            R_PAREN ")"
+          SEMICOLON ";"
+        WHITESPACE "\n    "
+        ASM_EXPR
+          BUILTIN_KW "builtin"
+          POUND "#"
+          GLOBAL_ASM_KW "global_asm"
+          L_PAREN "("
+          LITERAL
+            STRING "\"\""
+          R_PAREN ")"
+        SEMICOLON ";"
+        WHITESPACE "\n    "
+        EXPR_STMT
+          ASM_EXPR
+            BUILTIN_KW "builtin"
+            POUND "#"
+            NAKED_ASM_KW "naked_asm"
+            L_PAREN "("
+            LITERAL
+              STRING "\"\""
+            R_PAREN ")"
+          SEMICOLON ";"
+        WHITESPACE "\n"
+        R_CURLY "}"
+  WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_kinds.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_kinds.rs
new file mode 100644
index 00000000000..9c03e9de689
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/asm_kinds.rs
@@ -0,0 +1,5 @@
+fn foo() {
+    builtin#asm("");
+    builtin#global_asm("");
+    builtin#naked_asm("");
+}
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/global_asm.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/global_asm.rast
new file mode 100644
index 00000000000..5337c56be17
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/global_asm.rast
@@ -0,0 +1,10 @@
+SOURCE_FILE
+  ASM_EXPR
+    BUILTIN_KW "builtin"
+    POUND "#"
+    GLOBAL_ASM_KW "global_asm"
+    L_PAREN "("
+    LITERAL
+      STRING "\"\""
+    R_PAREN ")"
+  WHITESPACE "\n"
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/global_asm.rs b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/global_asm.rs
new file mode 100644
index 00000000000..967ce1f5fd9
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/inline/ok/global_asm.rs
@@ -0,0 +1 @@
+builtin#global_asm("")
diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_config_file.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_config_file.rs
new file mode 100644
index 00000000000..7966f74df30
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_config_file.rs
@@ -0,0 +1,34 @@
+//! Read `.cargo/config.toml` as a JSON object
+use rustc_hash::FxHashMap;
+use toolchain::Tool;
+
+use crate::{ManifestPath, Sysroot, utf8_stdout};
+
+pub(crate) type CargoConfigFile = serde_json::Map<String, serde_json::Value>;
+
+pub(crate) fn read(
+    manifest: &ManifestPath,
+    extra_env: &FxHashMap<String, Option<String>>,
+    sysroot: &Sysroot,
+) -> Option<CargoConfigFile> {
+    let mut cargo_config = sysroot.tool(Tool::Cargo, manifest.parent(), extra_env);
+    cargo_config
+        .args(["-Z", "unstable-options", "config", "get", "--format", "json"])
+        .env("RUSTC_BOOTSTRAP", "1");
+    if manifest.is_rust_manifest() {
+        cargo_config.arg("-Zscript");
+    }
+
+    tracing::debug!("Discovering cargo config by {:?}", cargo_config);
+    let json: serde_json::Map<String, serde_json::Value> = utf8_stdout(&mut cargo_config)
+        .inspect(|json| {
+            tracing::debug!("Discovered cargo config: {:?}", json);
+        })
+        .inspect_err(|err| {
+            tracing::debug!("Failed to discover cargo config: {:?}", err);
+        })
+        .ok()
+        .and_then(|stdout| serde_json::from_str(&stdout).ok())?;
+
+    Some(json)
+}
diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
index 4bacc904174..daadcd9d79a 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs
@@ -300,8 +300,6 @@ pub struct CargoMetadataConfig {
     pub extra_args: Vec<String>,
     /// Extra env vars to set when invoking the cargo command
     pub extra_env: FxHashMap<String, Option<String>>,
-    /// The target dir for this workspace load.
-    pub target_dir: Utf8PathBuf,
     /// What kind of metadata are we fetching: workspace, rustc, or sysroot.
     pub kind: &'static str,
     /// The toolchain version, if known.
@@ -317,188 +315,6 @@ struct PackageMetadata {
 }
 
 impl CargoWorkspace {
-    /// Fetches the metadata for the given `cargo_toml` manifest.
-    /// A successful result may contain another metadata error if the initial fetching failed but
-    /// the `--no-deps` retry succeeded.
-    ///
-    /// The sysroot is used to set the `RUSTUP_TOOLCHAIN` env var when invoking cargo
-    /// to ensure that the rustup proxy uses the correct toolchain.
-    pub fn fetch_metadata(
-        cargo_toml: &ManifestPath,
-        current_dir: &AbsPath,
-        config: &CargoMetadataConfig,
-        sysroot: &Sysroot,
-        no_deps: bool,
-        locked: bool,
-        progress: &dyn Fn(String),
-    ) -> anyhow::Result<(cargo_metadata::Metadata, Option<anyhow::Error>)> {
-        let res = Self::fetch_metadata_(
-            cargo_toml,
-            current_dir,
-            config,
-            sysroot,
-            no_deps,
-            locked,
-            progress,
-        );
-        if let Ok((_, Some(ref e))) = res {
-            tracing::warn!(
-                %cargo_toml,
-                ?e,
-                "`cargo metadata` failed, but retry with `--no-deps` succeeded"
-            );
-        }
-        res
-    }
-
-    fn fetch_metadata_(
-        cargo_toml: &ManifestPath,
-        current_dir: &AbsPath,
-        config: &CargoMetadataConfig,
-        sysroot: &Sysroot,
-        no_deps: bool,
-        locked: bool,
-        progress: &dyn Fn(String),
-    ) -> anyhow::Result<(cargo_metadata::Metadata, Option<anyhow::Error>)> {
-        let cargo = sysroot.tool(Tool::Cargo, current_dir, &config.extra_env);
-        let mut meta = MetadataCommand::new();
-        meta.cargo_path(cargo.get_program());
-        cargo.get_envs().for_each(|(var, val)| _ = meta.env(var, val.unwrap_or_default()));
-        meta.manifest_path(cargo_toml.to_path_buf());
-        match &config.features {
-            CargoFeatures::All => {
-                meta.features(CargoOpt::AllFeatures);
-            }
-            CargoFeatures::Selected { features, no_default_features } => {
-                if *no_default_features {
-                    meta.features(CargoOpt::NoDefaultFeatures);
-                }
-                if !features.is_empty() {
-                    meta.features(CargoOpt::SomeFeatures(features.clone()));
-                }
-            }
-        }
-        meta.current_dir(current_dir);
-
-        let mut other_options = vec![];
-        // cargo metadata only supports a subset of flags of what cargo usually accepts, and usually
-        // the only relevant flags for metadata here are unstable ones, so we pass those along
-        // but nothing else
-        let mut extra_args = config.extra_args.iter();
-        while let Some(arg) = extra_args.next() {
-            if arg == "-Z" {
-                if let Some(arg) = extra_args.next() {
-                    other_options.push("-Z".to_owned());
-                    other_options.push(arg.to_owned());
-                }
-            }
-        }
-
-        if !config.targets.is_empty() {
-            other_options.extend(
-                config.targets.iter().flat_map(|it| ["--filter-platform".to_owned(), it.clone()]),
-            );
-        }
-        if no_deps {
-            other_options.push("--no-deps".to_owned());
-        }
-
-        let mut using_lockfile_copy = false;
-        // The manifest is a rust file, so this means its a script manifest
-        if cargo_toml.is_rust_manifest() {
-            other_options.push("-Zscript".to_owned());
-        } else if config
-            .toolchain_version
-            .as_ref()
-            .is_some_and(|v| *v >= MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH)
-        {
-            let lockfile = <_ as AsRef<Utf8Path>>::as_ref(cargo_toml).with_extension("lock");
-            let target_lockfile = config
-                .target_dir
-                .join("rust-analyzer")
-                .join("metadata")
-                .join(config.kind)
-                .join("Cargo.lock");
-            match std::fs::copy(&lockfile, &target_lockfile) {
-                Ok(_) => {
-                    using_lockfile_copy = true;
-                    other_options.push("--lockfile-path".to_owned());
-                    other_options.push(target_lockfile.to_string());
-                }
-                Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
-                    // There exists no lockfile yet
-                    using_lockfile_copy = true;
-                    other_options.push("--lockfile-path".to_owned());
-                    other_options.push(target_lockfile.to_string());
-                }
-                Err(e) => {
-                    tracing::warn!(
-                        "Failed to copy lock file from `{lockfile}` to `{target_lockfile}`: {e}",
-                    );
-                }
-            }
-        }
-        if using_lockfile_copy {
-            other_options.push("-Zunstable-options".to_owned());
-            meta.env("RUSTC_BOOTSTRAP", "1");
-        }
-        // No need to lock it if we copied the lockfile, we won't modify the original after all/
-        // This way cargo cannot error out on us if the lockfile requires updating.
-        if !using_lockfile_copy && locked {
-            other_options.push("--locked".to_owned());
-        }
-        meta.other_options(other_options);
-
-        // FIXME: Fetching metadata is a slow process, as it might require
-        // calling crates.io. We should be reporting progress here, but it's
-        // unclear whether cargo itself supports it.
-        progress("cargo metadata: started".to_owned());
-
-        let res = (|| -> anyhow::Result<(_, _)> {
-            let mut errored = false;
-            let output =
-                spawn_with_streaming_output(meta.cargo_command(), &mut |_| (), &mut |line| {
-                    errored = errored || line.starts_with("error") || line.starts_with("warning");
-                    if errored {
-                        progress("cargo metadata: ?".to_owned());
-                        return;
-                    }
-                    progress(format!("cargo metadata: {line}"));
-                })?;
-            if !output.status.success() {
-                progress(format!("cargo metadata: failed {}", output.status));
-                let error = cargo_metadata::Error::CargoMetadata {
-                    stderr: String::from_utf8(output.stderr)?,
-                }
-                .into();
-                if !no_deps {
-                    // If we failed to fetch metadata with deps, try again without them.
-                    // This makes r-a still work partially when offline.
-                    if let Ok((metadata, _)) = Self::fetch_metadata_(
-                        cargo_toml,
-                        current_dir,
-                        config,
-                        sysroot,
-                        true,
-                        locked,
-                        progress,
-                    ) {
-                        return Ok((metadata, Some(error)));
-                    }
-                }
-                return Err(error);
-            }
-            let stdout = from_utf8(&output.stdout)?
-                .lines()
-                .find(|line| line.starts_with('{'))
-                .ok_or(cargo_metadata::Error::NoJson)?;
-            Ok((cargo_metadata::MetadataCommand::parse(stdout)?, None))
-        })()
-        .with_context(|| format!("Failed to run `{:?}`", meta.cargo_command()));
-        progress("cargo metadata: finished".to_owned());
-        res
-    }
-
     pub fn new(
         mut meta: cargo_metadata::Metadata,
         ws_manifest_path: ManifestPath,
@@ -733,3 +549,214 @@ impl CargoWorkspace {
         self.requires_rustc_private
     }
 }
+
+pub(crate) struct FetchMetadata {
+    command: cargo_metadata::MetadataCommand,
+    lockfile_path: Option<Utf8PathBuf>,
+    kind: &'static str,
+    no_deps: bool,
+    no_deps_result: anyhow::Result<cargo_metadata::Metadata>,
+    other_options: Vec<String>,
+}
+
+impl FetchMetadata {
+    /// Builds a command to fetch metadata for the given `cargo_toml` manifest.
+    ///
+    /// Performs a lightweight pre-fetch using the `--no-deps` option,
+    /// available via [`FetchMetadata::no_deps_metadata`], to gather basic
+    /// information such as the `target-dir`.
+    ///
+    /// The provided sysroot is used to set the `RUSTUP_TOOLCHAIN`
+    /// environment variable when invoking Cargo, ensuring that the
+    /// rustup proxy selects the correct toolchain.
+    pub(crate) fn new(
+        cargo_toml: &ManifestPath,
+        current_dir: &AbsPath,
+        config: &CargoMetadataConfig,
+        sysroot: &Sysroot,
+        no_deps: bool,
+    ) -> Self {
+        let cargo = sysroot.tool(Tool::Cargo, current_dir, &config.extra_env);
+        let mut command = MetadataCommand::new();
+        command.cargo_path(cargo.get_program());
+        cargo.get_envs().for_each(|(var, val)| _ = command.env(var, val.unwrap_or_default()));
+        command.manifest_path(cargo_toml.to_path_buf());
+        match &config.features {
+            CargoFeatures::All => {
+                command.features(CargoOpt::AllFeatures);
+            }
+            CargoFeatures::Selected { features, no_default_features } => {
+                if *no_default_features {
+                    command.features(CargoOpt::NoDefaultFeatures);
+                }
+                if !features.is_empty() {
+                    command.features(CargoOpt::SomeFeatures(features.clone()));
+                }
+            }
+        }
+        command.current_dir(current_dir);
+
+        let mut needs_nightly = false;
+        let mut other_options = vec![];
+        // cargo metadata only supports a subset of flags of what cargo usually accepts, and usually
+        // the only relevant flags for metadata here are unstable ones, so we pass those along
+        // but nothing else
+        let mut extra_args = config.extra_args.iter();
+        while let Some(arg) = extra_args.next() {
+            if arg == "-Z" {
+                if let Some(arg) = extra_args.next() {
+                    needs_nightly = true;
+                    other_options.push("-Z".to_owned());
+                    other_options.push(arg.to_owned());
+                }
+            }
+        }
+
+        let mut lockfile_path = None;
+        if cargo_toml.is_rust_manifest() {
+            needs_nightly = true;
+            other_options.push("-Zscript".to_owned());
+        } else if config
+            .toolchain_version
+            .as_ref()
+            .is_some_and(|v| *v >= MINIMUM_TOOLCHAIN_VERSION_SUPPORTING_LOCKFILE_PATH)
+        {
+            lockfile_path = Some(<_ as AsRef<Utf8Path>>::as_ref(cargo_toml).with_extension("lock"));
+        }
+
+        if !config.targets.is_empty() {
+            other_options.extend(
+                config.targets.iter().flat_map(|it| ["--filter-platform".to_owned(), it.clone()]),
+            );
+        }
+
+        command.other_options(other_options.clone());
+
+        if needs_nightly {
+            command.env("RUSTC_BOOTSTRAP", "1");
+        }
+
+        // Pre-fetch basic metadata using `--no-deps`, which:
+        // - avoids fetching registries like crates.io,
+        // - skips dependency resolution and does not modify lockfiles,
+        // - and thus doesn't require progress reporting or copying lockfiles.
+        //
+        // Useful as a fast fallback to extract info like `target-dir`.
+        let cargo_command;
+        let no_deps_result = if no_deps {
+            command.no_deps();
+            cargo_command = command.cargo_command();
+            command.exec()
+        } else {
+            let mut no_deps_command = command.clone();
+            no_deps_command.no_deps();
+            cargo_command = no_deps_command.cargo_command();
+            no_deps_command.exec()
+        }
+        .with_context(|| format!("Failed to run `{cargo_command:?}`"));
+
+        Self { command, lockfile_path, kind: config.kind, no_deps, no_deps_result, other_options }
+    }
+
+    pub(crate) fn no_deps_metadata(&self) -> Option<&cargo_metadata::Metadata> {
+        self.no_deps_result.as_ref().ok()
+    }
+
+    /// Executes the metadata-fetching command.
+    ///
+    /// A successful result may still contain a metadata error if the full fetch failed,
+    /// but the fallback `--no-deps` pre-fetch succeeded during command construction.
+    pub(crate) fn exec(
+        self,
+        target_dir: &Utf8Path,
+        locked: bool,
+        progress: &dyn Fn(String),
+    ) -> anyhow::Result<(cargo_metadata::Metadata, Option<anyhow::Error>)> {
+        let Self { mut command, lockfile_path, kind, no_deps, no_deps_result, mut other_options } =
+            self;
+
+        if no_deps {
+            return no_deps_result.map(|m| (m, None));
+        }
+
+        let mut using_lockfile_copy = false;
+        // The manifest is a rust file, so this means its a script manifest
+        if let Some(lockfile) = lockfile_path {
+            let target_lockfile =
+                target_dir.join("rust-analyzer").join("metadata").join(kind).join("Cargo.lock");
+            match std::fs::copy(&lockfile, &target_lockfile) {
+                Ok(_) => {
+                    using_lockfile_copy = true;
+                    other_options.push("--lockfile-path".to_owned());
+                    other_options.push(target_lockfile.to_string());
+                }
+                Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
+                    // There exists no lockfile yet
+                    using_lockfile_copy = true;
+                    other_options.push("--lockfile-path".to_owned());
+                    other_options.push(target_lockfile.to_string());
+                }
+                Err(e) => {
+                    tracing::warn!(
+                        "Failed to copy lock file from `{lockfile}` to `{target_lockfile}`: {e}",
+                    );
+                }
+            }
+        }
+        if using_lockfile_copy {
+            other_options.push("-Zunstable-options".to_owned());
+            command.env("RUSTC_BOOTSTRAP", "1");
+        }
+        // No need to lock it if we copied the lockfile, we won't modify the original after all/
+        // This way cargo cannot error out on us if the lockfile requires updating.
+        if !using_lockfile_copy && locked {
+            other_options.push("--locked".to_owned());
+        }
+        command.other_options(other_options);
+
+        // FIXME: Fetching metadata is a slow process, as it might require
+        // calling crates.io. We should be reporting progress here, but it's
+        // unclear whether cargo itself supports it.
+        progress("cargo metadata: started".to_owned());
+
+        let res = (|| -> anyhow::Result<(_, _)> {
+            let mut errored = false;
+            let output =
+                spawn_with_streaming_output(command.cargo_command(), &mut |_| (), &mut |line| {
+                    errored = errored || line.starts_with("error") || line.starts_with("warning");
+                    if errored {
+                        progress("cargo metadata: ?".to_owned());
+                        return;
+                    }
+                    progress(format!("cargo metadata: {line}"));
+                })?;
+            if !output.status.success() {
+                progress(format!("cargo metadata: failed {}", output.status));
+                let error = cargo_metadata::Error::CargoMetadata {
+                    stderr: String::from_utf8(output.stderr)?,
+                }
+                .into();
+                if !no_deps {
+                    // If we failed to fetch metadata with deps, return pre-fetched result without them.
+                    // This makes r-a still work partially when offline.
+                    if let Ok(metadata) = no_deps_result {
+                        tracing::warn!(
+                            ?error,
+                            "`cargo metadata` failed and returning succeeded result with `--no-deps`"
+                        );
+                        return Ok((metadata, Some(error)));
+                    }
+                }
+                return Err(error);
+            }
+            let stdout = from_utf8(&output.stdout)?
+                .lines()
+                .find(|line| line.starts_with('{'))
+                .ok_or(cargo_metadata::Error::NoJson)?;
+            Ok((cargo_metadata::MetadataCommand::parse(stdout)?, None))
+        })()
+        .with_context(|| format!("Failed to run `{:?}`", command.cargo_command()));
+        progress("cargo metadata: finished".to_owned());
+        res
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/project-model/src/env.rs b/src/tools/rust-analyzer/crates/project-model/src/env.rs
index 9e0415c3b39..d281492fc98 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/env.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/env.rs
@@ -1,10 +1,9 @@
 //! Cargo-like environment variables injection.
 use base_db::Env;
-use paths::{Utf8Path, Utf8PathBuf};
-use rustc_hash::FxHashMap;
+use paths::Utf8Path;
 use toolchain::Tool;
 
-use crate::{ManifestPath, PackageData, Sysroot, TargetKind, utf8_stdout};
+use crate::{ManifestPath, PackageData, TargetKind, cargo_config_file::CargoConfigFile};
 
 /// Recreates the compile-time environment variables that Cargo sets.
 ///
@@ -61,104 +60,68 @@ pub(crate) fn inject_rustc_tool_env(env: &mut Env, cargo_name: &str, kind: Targe
     env.set("CARGO_CRATE_NAME", cargo_name.replace('-', "_"));
 }
 
-pub(crate) fn cargo_config_env(
-    manifest: &ManifestPath,
-    extra_env: &FxHashMap<String, Option<String>>,
-    sysroot: &Sysroot,
-) -> Env {
-    let mut cargo_config = sysroot.tool(Tool::Cargo, manifest.parent(), extra_env);
-    cargo_config
-        .args(["-Z", "unstable-options", "config", "get", "env"])
-        .env("RUSTC_BOOTSTRAP", "1");
-    if manifest.is_rust_manifest() {
-        cargo_config.arg("-Zscript");
-    }
-    // if successful we receive `env.key.value = "value" per entry
-    tracing::debug!("Discovering cargo config env by {:?}", cargo_config);
-    utf8_stdout(&mut cargo_config)
-        .map(|stdout| parse_output_cargo_config_env(manifest, &stdout))
-        .inspect(|env| {
-            tracing::debug!("Discovered cargo config env: {:?}", env);
-        })
-        .inspect_err(|err| {
-            tracing::debug!("Failed to discover cargo config env: {:?}", err);
-        })
-        .unwrap_or_default()
-}
-
-fn parse_output_cargo_config_env(manifest: &ManifestPath, stdout: &str) -> Env {
+pub(crate) fn cargo_config_env(manifest: &ManifestPath, config: &Option<CargoConfigFile>) -> Env {
     let mut env = Env::default();
-    let mut relatives = vec![];
-    for (key, val) in
-        stdout.lines().filter_map(|l| l.strip_prefix("env.")).filter_map(|l| l.split_once(" = "))
-    {
-        let val = val.trim_matches('"').to_owned();
-        if let Some((key, modifier)) = key.split_once('.') {
-            match modifier {
-                "relative" => relatives.push((key, val)),
-                "value" => _ = env.insert(key, val),
-                _ => {
-                    tracing::warn!(
-                        "Unknown modifier in cargo config env: {}, expected `relative` or `value`",
-                        modifier
-                    );
-                    continue;
-                }
-            }
-        } else {
-            env.insert(key, val);
-        }
-    }
+    let Some(serde_json::Value::Object(env_json)) = config.as_ref().and_then(|c| c.get("env"))
+    else {
+        return env;
+    };
+
     // FIXME: The base here should be the parent of the `.cargo/config` file, not the manifest.
     // But cargo does not provide this information.
     let base = <_ as AsRef<Utf8Path>>::as_ref(manifest.parent());
-    for (key, relative) in relatives {
-        if relative != "true" {
+
+    for (key, entry) in env_json {
+        let serde_json::Value::Object(entry) = entry else {
             continue;
-        }
-        if let Some(suffix) = env.get(key) {
-            env.insert(key, base.join(suffix).to_string());
-        }
-    }
-    env
-}
+        };
+        let Some(value) = entry.get("value").and_then(|v| v.as_str()) else {
+            continue;
+        };
 
-pub(crate) fn cargo_config_build_target_dir(
-    manifest: &ManifestPath,
-    extra_env: &FxHashMap<String, Option<String>>,
-    sysroot: &Sysroot,
-) -> Option<Utf8PathBuf> {
-    let mut cargo_config = sysroot.tool(Tool::Cargo, manifest.parent(), extra_env);
-    cargo_config
-        .args(["-Z", "unstable-options", "config", "get", "build.target-dir"])
-        .env("RUSTC_BOOTSTRAP", "1");
-    if manifest.is_rust_manifest() {
-        cargo_config.arg("-Zscript");
+        let value = if entry
+            .get("relative")
+            .and_then(|v| v.as_bool())
+            .is_some_and(std::convert::identity)
+        {
+            base.join(value).to_string()
+        } else {
+            value.to_owned()
+        };
+        env.insert(key, value);
     }
-    utf8_stdout(&mut cargo_config)
-        .map(|stdout| {
-            Utf8Path::new(stdout.trim_start_matches("build.target-dir = ").trim_matches('"'))
-                .to_owned()
-        })
-        .ok()
+
+    env
 }
 
 #[test]
 fn parse_output_cargo_config_env_works() {
-    let stdout = r#"
-env.CARGO_WORKSPACE_DIR.relative = true
-env.CARGO_WORKSPACE_DIR.value = ""
-env.RELATIVE.relative = true
-env.RELATIVE.value = "../relative"
-env.INVALID.relative = invalidbool
-env.INVALID.value = "../relative"
-env.TEST.value = "test"
-"#
-    .trim();
+    let raw = r#"
+{
+  "env": {
+    "CARGO_WORKSPACE_DIR": {
+      "relative": true,
+      "value": ""
+    },
+    "INVALID": {
+      "relative": "invalidbool",
+      "value": "../relative"
+    },
+    "RELATIVE": {
+      "relative": true,
+      "value": "../relative"
+    },
+    "TEST": {
+      "value": "test"
+    }
+  }
+}
+"#;
+    let config: CargoConfigFile = serde_json::from_str(raw).unwrap();
     let cwd = paths::Utf8PathBuf::try_from(std::env::current_dir().unwrap()).unwrap();
     let manifest = paths::AbsPathBuf::assert(cwd.join("Cargo.toml"));
     let manifest = ManifestPath::try_from(manifest).unwrap();
-    let env = parse_output_cargo_config_env(&manifest, stdout);
+    let env = cargo_config_env(&manifest, &Some(config));
     assert_eq!(env.get("CARGO_WORKSPACE_DIR").as_deref(), Some(cwd.join("").as_str()));
     assert_eq!(env.get("RELATIVE").as_deref(), Some(cwd.join("../relative").as_str()));
     assert_eq!(env.get("INVALID").as_deref(), Some("../relative"));
diff --git a/src/tools/rust-analyzer/crates/project-model/src/lib.rs b/src/tools/rust-analyzer/crates/project-model/src/lib.rs
index 436af64cf13..3bf3d06e6b1 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/lib.rs
@@ -24,7 +24,7 @@ pub mod toolchain_info {
 
     use std::path::Path;
 
-    use crate::{ManifestPath, Sysroot};
+    use crate::{ManifestPath, Sysroot, cargo_config_file::CargoConfigFile};
 
     #[derive(Copy, Clone)]
     pub enum QueryConfig<'a> {
@@ -32,11 +32,12 @@ pub mod toolchain_info {
         Rustc(&'a Sysroot, &'a Path),
         /// Attempt to use cargo to query the desired information, honoring cargo configurations.
         /// If this fails, falls back to invoking `rustc` directly.
-        Cargo(&'a Sysroot, &'a ManifestPath),
+        Cargo(&'a Sysroot, &'a ManifestPath, &'a Option<CargoConfigFile>),
     }
 }
 
 mod build_dependencies;
+mod cargo_config_file;
 mod cargo_workspace;
 mod env;
 mod manifest_path;
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 9f19260d309..9781c46737d 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/sysroot.rs
@@ -9,14 +9,15 @@ use std::{env, fs, ops::Not, path::Path, process::Command};
 
 use anyhow::{Result, format_err};
 use itertools::Itertools;
-use paths::{AbsPath, AbsPathBuf, Utf8PathBuf};
+use paths::{AbsPath, AbsPathBuf, Utf8Path, Utf8PathBuf};
 use rustc_hash::FxHashMap;
 use stdx::format_to;
 use toolchain::{Tool, probe_for_binary};
 
 use crate::{
     CargoWorkspace, ManifestPath, ProjectJson, RustSourceWorkspaceConfig,
-    cargo_workspace::CargoMetadataConfig, utf8_stdout,
+    cargo_workspace::{CargoMetadataConfig, FetchMetadata},
+    utf8_stdout,
 };
 
 #[derive(Debug, Clone, PartialEq, Eq)]
@@ -211,6 +212,7 @@ impl Sysroot {
         sysroot_source_config: &RustSourceWorkspaceConfig,
         no_deps: bool,
         current_dir: &AbsPath,
+        target_dir: &Utf8Path,
         progress: &dyn Fn(String),
     ) -> Option<RustLibSrcWorkspace> {
         assert!(matches!(self.workspace, RustLibSrcWorkspace::Empty), "workspace already loaded");
@@ -224,6 +226,7 @@ impl Sysroot {
                 match self.load_library_via_cargo(
                     &library_manifest,
                     current_dir,
+                    target_dir,
                     cargo_config,
                     no_deps,
                     progress,
@@ -319,6 +322,7 @@ impl Sysroot {
         &self,
         library_manifest: &ManifestPath,
         current_dir: &AbsPath,
+        target_dir: &Utf8Path,
         cargo_config: &CargoMetadataConfig,
         no_deps: bool,
         progress: &dyn Fn(String),
@@ -331,16 +335,11 @@ impl Sysroot {
             Some("nightly".to_owned()),
         );
 
-        let (mut res, _) = CargoWorkspace::fetch_metadata(
-            library_manifest,
-            current_dir,
-            &cargo_config,
-            self,
-            no_deps,
-            // Make sure we never attempt to write to the sysroot
-            true,
-            progress,
-        )?;
+        // Make sure we never attempt to write to the sysroot
+        let locked = true;
+        let (mut res, _) =
+            FetchMetadata::new(library_manifest, current_dir, &cargo_config, self, no_deps)
+                .exec(target_dir, locked, progress)?;
 
         // Patch out `rustc-std-workspace-*` crates to point to the real crates.
         // This is done prior to `CrateGraph` construction to prevent de-duplication logic from failing.
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 f229e9a650d..ed72520f40d 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs
@@ -239,8 +239,13 @@ 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(), false, &cwd, &|_| ());
+    let loaded_sysroot = sysroot.load_workspace(
+        &RustSourceWorkspaceConfig::default_cargo(),
+        false,
+        &cwd,
+        &Utf8PathBuf::default(),
+        &|_| (),
+    );
     if let Some(loaded_sysroot) = loaded_sysroot {
         sysroot.set_workspace(loaded_sysroot);
     }
diff --git a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs
index a77f76797fc..6e06e88bf7a 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/rustc_cfg.rs
@@ -63,7 +63,7 @@ fn rustc_print_cfg(
 ) -> anyhow::Result<String> {
     const RUSTC_ARGS: [&str; 2] = ["--print", "cfg"];
     let (sysroot, current_dir) = match config {
-        QueryConfig::Cargo(sysroot, cargo_toml) => {
+        QueryConfig::Cargo(sysroot, cargo_toml, _) => {
             let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent(), extra_env);
             cmd.args(["rustc", "-Z", "unstable-options"]).args(RUSTC_ARGS);
             if let Some(target) = target {
@@ -109,7 +109,7 @@ mod tests {
         let sysroot = Sysroot::empty();
         let manifest_path =
             ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap();
-        let cfg = QueryConfig::Cargo(&sysroot, &manifest_path);
+        let cfg = QueryConfig::Cargo(&sysroot, &manifest_path, &None);
         assert_ne!(get(cfg, None, &FxHashMap::default()), vec![]);
     }
 
diff --git a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_data_layout.rs b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_data_layout.rs
index a4d0ec69537..a28f468e692 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_data_layout.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_data_layout.rs
@@ -20,7 +20,7 @@ pub fn get(
             })
     };
     let (sysroot, current_dir) = match config {
-        QueryConfig::Cargo(sysroot, cargo_toml) => {
+        QueryConfig::Cargo(sysroot, cargo_toml, _) => {
             let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent(), extra_env);
             cmd.env("RUSTC_BOOTSTRAP", "1");
             cmd.args(["rustc", "-Z", "unstable-options"]).args(RUSTC_ARGS).args([
@@ -66,7 +66,7 @@ mod tests {
         let sysroot = Sysroot::empty();
         let manifest_path =
             ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap();
-        let cfg = QueryConfig::Cargo(&sysroot, &manifest_path);
+        let cfg = QueryConfig::Cargo(&sysroot, &manifest_path, &None);
         assert!(get(cfg, None, &FxHashMap::default()).is_ok());
     }
 
diff --git a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_tuple.rs b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_tuple.rs
index f6ab8532197..9f12ededb61 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_tuple.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/target_tuple.rs
@@ -5,7 +5,9 @@ use anyhow::Context;
 use rustc_hash::FxHashMap;
 use toolchain::Tool;
 
-use crate::{ManifestPath, Sysroot, toolchain_info::QueryConfig, utf8_stdout};
+use crate::{
+    Sysroot, cargo_config_file::CargoConfigFile, toolchain_info::QueryConfig, utf8_stdout,
+};
 
 /// For cargo, runs `cargo -Zunstable-options config get build.target` to get the configured project target(s).
 /// For rustc, runs `rustc --print -vV` to get the host target.
@@ -20,8 +22,8 @@ pub fn get(
     }
 
     let (sysroot, current_dir) = match config {
-        QueryConfig::Cargo(sysroot, cargo_toml) => {
-            match cargo_config_build_target(cargo_toml, extra_env, sysroot) {
+        QueryConfig::Cargo(sysroot, cargo_toml, config_file) => {
+            match config_file.as_ref().and_then(cargo_config_build_target) {
                 Some(it) => return Ok(it),
                 None => (sysroot, cargo_toml.parent().as_ref()),
             }
@@ -50,30 +52,30 @@ fn rustc_discover_host_tuple(
     }
 }
 
-fn cargo_config_build_target(
-    cargo_toml: &ManifestPath,
-    extra_env: &FxHashMap<String, Option<String>>,
-    sysroot: &Sysroot,
-) -> Option<Vec<String>> {
-    let mut cmd = sysroot.tool(Tool::Cargo, cargo_toml.parent(), extra_env);
-    cmd.current_dir(cargo_toml.parent()).env("RUSTC_BOOTSTRAP", "1");
-    cmd.args(["-Z", "unstable-options", "config", "get", "build.target"]);
-    // if successful we receive `build.target = "target-tuple"`
-    // or `build.target = ["<target 1>", ..]`
-    // this might be `error: config value `build.target` is not set` in which case we
-    // don't wanna log the error
-    utf8_stdout(&mut cmd).and_then(parse_output_cargo_config_build_target).ok()
+fn cargo_config_build_target(config: &CargoConfigFile) -> Option<Vec<String>> {
+    match parse_json_cargo_config_build_target(config) {
+        Ok(v) => v,
+        Err(e) => {
+            tracing::debug!("Failed to discover cargo config build target {e:?}");
+            None
+        }
+    }
 }
 
 // Parses `"build.target = [target-tuple, target-tuple, ...]"` or `"build.target = "target-tuple"`
-fn parse_output_cargo_config_build_target(stdout: String) -> anyhow::Result<Vec<String>> {
-    let trimmed = stdout.trim_start_matches("build.target = ").trim_matches('"');
-
-    if !trimmed.starts_with('[') {
-        return Ok([trimmed.to_owned()].to_vec());
+fn parse_json_cargo_config_build_target(
+    config: &CargoConfigFile,
+) -> anyhow::Result<Option<Vec<String>>> {
+    let target = config.get("build").and_then(|v| v.as_object()).and_then(|m| m.get("target"));
+    match target {
+        Some(serde_json::Value::String(s)) => Ok(Some(vec![s.to_owned()])),
+        Some(v) => serde_json::from_value(v.clone())
+            .map(Option::Some)
+            .context("Failed to parse `build.target` as an array of target"),
+        // t`error: config value `build.target` is not set`, in which case we
+        // don't wanna log the error
+        None => Ok(None),
     }
-
-    serde_json::from_str(trimmed).context("Failed to parse `build.target` as an array of target")
 }
 
 #[cfg(test)]
@@ -90,7 +92,7 @@ mod tests {
         let sysroot = Sysroot::empty();
         let manifest_path =
             ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap();
-        let cfg = QueryConfig::Cargo(&sysroot, &manifest_path);
+        let cfg = QueryConfig::Cargo(&sysroot, &manifest_path, &None);
         assert!(get(cfg, None, &FxHashMap::default()).is_ok());
     }
 
diff --git a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/version.rs b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/version.rs
index 91ba8598591..357053d8e82 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/version.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/toolchain_info/version.rs
@@ -12,7 +12,7 @@ pub(crate) fn get(
     extra_env: &FxHashMap<String, Option<String>>,
 ) -> Result<Option<Version>, anyhow::Error> {
     let (mut cmd, prefix) = match config {
-        QueryConfig::Cargo(sysroot, cargo_toml) => {
+        QueryConfig::Cargo(sysroot, cargo_toml, _) => {
             (sysroot.tool(Tool::Cargo, cargo_toml.parent(), extra_env), "cargo ")
         }
         QueryConfig::Rustc(sysroot, current_dir) => {
@@ -44,7 +44,7 @@ mod tests {
         let sysroot = Sysroot::empty();
         let manifest_path =
             ManifestPath::try_from(AbsPathBuf::assert(Utf8PathBuf::from(manifest_path))).unwrap();
-        let cfg = QueryConfig::Cargo(&sysroot, &manifest_path);
+        let cfg = QueryConfig::Cargo(&sysroot, &manifest_path, &None);
         assert!(get(cfg, &FxHashMap::default()).is_ok());
     }
 
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 43db84b4fa3..677f29e3c60 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
@@ -25,11 +25,9 @@ use crate::{
     ProjectJson, ProjectManifest, RustSourceWorkspaceConfig, Sysroot, TargetData, TargetKind,
     WorkspaceBuildScripts,
     build_dependencies::BuildScriptOutput,
-    cargo_workspace::{CargoMetadataConfig, DepKind, PackageData, RustLibSource},
-    env::{
-        cargo_config_build_target_dir, cargo_config_env, inject_cargo_env,
-        inject_cargo_package_env, inject_rustc_tool_env,
-    },
+    cargo_config_file,
+    cargo_workspace::{CargoMetadataConfig, DepKind, FetchMetadata, PackageData, RustLibSource},
+    env::{cargo_config_env, inject_cargo_env, inject_cargo_package_env, inject_rustc_tool_env},
     project_json::{Crate, CrateArrayIdx},
     sysroot::RustLibSrcWorkspace,
     toolchain_info::{QueryConfig, rustc_cfg, target_data_layout, target_tuple, version},
@@ -270,7 +268,9 @@ impl ProjectWorkspace {
 
         tracing::info!(workspace = %cargo_toml, src_root = ?sysroot.rust_lib_src_root(), root = ?sysroot.root(), "Using sysroot");
         progress("querying project metadata".to_owned());
-        let toolchain_config = QueryConfig::Cargo(&sysroot, cargo_toml);
+        let config_file = cargo_config_file::read(cargo_toml, extra_env, &sysroot);
+        let config_file_ = config_file.clone();
+        let toolchain_config = QueryConfig::Cargo(&sysroot, cargo_toml, &config_file_);
         let targets =
             target_tuple::get(toolchain_config, target.as_deref(), extra_env).unwrap_or_default();
         let toolchain = version::get(toolchain_config, extra_env)
@@ -282,10 +282,24 @@ impl ProjectWorkspace {
             .ok()
             .flatten();
 
+        let fetch_metadata = FetchMetadata::new(
+            cargo_toml,
+            workspace_dir,
+            &CargoMetadataConfig {
+                features: features.clone(),
+                targets: targets.clone(),
+                extra_args: extra_args.clone(),
+                extra_env: extra_env.clone(),
+                toolchain_version: toolchain.clone(),
+                kind: "workspace",
+            },
+            &sysroot,
+            *no_deps,
+        );
         let target_dir = config
             .target_dir
             .clone()
-            .or_else(|| cargo_config_build_target_dir(cargo_toml, extra_env, &sysroot))
+            .or_else(|| fetch_metadata.no_deps_metadata().map(|m| m.target_directory.clone()))
             .unwrap_or_else(|| workspace_dir.join("target").into());
 
         // We spawn a bunch of processes to query various information about the workspace's
@@ -319,7 +333,7 @@ impl ProjectWorkspace {
                 };
                 rustc_dir.and_then(|rustc_dir| {
                     info!(workspace = %cargo_toml, rustc_dir = %rustc_dir, "Using rustc source");
-                    match CargoWorkspace::fetch_metadata(
+                    match FetchMetadata::new(
                         &rustc_dir,
                         workspace_dir,
                         &CargoMetadataConfig {
@@ -327,15 +341,12 @@ impl ProjectWorkspace {
                             targets: targets.clone(),
                             extra_args: extra_args.clone(),
                             extra_env: extra_env.clone(),
-                            target_dir: target_dir.clone(),
                             toolchain_version: toolchain.clone(),
                             kind: "rustc-dev"
                         },
                         &sysroot,
                         *no_deps,
-                        true,
-                        progress,
-                    ) {
+                    ).exec(&target_dir, true, progress) {
                         Ok((meta, _error)) => {
                             let workspace = CargoWorkspace::new(
                                 meta,
@@ -364,40 +375,22 @@ impl ProjectWorkspace {
                 })
             });
 
-            let cargo_metadata = s.spawn(|| {
-                CargoWorkspace::fetch_metadata(
-                    cargo_toml,
-                    workspace_dir,
-                    &CargoMetadataConfig {
-                        features: features.clone(),
-                        targets: targets.clone(),
-                        extra_args: extra_args.clone(),
-                        extra_env: extra_env.clone(),
-                        target_dir: target_dir.clone(),
-                        toolchain_version: toolchain.clone(),
-                        kind: "workspace",
-                    },
-                    &sysroot,
-                    *no_deps,
-                    false,
-                    progress,
-                )
-            });
+            let cargo_metadata = s.spawn(|| fetch_metadata.exec(&target_dir, false, progress));
             let loaded_sysroot = s.spawn(|| {
                 sysroot.load_workspace(
                     &RustSourceWorkspaceConfig::CargoMetadata(sysroot_metadata_config(
                         config,
                         &targets,
                         toolchain.clone(),
-                        target_dir.clone(),
                     )),
                     config.no_deps,
                     workspace_dir,
+                    &target_dir,
                     progress,
                 )
             });
             let cargo_config_extra_env =
-                s.spawn(|| cargo_config_env(cargo_toml, extra_env, &sysroot));
+                s.spawn(move || cargo_config_env(cargo_toml, &config_file));
             thread::Result::Ok((
                 rustc_cfg.join()?,
                 data_layout.join()?,
@@ -476,9 +469,7 @@ impl ProjectWorkspace {
         let target_dir = config
             .target_dir
             .clone()
-            .or_else(|| {
-                cargo_config_build_target_dir(project_json.manifest()?, &config.extra_env, &sysroot)
-            })
+            .or_else(|| cargo_target_dir(project_json.manifest()?, &config.extra_env, &sysroot))
             .unwrap_or_else(|| project_root.join("target").into());
 
         // We spawn a bunch of processes to query various information about the workspace's
@@ -502,6 +493,7 @@ impl ProjectWorkspace {
                         &RustSourceWorkspaceConfig::Json(*sysroot_project),
                         config.no_deps,
                         project_root,
+                        &target_dir,
                         progress,
                     )
                 } else {
@@ -510,10 +502,10 @@ impl ProjectWorkspace {
                             config,
                             &targets,
                             toolchain.clone(),
-                            target_dir,
                         )),
                         config.no_deps,
                         project_root,
+                        &target_dir,
                         progress,
                     )
                 }
@@ -554,7 +546,8 @@ impl ProjectWorkspace {
             None => Sysroot::empty(),
         };
 
-        let query_config = QueryConfig::Cargo(&sysroot, detached_file);
+        let config_file = cargo_config_file::read(detached_file, &config.extra_env, &sysroot);
+        let query_config = QueryConfig::Cargo(&sysroot, detached_file, &config_file);
         let toolchain = version::get(query_config, &config.extra_env).ok().flatten();
         let targets = target_tuple::get(query_config, config.target.as_deref(), &config.extra_env)
             .unwrap_or_default();
@@ -563,7 +556,7 @@ impl ProjectWorkspace {
         let target_dir = config
             .target_dir
             .clone()
-            .or_else(|| cargo_config_build_target_dir(detached_file, &config.extra_env, &sysroot))
+            .or_else(|| cargo_target_dir(detached_file, &config.extra_env, &sysroot))
             .unwrap_or_else(|| dir.join("target").into());
 
         let loaded_sysroot = sysroot.load_workspace(
@@ -571,17 +564,17 @@ impl ProjectWorkspace {
                 config,
                 &targets,
                 toolchain.clone(),
-                target_dir.clone(),
             )),
             config.no_deps,
             dir,
+            &target_dir,
             &|_| (),
         );
         if let Some(loaded_sysroot) = loaded_sysroot {
             sysroot.set_workspace(loaded_sysroot);
         }
 
-        let cargo_script = CargoWorkspace::fetch_metadata(
+        let fetch_metadata = FetchMetadata::new(
             detached_file,
             dir,
             &CargoMetadataConfig {
@@ -589,25 +582,26 @@ impl ProjectWorkspace {
                 targets,
                 extra_args: config.extra_args.clone(),
                 extra_env: config.extra_env.clone(),
-                target_dir,
                 toolchain_version: toolchain.clone(),
                 kind: "detached-file",
             },
             &sysroot,
             config.no_deps,
-            false,
-            &|_| (),
-        )
-        .ok()
-        .map(|(ws, error)| {
-            let cargo_config_extra_env =
-                cargo_config_env(detached_file, &config.extra_env, &sysroot);
-            (
-                CargoWorkspace::new(ws, detached_file.clone(), cargo_config_extra_env, false),
-                WorkspaceBuildScripts::default(),
-                error.map(Arc::new),
-            )
-        });
+        );
+        let target_dir = config
+            .target_dir
+            .clone()
+            .or_else(|| fetch_metadata.no_deps_metadata().map(|m| m.target_directory.clone()))
+            .unwrap_or_else(|| dir.join("target").into());
+        let cargo_script =
+            fetch_metadata.exec(&target_dir, false, &|_| ()).ok().map(|(ws, error)| {
+                let cargo_config_extra_env = cargo_config_env(detached_file, &config_file);
+                (
+                    CargoWorkspace::new(ws, detached_file.clone(), cargo_config_extra_env, false),
+                    WorkspaceBuildScripts::default(),
+                    error.map(Arc::new),
+                )
+            });
 
         Ok(ProjectWorkspace {
             kind: ProjectWorkspaceKind::DetachedFile {
@@ -1889,15 +1883,33 @@ fn sysroot_metadata_config(
     config: &CargoConfig,
     targets: &[String],
     toolchain_version: Option<Version>,
-    target_dir: Utf8PathBuf,
 ) -> CargoMetadataConfig {
     CargoMetadataConfig {
         features: Default::default(),
         targets: targets.to_vec(),
         extra_args: Default::default(),
         extra_env: config.extra_env.clone(),
-        target_dir,
         toolchain_version,
         kind: "sysroot",
     }
 }
+
+fn cargo_target_dir(
+    manifest: &ManifestPath,
+    extra_env: &FxHashMap<String, Option<String>>,
+    sysroot: &Sysroot,
+) -> Option<Utf8PathBuf> {
+    let cargo = sysroot.tool(Tool::Cargo, manifest.parent(), extra_env);
+    let mut meta = cargo_metadata::MetadataCommand::new();
+    meta.cargo_path(cargo.get_program());
+    meta.manifest_path(manifest);
+    // `--no-deps` doesn't (over)write lockfiles as it doesn't do any package resolve.
+    // So we can use it to get `target_directory` before copying lockfiles
+    let mut other_options = vec!["--no-deps".to_owned()];
+    if manifest.is_rust_manifest() {
+        meta.env("RUSTC_BOOTSTRAP", "1");
+        other_options.push("-Zscript".to_owned());
+    }
+    meta.other_options(other_options);
+    meta.exec().map(|m| m.target_directory).ok()
+}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
index 0ee01982fea..fc89f486f84 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -796,7 +796,7 @@ impl flags::AnalysisStats {
             // region:expressions
             let (previous_exprs, previous_unknown, previous_partially_unknown) =
                 (num_exprs, num_exprs_unknown, num_exprs_partially_unknown);
-            for (expr_id, _) in body.exprs.iter() {
+            for (expr_id, _) in body.exprs() {
                 let ty = &inference_result[expr_id];
                 num_exprs += 1;
                 let unknown_or_partial = if ty.is_unknown() {
@@ -901,7 +901,7 @@ impl flags::AnalysisStats {
             // region:patterns
             let (previous_pats, previous_unknown, previous_partially_unknown) =
                 (num_pats, num_pats_unknown, num_pats_partially_unknown);
-            for (pat_id, _) in body.pats.iter() {
+            for (pat_id, _) in body.pats() {
                 let ty = &inference_result[pat_id];
                 num_pats += 1;
                 let unknown_or_partial = if ty.is_unknown() {
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 f97bf832442..30ac93fb6f8 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
@@ -9,6 +9,7 @@ use hir::{ChangeWithProcMacros, Crate};
 use ide::{AnalysisHost, DiagnosticCode, DiagnosticsConfig};
 use ide_db::base_db;
 use itertools::Either;
+use paths::Utf8PathBuf;
 use profile::StopWatch;
 use project_model::toolchain_info::{QueryConfig, target_data_layout};
 use project_model::{
@@ -79,6 +80,7 @@ impl Tester {
             &RustSourceWorkspaceConfig::default_cargo(),
             false,
             &path,
+            &Utf8PathBuf::default(),
             &|_| (),
         );
         if let Some(loaded_sysroot) = loaded_sysroot {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs
index 8a848fb848c..292be1d5315 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs
@@ -911,7 +911,8 @@ pub(crate) fn folding_range(
         | FoldKind::Array
         | FoldKind::TraitAliases
         | FoldKind::ExternCrates
-        | FoldKind::MatchArm => None,
+        | FoldKind::MatchArm
+        | FoldKind::Function => None,
     };
 
     let range = range(line_index, fold.range);
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
index 59073af983b..1b940c70da6 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
@@ -880,7 +880,8 @@ fn main() {{}}
 
 #[test]
 fn diagnostics_dont_block_typing() {
-    if skip_slow_tests() {
+    if skip_slow_tests() || std::env::var("CI").is_ok() {
+        // FIXME: This test is failing too frequently (therefore we disable it on CI).
         return;
     }
 
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 121d2e33243..a9288ecd6fa 100644
--- a/src/tools/rust-analyzer/crates/span/src/ast_id.rs
+++ b/src/tools/rust-analyzer/crates/span/src/ast_id.rs
@@ -92,6 +92,7 @@ impl fmt::Debug for ErasedFileAstId {
             Use,
             Impl,
             BlockExpr,
+            AsmExpr,
             Fixup,
         );
         if f.alternate() {
@@ -144,6 +145,10 @@ enum ErasedFileAstIdKind {
     Impl,
     /// Associated with [`BlockExprFileAstId`].
     BlockExpr,
+    // `global_asm!()` is an item, so we need to give it an `AstId`. So we give to all inline asm
+    // because incrementality is not a problem, they will always be the only item in the macro file,
+    // and memory usage also not because they're rare.
+    AsmExpr,
     /// Keep this last.
     Root,
 }
@@ -204,14 +209,17 @@ impl ErasedFileAstId {
             .or_else(|| extern_block_ast_id(node, index_map))
             .or_else(|| use_ast_id(node, index_map))
             .or_else(|| impl_ast_id(node, index_map))
+            .or_else(|| asm_expr_ast_id(node, index_map))
     }
 
     fn should_alloc(node: &SyntaxNode) -> bool {
-        should_alloc_has_name(node)
-            || should_alloc_assoc_item(node)
-            || ast::ExternBlock::can_cast(node.kind())
-            || ast::Use::can_cast(node.kind())
-            || ast::Impl::can_cast(node.kind())
+        let kind = node.kind();
+        should_alloc_has_name(kind)
+            || should_alloc_assoc_item(kind)
+            || ast::ExternBlock::can_cast(kind)
+            || ast::Use::can_cast(kind)
+            || ast::Impl::can_cast(kind)
+            || ast::AsmExpr::can_cast(kind)
     }
 
     #[inline]
@@ -278,7 +286,6 @@ impl<N> FileAstId<N> {
 
 #[derive(Hash)]
 struct ErasedHasNameFileAstId<'a> {
-    kind: SyntaxKind,
     name: &'a str,
 }
 
@@ -332,6 +339,19 @@ fn use_ast_id(
     }
 }
 
+impl AstIdNode for ast::AsmExpr {}
+
+fn asm_expr_ast_id(
+    node: &SyntaxNode,
+    index_map: &mut ErasedAstIdNextIndexMap,
+) -> Option<ErasedFileAstId> {
+    if ast::AsmExpr::can_cast(node.kind()) {
+        Some(index_map.new_id(ErasedFileAstIdKind::AsmExpr, ()))
+    } else {
+        None
+    }
+}
+
 impl AstIdNode for ast::Impl {}
 
 fn impl_ast_id(
@@ -433,7 +453,6 @@ macro_rules! register_has_name_ast_id {
         )+
 
         fn has_name_ast_id(node: &SyntaxNode, index_map: &mut ErasedAstIdNextIndexMap) -> Option<ErasedFileAstId> {
-            let kind = node.kind();
             match_ast! {
                 match node {
                     $(
@@ -441,7 +460,6 @@ macro_rules! register_has_name_ast_id {
                             let name = node.$name_method();
                             let name = name.as_ref().map_or("", |it| it.text_non_mutable());
                             let result = ErasedHasNameFileAstId {
-                                kind,
                                 name,
                             };
                             Some(index_map.new_id(ErasedFileAstIdKind::$ident, result))
@@ -452,8 +470,7 @@ macro_rules! register_has_name_ast_id {
             }
         }
 
-        fn should_alloc_has_name(node: &SyntaxNode) -> bool {
-            let kind = node.kind();
+        fn should_alloc_has_name(kind: SyntaxKind) -> bool {
             false $( || ast::$ident::can_cast(kind) )*
         }
     };
@@ -483,7 +500,6 @@ macro_rules! register_assoc_item_ast_id {
             index_map: &mut ErasedAstIdNextIndexMap,
             parent: Option<&ErasedFileAstId>,
         ) -> Option<ErasedFileAstId> {
-            let kind = node.kind();
             match_ast! {
                 match node {
                     $(
@@ -491,7 +507,6 @@ macro_rules! register_assoc_item_ast_id {
                             let name = $name_callback(node);
                             let name = name.as_ref().map_or("", |it| it.text_non_mutable());
                             let properties = ErasedHasNameFileAstId {
-                                kind,
                                 name,
                             };
                             let result = ErasedAssocItemFileAstId {
@@ -506,8 +521,7 @@ macro_rules! register_assoc_item_ast_id {
             }
         }
 
-        fn should_alloc_assoc_item(node: &SyntaxNode) -> bool {
-            let kind = node.kind();
+        fn should_alloc_assoc_item(kind: SyntaxKind) -> bool {
             false $( || ast::$ident::can_cast(kind) )*
         }
     };
diff --git a/src/tools/rust-analyzer/crates/syntax/rust.ungram b/src/tools/rust-analyzer/crates/syntax/rust.ungram
index 3f439472337..4cbc88cfb5e 100644
--- a/src/tools/rust-analyzer/crates/syntax/rust.ungram
+++ b/src/tools/rust-analyzer/crates/syntax/rust.ungram
@@ -158,6 +158,7 @@ Item =
 | TypeAlias
 | Union
 | Use
+| AsmExpr
 
 MacroRules =
   Attr* Visibility?
@@ -409,7 +410,8 @@ OffsetOfExpr =
 // global_asm := "global_asm!(" format_string *("," format_string) *("," operand) [","] ")"
 // format_string := STRING_LITERAL / RAW_STRING_LITERAL
 AsmExpr =
-  Attr* 'builtin' '#' 'asm' '(' template:(Expr (',' Expr)*) (AsmPiece (',' AsmPiece)*)? ','? ')'
+  Attr* 'builtin' '#' ( 'asm' | 'global_asm' | 'naked_asm' )
+  '(' template:(Expr (',' Expr)*) (AsmPiece (',' AsmPiece)*)? ','? ')'
 
 // operand_expr := expr / "_" / expr "=>" expr / expr "=>" "_"
 AsmOperandExpr = in_expr:Expr ('=>' out_expr:Expr)?
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs
index e60243f2c91..e902516471d 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/edit_in_place.rs
@@ -406,42 +406,6 @@ impl ast::WhereClause {
     }
 }
 
-impl ast::TypeParam {
-    pub fn remove_default(&self) {
-        if let Some((eq, last)) = self
-            .syntax()
-            .children_with_tokens()
-            .find(|it| it.kind() == T![=])
-            .zip(self.syntax().last_child_or_token())
-        {
-            ted::remove_all(eq..=last);
-
-            // remove any trailing ws
-            if let Some(last) = self.syntax().last_token().filter(|it| it.kind() == WHITESPACE) {
-                last.detach();
-            }
-        }
-    }
-}
-
-impl ast::ConstParam {
-    pub fn remove_default(&self) {
-        if let Some((eq, last)) = self
-            .syntax()
-            .children_with_tokens()
-            .find(|it| it.kind() == T![=])
-            .zip(self.syntax().last_child_or_token())
-        {
-            ted::remove_all(eq..=last);
-
-            // remove any trailing ws
-            if let Some(last) = self.syntax().last_token().filter(|it| it.kind() == WHITESPACE) {
-                last.detach();
-            }
-        }
-    }
-}
-
 pub trait Removable: AstNode {
     fn remove(&self);
 }
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
index 79a9f4da338..2b862465420 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/generated/nodes.rs
@@ -118,6 +118,14 @@ impl AsmExpr {
     pub fn asm_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![asm]) }
     #[inline]
     pub fn builtin_token(&self) -> Option<SyntaxToken> { support::token(&self.syntax, T![builtin]) }
+    #[inline]
+    pub fn global_asm_token(&self) -> Option<SyntaxToken> {
+        support::token(&self.syntax, T![global_asm])
+    }
+    #[inline]
+    pub fn naked_asm_token(&self) -> Option<SyntaxToken> {
+        support::token(&self.syntax, T![naked_asm])
+    }
 }
 pub struct AsmLabel {
     pub(crate) syntax: SyntaxNode,
@@ -2087,6 +2095,7 @@ impl ast::HasAttrs for GenericParam {}
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub enum Item {
+    AsmExpr(AsmExpr),
     Const(Const),
     Enum(Enum),
     ExternBlock(ExternBlock),
@@ -2106,7 +2115,6 @@ pub enum Item {
     Use(Use),
 }
 impl ast::HasAttrs for Item {}
-impl ast::HasDocComments for Item {}
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
 pub enum Pat {
@@ -8409,6 +8417,10 @@ impl AstNode for GenericParam {
         }
     }
 }
+impl From<AsmExpr> for Item {
+    #[inline]
+    fn from(node: AsmExpr) -> Item { Item::AsmExpr(node) }
+}
 impl From<Const> for Item {
     #[inline]
     fn from(node: Const) -> Item { Item::Const(node) }
@@ -8482,7 +8494,8 @@ impl AstNode for Item {
     fn can_cast(kind: SyntaxKind) -> bool {
         matches!(
             kind,
-            CONST
+            ASM_EXPR
+                | CONST
                 | ENUM
                 | EXTERN_BLOCK
                 | EXTERN_CRATE
@@ -8504,6 +8517,7 @@ impl AstNode for Item {
     #[inline]
     fn cast(syntax: SyntaxNode) -> Option<Self> {
         let res = match syntax.kind() {
+            ASM_EXPR => Item::AsmExpr(AsmExpr { syntax }),
             CONST => Item::Const(Const { syntax }),
             ENUM => Item::Enum(Enum { syntax }),
             EXTERN_BLOCK => Item::ExternBlock(ExternBlock { syntax }),
@@ -8528,6 +8542,7 @@ impl AstNode for Item {
     #[inline]
     fn syntax(&self) -> &SyntaxNode {
         match self {
+            Item::AsmExpr(it) => &it.syntax,
             Item::Const(it) => &it.syntax,
             Item::Enum(it) => &it.syntax,
             Item::ExternBlock(it) => &it.syntax,
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 309332873cb..d67f24fda96 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/make.rs
@@ -680,7 +680,7 @@ pub fn expr_tuple(elements: impl IntoIterator<Item = ast::Expr>) -> ast::TupleEx
     let expr = elements.into_iter().format(", ");
     expr_from_text(&format!("({expr})"))
 }
-pub fn expr_assignment(lhs: ast::Expr, rhs: ast::Expr) -> ast::Expr {
+pub fn expr_assignment(lhs: ast::Expr, rhs: ast::Expr) -> ast::BinExpr {
     expr_from_text(&format!("{lhs} = {rhs}"))
 }
 fn expr_from_text<E: Into<ast::Expr> + AstNode>(text: &str) -> E {
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 17cc5f9c057..1ba61073151 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
@@ -440,6 +440,19 @@ impl SyntaxFactory {
         ast
     }
 
+    pub fn expr_assignment(&self, lhs: ast::Expr, rhs: ast::Expr) -> ast::BinExpr {
+        let ast = make::expr_assignment(lhs.clone(), rhs.clone()).clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_node(lhs.syntax().clone(), ast.lhs().unwrap().syntax().clone());
+            builder.map_node(rhs.syntax().clone(), ast.rhs().unwrap().syntax().clone());
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
     pub fn expr_bin(&self, lhs: ast::Expr, op: ast::BinaryOp, rhs: ast::Expr) -> ast::BinExpr {
         let ast::Expr::BinExpr(ast) =
             make::expr_bin_op(lhs.clone(), op, rhs.clone()).clone_for_update()
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 dc1eba1a1ab..7b719b5dec7 100644
--- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
+++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
@@ -1011,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);
     }
 
@@ -1941,6 +1940,7 @@ pub mod prelude {
             clone::Clone,                            // :clone
             cmp::{Eq, PartialEq},                    // :eq
             cmp::{Ord, PartialOrd},                  // :ord
+            convert::AsMut,                          // :as_mut
             convert::AsRef,                          // :as_ref
             convert::{From, Into, TryFrom, TryInto}, // :from
             default::Default,                        // :default
diff --git a/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml b/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml
index 35a5a4d82b2..1fc1da50a0a 100644
--- a/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml
+++ b/src/tools/rust-analyzer/lib/lsp-server/Cargo.toml
@@ -16,6 +16,9 @@ crossbeam-channel.workspace = true
 [dev-dependencies]
 lsp-types = "=0.95"
 ctrlc = "3.4.7"
+anyhow.workspace     = true
+rustc-hash.workspace = true
+toolchain.workspace  = true
 
 [lints]
 workspace = true
diff --git a/src/tools/rust-analyzer/lib/lsp-server/examples/goto_def.rs b/src/tools/rust-analyzer/lib/lsp-server/examples/goto_def.rs
deleted file mode 100644
index 6b3acda7bcd..00000000000
--- a/src/tools/rust-analyzer/lib/lsp-server/examples/goto_def.rs
+++ /dev/null
@@ -1,132 +0,0 @@
-//! A minimal example LSP server that can only respond to the `gotoDefinition` request. To use
-//! this example, execute it and then send an `initialize` request.
-//!
-//! ```no_run
-//! Content-Length: 85
-//!
-//! {"jsonrpc": "2.0", "method": "initialize", "id": 1, "params": {"capabilities": {}}}
-//! ```
-//!
-//! This will respond with a server response. Then send it a `initialized` notification which will
-//! have no response.
-//!
-//! ```no_run
-//! Content-Length: 59
-//!
-//! {"jsonrpc": "2.0", "method": "initialized", "params": {}}
-//! ```
-//!
-//! Once these two are sent, then we enter the main loop of the server. The only request this
-//! example can handle is `gotoDefinition`:
-//!
-//! ```no_run
-//! Content-Length: 159
-//!
-//! {"jsonrpc": "2.0", "method": "textDocument/definition", "id": 2, "params": {"textDocument": {"uri": "file://temp"}, "position": {"line": 1, "character": 1}}}
-//! ```
-//!
-//! To finish up without errors, send a shutdown request:
-//!
-//! ```no_run
-//! Content-Length: 67
-//!
-//! {"jsonrpc": "2.0", "method": "shutdown", "id": 3, "params": null}
-//! ```
-//!
-//! The server will exit the main loop and finally we send a `shutdown` notification to stop
-//! the server.
-//!
-//! ```
-//! Content-Length: 54
-//!
-//! {"jsonrpc": "2.0", "method": "exit", "params": null}
-//! ```
-
-#![allow(clippy::print_stderr)]
-
-use std::error::Error;
-
-use lsp_types::OneOf;
-use lsp_types::{
-    GotoDefinitionResponse, InitializeParams, ServerCapabilities, request::GotoDefinition,
-};
-
-use lsp_server::{Connection, ExtractError, Message, Request, RequestId, Response};
-
-fn main() -> Result<(), Box<dyn Error + Sync + Send>> {
-    // Note that  we must have our logging only write out to stderr.
-    eprintln!("starting generic LSP server");
-
-    // Create the transport. Includes the stdio (stdin and stdout) versions but this could
-    // also be implemented to use sockets or HTTP.
-    let (connection, io_threads) = Connection::stdio();
-
-    // Run the server and wait for the two threads to end (typically by trigger LSP Exit event).
-    let server_capabilities = serde_json::to_value(&ServerCapabilities {
-        definition_provider: Some(OneOf::Left(true)),
-        ..Default::default()
-    })
-    .unwrap();
-    let initialization_params = match connection.initialize(server_capabilities) {
-        Ok(it) => it,
-        Err(e) => {
-            if e.channel_is_disconnected() {
-                io_threads.join()?;
-            }
-            return Err(e.into());
-        }
-    };
-    main_loop(connection, initialization_params)?;
-    io_threads.join()?;
-
-    // Shut down gracefully.
-    eprintln!("shutting down server");
-    Ok(())
-}
-
-fn main_loop(
-    connection: Connection,
-    params: serde_json::Value,
-) -> Result<(), Box<dyn Error + Sync + Send>> {
-    let _params: InitializeParams = serde_json::from_value(params).unwrap();
-    eprintln!("starting example main loop");
-    for msg in &connection.receiver {
-        eprintln!("got msg: {msg:?}");
-        match msg {
-            Message::Request(req) => {
-                if connection.handle_shutdown(&req)? {
-                    return Ok(());
-                }
-                eprintln!("got request: {req:?}");
-                match cast::<GotoDefinition>(req) {
-                    Ok((id, params)) => {
-                        eprintln!("got gotoDefinition request #{id}: {params:?}");
-                        let result = Some(GotoDefinitionResponse::Array(Vec::new()));
-                        let result = serde_json::to_value(&result).unwrap();
-                        let resp = Response { id, result: Some(result), error: None };
-                        connection.sender.send(Message::Response(resp))?;
-                        continue;
-                    }
-                    Err(err @ ExtractError::JsonError { .. }) => panic!("{err:?}"),
-                    Err(ExtractError::MethodMismatch(req)) => req,
-                };
-                // ...
-            }
-            Message::Response(resp) => {
-                eprintln!("got response: {resp:?}");
-            }
-            Message::Notification(not) => {
-                eprintln!("got notification: {not:?}");
-            }
-        }
-    }
-    Ok(())
-}
-
-fn cast<R>(req: Request) -> Result<(RequestId, R::Params), ExtractError<Request>>
-where
-    R: lsp_types::request::Request,
-    R::Params: serde::de::DeserializeOwned,
-{
-    req.extract(R::METHOD)
-}
diff --git a/src/tools/rust-analyzer/lib/lsp-server/examples/manual_test.sh b/src/tools/rust-analyzer/lib/lsp-server/examples/manual_test.sh
new file mode 100755
index 00000000000..d028ac43301
--- /dev/null
+++ b/src/tools/rust-analyzer/lib/lsp-server/examples/manual_test.sh
@@ -0,0 +1,53 @@
+#!/usr/bin/env bash
+# Simple nine-packet LSP test for examples/minimal_lsp.rs
+# Usage (two tabs):
+#
+#   mkfifo /tmp/lsp_pipe          # one-time setup
+#   # tab 1 – run the server
+#   cat /tmp/lsp_pipe | cargo run --example minimal_lsp
+#
+#   # tab 2 – fire the packets (this script)
+#   bash examples/manual_test.sh          # blocks until server exits
+#
+# If you don’t use a second tab, run the script in the background:
+#
+#   bash examples/manual_test.sh &        # writer in background
+#   cat /tmp/lsp_pipe | cargo run --example minimal_lsp
+#
+# The script opens /tmp/lsp_pipe for writing (exec 3>) and sends each JSON
+# packet with a correct Content-Length header.
+#
+# One-liner alternative (single terminal, no FIFO):
+#
+#   cargo run --example minimal_lsp <<'EOF'
+#     … nine packets …
+#   EOF
+#
+# Both approaches feed identical bytes to minimal_lsp via stdin.
+
+set -eu
+PIPE=${1:-/tmp/lsp_pipe}
+
+mkfifo -m 600 "$PIPE" 2>/dev/null || true       # create once, ignore if exists
+
+# open write end so the fifo stays open
+exec 3> "$PIPE"
+
+send() {
+  local body=$1
+  local len=$(printf '%s' "$body" | wc -c)
+  printf 'Content-Length: %d\r\n\r\n%s' "$len" "$body" >&3
+}
+
+send '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"capabilities":{}}}'
+send '{"jsonrpc":"2.0","method":"initialized","params":{}}'
+send '{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///tmp/foo.rs","languageId":"rust","version":1,"text":"fn  main( ){println!(\"hi\") }"}}}'
+send '{"jsonrpc":"2.0","id":2,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///tmp/foo.rs"},"position":{"line":0,"character":0}}}'
+send '{"jsonrpc":"2.0","id":3,"method":"textDocument/hover","params":{"textDocument":{"uri":"file:///tmp/foo.rs"},"position":{"line":0,"character":0}}}'
+send '{"jsonrpc":"2.0","id":4,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///tmp/foo.rs"},"position":{"line":0,"character":0}}}'
+send '{"jsonrpc":"2.0","id":5,"method":"textDocument/formatting","params":{"textDocument":{"uri":"file:///tmp/foo.rs"},"options":{"tabSize":4,"insertSpaces":true}}}'
+send '{"jsonrpc":"2.0","id":6,"method":"shutdown","params":null}'
+send '{"jsonrpc":"2.0","method":"exit","params":null}'
+
+exec 3>&-
+echo "Packets sent – watch the other terminal for responses."
diff --git a/src/tools/rust-analyzer/lib/lsp-server/examples/minimal_lsp.rs b/src/tools/rust-analyzer/lib/lsp-server/examples/minimal_lsp.rs
new file mode 100644
index 00000000000..5eef999e062
--- /dev/null
+++ b/src/tools/rust-analyzer/lib/lsp-server/examples/minimal_lsp.rs
@@ -0,0 +1,335 @@
+//! Minimal Language‑Server‑Protocol example: **`minimal_lsp.rs`**
+//! =============================================================
+//!
+//! | ↔ / ← | LSP method | What the implementation does |
+//! |-------|------------|------------------------------|
+//! | ↔ | `initialize` / `initialized` | capability handshake |
+//! | ← | `textDocument/publishDiagnostics` | pushes a dummy info diagnostic whenever the buffer changes |
+//! | ← | `textDocument/definition` | echoes an empty location array so the jump works |
+//! | ← | `textDocument/completion` | offers one hard‑coded item `HelloFromLSP` |
+//! | ← | `textDocument/hover` | shows *Hello from minimal_lsp* markdown |
+//! | ← | `textDocument/formatting` | pipes the doc through **rustfmt** and returns a full‑file edit |
+//!
+//! ### Quick start
+//! ```bash
+//! cd rust-analyzer/lib/lsp-server
+//! cargo run --example minimal_lsp
+//! ```
+//!
+//! ### Minimal manual session (all nine packets)
+//! ```no_run
+//! # 1. initialize - server replies with capabilities
+//! Content-Length: 85
+
+//! {"jsonrpc":"2.0","id":1,"method":"initialize","params":{"capabilities":{}}}
+//!
+//! # 2. initialized - no response expected
+//! Content-Length: 59
+
+//! {"jsonrpc":"2.0","method":"initialized","params":{}}
+//!
+//! # 3. didOpen - provide initial buffer text
+//! Content-Length: 173
+
+//! {"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///tmp/foo.rs","languageId":"rust","version":1,"text":"fn  main( ){println!(\"hi\") }"}}}
+//!
+//! # 4. completion - expect HelloFromLSP
+//! Content-Length: 139
+
+//! {"jsonrpc":"2.0","id":2,"method":"textDocument/completion","params":{"textDocument":{"uri":"file:///tmp/foo.rs"},"position":{"line":0,"character":0}}}
+//!
+//! # 5. hover - expect markdown greeting
+//! Content-Length: 135
+
+//! {"jsonrpc":"2.0","id":3,"method":"textDocument/hover","params":{"textDocument":{"uri":"file:///tmp/foo.rs"},"position":{"line":0,"character":0}}}
+//!
+//! # 6. goto-definition - dummy empty array
+//! Content-Length: 139
+
+//! {"jsonrpc":"2.0","id":4,"method":"textDocument/definition","params":{"textDocument":{"uri":"file:///tmp/foo.rs"},"position":{"line":0,"character":0}}}
+//!
+//! # 7. formatting - rustfmt full document
+//! Content-Length: 157
+
+//! {"jsonrpc":"2.0","id":5,"method":"textDocument/formatting","params":{"textDocument":{"uri":"file:///tmp/foo.rs"},"options":{"tabSize":4,"insertSpaces":true}}}
+//!
+//! # 8. shutdown request - server acks and prepares to exit
+//! Content-Length: 67
+
+//! {"jsonrpc":"2.0","id":6,"method":"shutdown","params":null}
+//!
+//! # 9. exit notification - terminates the server
+//! Content-Length: 54
+
+//! {"jsonrpc":"2.0","method":"exit","params":null}
+//! ```
+//!
+
+use std::{error::Error, io::Write};
+
+use rustc_hash::FxHashMap; // fast hash map
+use std::process::Stdio;
+use toolchain::command; // clippy-approved wrapper
+
+#[allow(clippy::print_stderr, clippy::disallowed_types, clippy::disallowed_methods)]
+use anyhow::{Context, Result, anyhow, bail};
+use lsp_server::{Connection, Message, Request as ServerRequest, RequestId, Response};
+use lsp_types::notification::Notification as _; // for METHOD consts
+use lsp_types::request::Request as _;
+use lsp_types::{
+    CompletionItem,
+    CompletionItemKind,
+    // capability helpers
+    CompletionOptions,
+    CompletionResponse,
+    Diagnostic,
+    DiagnosticSeverity,
+    DidChangeTextDocumentParams,
+    DidOpenTextDocumentParams,
+    DocumentFormattingParams,
+    Hover,
+    HoverContents,
+    HoverProviderCapability,
+    // core
+    InitializeParams,
+    MarkedString,
+    OneOf,
+    Position,
+    PublishDiagnosticsParams,
+    Range,
+    ServerCapabilities,
+    TextDocumentSyncCapability,
+    TextDocumentSyncKind,
+    TextEdit,
+    Url,
+    // notifications
+    notification::{DidChangeTextDocument, DidOpenTextDocument, PublishDiagnostics},
+    // requests
+    request::{Completion, Formatting, GotoDefinition, HoverRequest},
+}; // for METHOD consts
+
+// =====================================================================
+// main
+// =====================================================================
+
+#[allow(clippy::print_stderr)]
+fn main() -> std::result::Result<(), Box<dyn Error + Sync + Send>> {
+    log::error!("starting minimal_lsp");
+
+    // transport
+    let (connection, io_thread) = Connection::stdio();
+
+    // advertised capabilities
+    let caps = ServerCapabilities {
+        text_document_sync: Some(TextDocumentSyncCapability::Kind(TextDocumentSyncKind::FULL)),
+        completion_provider: Some(CompletionOptions::default()),
+        definition_provider: Some(OneOf::Left(true)),
+        hover_provider: Some(HoverProviderCapability::Simple(true)),
+        document_formatting_provider: Some(OneOf::Left(true)),
+        ..Default::default()
+    };
+    let init_value = serde_json::json!({
+        "capabilities": caps,
+        "offsetEncoding": ["utf-8"],
+    });
+
+    let init_params = connection.initialize(init_value)?;
+    main_loop(connection, init_params)?;
+    io_thread.join()?;
+    log::error!("shutting down server");
+    Ok(())
+}
+
+// =====================================================================
+// event loop
+// =====================================================================
+
+fn main_loop(
+    connection: Connection,
+    params: serde_json::Value,
+) -> std::result::Result<(), Box<dyn Error + Sync + Send>> {
+    let _init: InitializeParams = serde_json::from_value(params)?;
+    let mut docs: FxHashMap<Url, String> = FxHashMap::default();
+
+    for msg in &connection.receiver {
+        match msg {
+            Message::Request(req) => {
+                if connection.handle_shutdown(&req)? {
+                    break;
+                }
+                if let Err(err) = handle_request(&connection, &req, &mut docs) {
+                    log::error!("[lsp] request {} failed: {err}", &req.method);
+                }
+            }
+            Message::Notification(note) => {
+                if let Err(err) = handle_notification(&connection, &note, &mut docs) {
+                    log::error!("[lsp] notification {} failed: {err}", note.method);
+                }
+            }
+            Message::Response(resp) => log::error!("[lsp] response: {resp:?}"),
+        }
+    }
+    Ok(())
+}
+
+// =====================================================================
+// notifications
+// =====================================================================
+
+fn handle_notification(
+    conn: &Connection,
+    note: &lsp_server::Notification,
+    docs: &mut FxHashMap<Url, String>,
+) -> Result<()> {
+    match note.method.as_str() {
+        DidOpenTextDocument::METHOD => {
+            let p: DidOpenTextDocumentParams = serde_json::from_value(note.params.clone())?;
+            let uri = p.text_document.uri;
+            docs.insert(uri.clone(), p.text_document.text);
+            publish_dummy_diag(conn, &uri)?;
+        }
+        DidChangeTextDocument::METHOD => {
+            let p: DidChangeTextDocumentParams = serde_json::from_value(note.params.clone())?;
+            if let Some(change) = p.content_changes.into_iter().next() {
+                let uri = p.text_document.uri;
+                docs.insert(uri.clone(), change.text);
+                publish_dummy_diag(conn, &uri)?;
+            }
+        }
+        _ => {}
+    }
+    Ok(())
+}
+
+// =====================================================================
+// requests
+// =====================================================================
+
+fn handle_request(
+    conn: &Connection,
+    req: &ServerRequest,
+    docs: &mut FxHashMap<Url, String>,
+) -> Result<()> {
+    match req.method.as_str() {
+        GotoDefinition::METHOD => {
+            send_ok(conn, req.id.clone(), &lsp_types::GotoDefinitionResponse::Array(Vec::new()))?;
+        }
+        Completion::METHOD => {
+            let item = CompletionItem {
+                label: "HelloFromLSP".into(),
+                kind: Some(CompletionItemKind::FUNCTION),
+                detail: Some("dummy completion".into()),
+                ..Default::default()
+            };
+            send_ok(conn, req.id.clone(), &CompletionResponse::Array(vec![item]))?;
+        }
+        HoverRequest::METHOD => {
+            let hover = Hover {
+                contents: HoverContents::Scalar(MarkedString::String(
+                    "Hello from *minimal_lsp*".into(),
+                )),
+                range: None,
+            };
+            send_ok(conn, req.id.clone(), &hover)?;
+        }
+        Formatting::METHOD => {
+            let p: DocumentFormattingParams = serde_json::from_value(req.params.clone())?;
+            let uri = p.text_document.uri;
+            let text = docs
+                .get(&uri)
+                .ok_or_else(|| anyhow!("document not in cache – did you send DidOpen?"))?;
+            let formatted = run_rustfmt(text)?;
+            let edit = TextEdit { range: full_range(text), new_text: formatted };
+            send_ok(conn, req.id.clone(), &vec![edit])?;
+        }
+        _ => send_err(
+            conn,
+            req.id.clone(),
+            lsp_server::ErrorCode::MethodNotFound,
+            "unhandled method",
+        )?,
+    }
+    Ok(())
+}
+
+// =====================================================================
+// diagnostics
+// =====================================================================
+fn publish_dummy_diag(conn: &Connection, uri: &Url) -> Result<()> {
+    let diag = Diagnostic {
+        range: Range::new(Position::new(0, 0), Position::new(0, 1)),
+        severity: Some(DiagnosticSeverity::INFORMATION),
+        code: None,
+        code_description: None,
+        source: Some("minimal_lsp".into()),
+        message: "dummy diagnostic".into(),
+        related_information: None,
+        tags: None,
+        data: None,
+    };
+    let params =
+        PublishDiagnosticsParams { uri: uri.clone(), diagnostics: vec![diag], version: None };
+    conn.sender.send(Message::Notification(lsp_server::Notification::new(
+        PublishDiagnostics::METHOD.to_owned(),
+        params,
+    )))?;
+    Ok(())
+}
+
+// =====================================================================
+// helpers
+// =====================================================================
+
+fn run_rustfmt(input: &str) -> Result<String> {
+    let cwd = std::env::current_dir().expect("can't determine CWD");
+    let mut child = command("rustfmt", &cwd, &FxHashMap::default())
+        .arg("--emit")
+        .arg("stdout")
+        .stdin(Stdio::piped())
+        .stdout(Stdio::piped())
+        .stderr(Stdio::piped())
+        .spawn()
+        .context("failed to spawn rustfmt – is it installed?")?;
+
+    let Some(stdin) = child.stdin.as_mut() else {
+        bail!("stdin unavailable");
+    };
+    stdin.write_all(input.as_bytes())?;
+    let output = child.wait_with_output()?;
+    if !output.status.success() {
+        let stderr = String::from_utf8_lossy(&output.stderr);
+        bail!("rustfmt failed: {stderr}");
+    }
+    Ok(String::from_utf8(output.stdout)?)
+}
+
+fn full_range(text: &str) -> Range {
+    let last_line_idx = text.lines().count().saturating_sub(1) as u32;
+    let last_col = text.lines().last().map_or(0, |l| l.chars().count()) as u32;
+    Range::new(Position::new(0, 0), Position::new(last_line_idx, last_col))
+}
+
+fn send_ok<T: serde::Serialize>(conn: &Connection, id: RequestId, result: &T) -> Result<()> {
+    let resp = Response { id, result: Some(serde_json::to_value(result)?), error: None };
+    conn.sender.send(Message::Response(resp))?;
+    Ok(())
+}
+
+fn send_err(
+    conn: &Connection,
+    id: RequestId,
+    code: lsp_server::ErrorCode,
+    msg: &str,
+) -> Result<()> {
+    let resp = Response {
+        id,
+        result: None,
+        error: Some(lsp_server::ResponseError {
+            code: code as i32,
+            message: msg.into(),
+            data: None,
+        }),
+    };
+    conn.sender.send(Message::Response(resp))?;
+    Ok(())
+}
diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version
index 902793225ea..57ff326ce5a 100644
--- a/src/tools/rust-analyzer/rust-version
+++ b/src/tools/rust-analyzer/rust-version
@@ -1 +1 @@
-ad3b7257615c28aaf8212a189ec032b8af75de51
+a9fb6103b05c6ad6eee6bed4c0bb5a2e8e1024c6
diff --git a/src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs b/src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs
index d8cbf894520..b9f570fe0e3 100644
--- a/src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs
+++ b/src/tools/rust-analyzer/xtask/src/codegen/grammar/ast_src.rs
@@ -116,6 +116,8 @@ const CONTEXTUAL_KEYWORDS: &[&str] =
 // keywords we use for special macro expansions
 const CONTEXTUAL_BUILTIN_KEYWORDS: &[&str] = &[
     "asm",
+    "naked_asm",
+    "global_asm",
     "att_syntax",
     "builtin",
     "clobber_abi",
diff --git a/src/tools/rustbook/Cargo.lock b/src/tools/rustbook/Cargo.lock
index 27798d6aeb0..e363668d462 100644
--- a/src/tools/rustbook/Cargo.lock
+++ b/src/tools/rustbook/Cargo.lock
@@ -19,9 +19,9 @@ dependencies = [
 
 [[package]]
 name = "ammonia"
-version = "4.1.0"
+version = "4.1.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ada2ee439075a3e70b6992fce18ac4e407cd05aea9ca3f75d2c0b0c20bbb364"
+checksum = "d6b346764dd0814805de8abf899fe03065bcee69bb1a4771c785817e39f3978f"
 dependencies = [
  "cssparser",
  "html5ever",
@@ -156,9 +156,9 @@ checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
 
 [[package]]
 name = "cc"
-version = "1.2.29"
+version = "1.2.30"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c1599538de2394445747c8cf7935946e3cc27e9625f889d979bfb2aaf569362"
+checksum = "deec109607ca693028562ed836a5f1c4b8bd77755c4e132fc5ce11b0b6211ae7"
 dependencies = [
  "shlex",
 ]
@@ -185,9 +185,9 @@ dependencies = [
 
 [[package]]
 name = "clap"
-version = "4.5.40"
+version = "4.5.41"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "40b6887a1d8685cebccf115538db5c0efe625ccac9696ad45c409d96566e910f"
+checksum = "be92d32e80243a54711e5d7ce823c35c41c9d929dc4ab58e1276f625841aadf9"
 dependencies = [
  "clap_builder",
  "clap_derive",
@@ -195,9 +195,9 @@ dependencies = [
 
 [[package]]
 name = "clap_builder"
-version = "4.5.40"
+version = "4.5.41"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e0c66c08ce9f0c698cbce5c0279d0bb6ac936d8674174fe48f736533b964f59e"
+checksum = "707eab41e9622f9139419d573eca0900137718000c517d47da73045f54331c3d"
 dependencies = [
  "anstream",
  "anstyle",
@@ -208,18 +208,18 @@ dependencies = [
 
 [[package]]
 name = "clap_complete"
-version = "4.5.54"
+version = "4.5.55"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aad5b1b4de04fead402672b48897030eec1f3bfe1550776322f59f6d6e6a5677"
+checksum = "a5abde44486daf70c5be8b8f8f1b66c49f86236edf6fa2abadb4d961c4c6229a"
 dependencies = [
  "clap",
 ]
 
 [[package]]
 name = "clap_derive"
-version = "4.5.40"
+version = "4.5.41"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2c7947ae4cc3d851207c1adb5b5e260ff0cca11446b1d6d1423788e442257ce"
+checksum = "ef4f52386a59ca4c860f7393bcf8abd8dfd91ecccc0f774635ff68e92eeef491"
 dependencies = [
  "heck",
  "proc-macro2",
@@ -256,9 +256,9 @@ dependencies = [
 
 [[package]]
 name = "crc32fast"
-version = "1.4.2"
+version = "1.5.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3"
+checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
 dependencies = [
  "cfg-if",
 ]
@@ -582,12 +582,11 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
 
 [[package]]
 name = "html5ever"
-version = "0.31.0"
+version = "0.35.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "953cbbe631aae7fc0a112702ad5d3aaf09da38beaf45ea84610d6e1c358f569c"
+checksum = "55d958c2f74b664487a2035fe1dadb032c48718a03b63f3ab0b8537db8549ed4"
 dependencies = [
  "log",
- "mac",
  "markup5ever",
  "match_token",
 ]
@@ -863,9 +862,9 @@ checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
 
 [[package]]
 name = "markup5ever"
-version = "0.16.2"
+version = "0.35.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e4cd8c02f18a011991a039855480c64d74291c5792fcc160d55d77dc4de4a39"
+checksum = "311fe69c934650f8f19652b3946075f0fc41ad8757dbb68f1ca14e7900ecc1c3"
 dependencies = [
  "log",
  "tendril",
@@ -874,9 +873,9 @@ dependencies = [
 
 [[package]]
 name = "match_token"
-version = "0.1.0"
+version = "0.35.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88a9689d8d44bf9964484516275f5cd4c9b59457a6940c1d5d0ecbb94510a36b"
+checksum = "ac84fd3f360fcc43dc5f5d186f02a94192761a080e8bc58621ad4d12296a58cf"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1394,15 +1393,15 @@ dependencies = [
 
 [[package]]
 name = "rustix"
-version = "1.0.7"
+version = "1.0.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266"
+checksum = "11181fbabf243db407ef8df94a6ce0b2f9a733bd8be4ad02b4eda9602296cac8"
 dependencies = [
  "bitflags 2.9.1",
  "errno",
  "libc",
  "linux-raw-sys",
- "windows-sys 0.59.0",
+ "windows-sys 0.60.2",
 ]
 
 [[package]]
@@ -1460,9 +1459,9 @@ dependencies = [
 
 [[package]]
 name = "serde_json"
-version = "1.0.140"
+version = "1.0.141"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
+checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3"
 dependencies = [
  "itoa",
  "memchr",
@@ -2103,9 +2102,9 @@ checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486"
 
 [[package]]
 name = "winnow"
-version = "0.7.11"
+version = "0.7.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "74c7b26e3480b707944fc872477815d29a8e429d2f93a1ce000f5fa84a15cbcd"
+checksum = "f3edebf492c8125044983378ecb5766203ad3b4c2f7a922bd7dd207f6d443e95"
 dependencies = [
  "memchr",
 ]
diff --git a/src/tools/rustdoc-gui-test/src/main.rs b/src/tools/rustdoc-gui-test/src/main.rs
index 0e35861fbf7..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 {
@@ -134,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"))
@@ -154,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/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js
index f70fc917770..0baa179e16b 100644
--- a/src/tools/rustdoc-js/tester.js
+++ b/src/tools/rustdoc-js/tester.js
@@ -28,7 +28,14 @@ function readFile(filePath) {
 }
 
 function contentToDiffLine(key, value) {
-    return `"${key}": "${value}",`;
+    if (typeof value === "object" && !Array.isArray(value) && value !== null) {
+        const out = Object.entries(value)
+            .filter(([subKey, _]) => ["path", "name"].includes(subKey))
+            .map(([subKey, subValue]) => `"${subKey}": ${JSON.stringify(subValue)}`)
+            .join(", ");
+        return `"${key}": ${out},`;
+    }
+    return `"${key}": ${JSON.stringify(value)},`;
 }
 
 function shouldIgnoreField(fieldName) {
@@ -37,47 +44,61 @@ function shouldIgnoreField(fieldName) {
         fieldName === "proposeCorrectionTo";
 }
 
+function valueMapper(key, testOutput) {
+    const isAlias = testOutput["is_alias"];
+    let value = testOutput[key];
+    // To make our life easier, if there is a "parent" type, we add it to the path.
+    if (key === "path") {
+        if (testOutput["parent"] !== undefined) {
+            if (value.length > 0) {
+                value += "::" + testOutput["parent"]["name"];
+            } else {
+                value = testOutput["parent"]["name"];
+            }
+        } else if (testOutput["is_alias"]) {
+            value = valueMapper(key, testOutput["original"]);
+        }
+    } else if (isAlias && key === "alias") {
+        value = testOutput["name"];
+    } else if (isAlias && ["name"].includes(key)) {
+        value = testOutput["original"][key];
+    }
+    return value;
+}
+
 // This function is only called when no matching result was found and therefore will only display
 // the diff between the two items.
-function betterLookingDiff(entry, data) {
+function betterLookingDiff(expected, testOutput) {
     let output = " {\n";
-    const spaces = "     ";
-    for (const key in entry) {
-        if (!Object.prototype.hasOwnProperty.call(entry, key)) {
+    const spaces = "    ";
+    for (const key in expected) {
+        if (!Object.prototype.hasOwnProperty.call(expected, key)) {
             continue;
         }
-        if (!data || !Object.prototype.hasOwnProperty.call(data, key)) {
-            output += "-" + spaces + contentToDiffLine(key, entry[key]) + "\n";
+        if (!testOutput || !Object.prototype.hasOwnProperty.call(testOutput, key)) {
+            output += "-" + spaces + contentToDiffLine(key, expected[key]) + "\n";
             continue;
         }
-        const value = data[key];
-        if (value !== entry[key]) {
-            output += "-" + spaces + contentToDiffLine(key, entry[key]) + "\n";
+        const value = valueMapper(key, testOutput);
+        if (value !== expected[key]) {
+            output += "-" + spaces + contentToDiffLine(key, expected[key]) + "\n";
             output += "+" + spaces + contentToDiffLine(key, value) + "\n";
         } else {
-            output += spaces + contentToDiffLine(key, value) + "\n";
+            output += spaces + " " + contentToDiffLine(key, value) + "\n";
         }
     }
     return output + " }";
 }
 
-function lookForEntry(entry, data) {
-    return data.findIndex(data_entry => {
+function lookForEntry(expected, testOutput) {
+    return testOutput.findIndex(testOutputEntry => {
         let allGood = true;
-        for (const key in entry) {
-            if (!Object.prototype.hasOwnProperty.call(entry, key)) {
+        for (const key in expected) {
+            if (!Object.prototype.hasOwnProperty.call(expected, key)) {
                 continue;
             }
-            let value = data_entry[key];
-            // To make our life easier, if there is a "parent" type, we add it to the path.
-            if (key === "path" && data_entry["parent"] !== undefined) {
-                if (value.length > 0) {
-                    value += "::" + data_entry["parent"]["name"];
-                } else {
-                    value = data_entry["parent"]["name"];
-                }
-            }
-            if (value !== entry[key]) {
+            const value = valueMapper(key, testOutputEntry);
+            if (value !== expected[key]) {
                 allGood = false;
                 break;
             }
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/tidy/src/features.rs b/src/tools/tidy/src/features.rs
index e83b47e1380..fb00b3a943f 100644
--- a/src/tools/tidy/src/features.rs
+++ b/src/tools/tidy/src/features.rs
@@ -331,11 +331,9 @@ fn collect_lang_features_in(features: &mut Features, base: &Path, file: &str, ba
             continue;
         }
 
-        if in_feature_group {
-            if let Some(doc_comment) = line.strip_prefix("///") {
-                doc_comments.push(doc_comment.trim().to_string());
-                continue;
-            }
+        if in_feature_group && let Some(doc_comment) = line.strip_prefix("///") {
+            doc_comments.push(doc_comment.trim().to_string());
+            continue;
         }
 
         let mut parts = line.split(',');
@@ -465,19 +463,20 @@ 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(s) = list.get(name) {
-                    if f.tracking_issue != s.tracking_issue && f.level != Status::Accepted {
-                        tidy_error!(
-                            bad,
-                            "{}:{}: feature gate {} has inconsistent `issue`: \"{}\" mismatches the {} `issue` of \"{}\"",
-                            file.display(),
-                            line,
-                            name,
-                            f.tracking_issue_display(),
-                            display,
-                            s.tracking_issue_display(),
-                        );
-                    }
+                if let Some(s) = list.get(name)
+                    && f.tracking_issue != s.tracking_issue
+                    && f.level != Status::Accepted
+                {
+                    tidy_error!(
+                        bad,
+                        "{}:{}: feature gate {} has inconsistent `issue`: \"{}\" mismatches the {} `issue` of \"{}\"",
+                        file.display(),
+                        line,
+                        name,
+                        f.tracking_issue_display(),
+                        display,
+                        s.tracking_issue_display(),
+                    );
                 }
             };
             check_features(&f, lang_features, "corresponding lang feature");
diff --git a/src/tools/tidy/src/fluent_period.rs b/src/tools/tidy/src/fluent_period.rs
index 85c1ef6166a..836b5699289 100644
--- a/src/tools/tidy/src/fluent_period.rs
+++ b/src/tools/tidy/src/fluent_period.rs
@@ -33,14 +33,14 @@ fn check_period(filename: &str, contents: &str, bad: &mut bool) {
                 continue;
             }
 
-            if let Some(pat) = &m.value {
-                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 name = m.id.name;
-                        tidy_error!(bad, "{filename}:{ll}: message `{name}` ends in a period");
-                    }
+            if let Some(pat) = &m.value
+                && 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 name = m.id.name;
+                    tidy_error!(bad, "{filename}:{ll}: message `{name}` ends in a period");
                 }
             }
 
@@ -50,12 +50,13 @@ fn check_period(filename: &str, contents: &str, bad: &mut bool) {
                     continue;
                 }
 
-                if let Some(PatternElement::TextElement { value }) = attr.value.elements.last() {
-                    if value.ends_with(".") && !value.ends_with("...") {
-                        let ll = find_line(contents, value);
-                        let name = attr.id.name;
-                        tidy_error!(bad, "{filename}:{ll}: attr `{name}` ends in a period");
-                    }
+                if let Some(PatternElement::TextElement { value }) = attr.value.elements.last()
+                    && value.ends_with(".")
+                    && !value.ends_with("...")
+                {
+                    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 12fafd9a7ff..909bf482ddf 100644
--- a/src/tools/tidy/src/fluent_used.rs
+++ b/src/tools/tidy/src/fluent_used.rs
@@ -12,7 +12,7 @@ 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.
+    // so we check messages appear only once in all Rust and Fluent files.
     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()) {
diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt
index 8b57db23d01..77414bec82d 100644
--- a/src/tools/tidy/src/issues.txt
+++ b/src/tools/tidy/src/issues.txt
@@ -1021,7 +1021,6 @@ ui/foreign/issue-91370-foreign-fn-block-impl.rs
 ui/foreign/issue-99276-same-type-lifetimes.rs
 ui/function-pointer/issue-102289.rs
 ui/functions-closures/closure-expected-type/issue-38714.rs
-ui/generic-associated-types/bugs/issue-100013.rs
 ui/generic-associated-types/bugs/issue-80626.rs
 ui/generic-associated-types/bugs/issue-87735.rs
 ui/generic-associated-types/bugs/issue-87755.rs
@@ -1099,7 +1098,6 @@ ui/generic-associated-types/issue-90729.rs
 ui/generic-associated-types/issue-91139.rs
 ui/generic-associated-types/issue-91883.rs
 ui/generic-associated-types/issue-92033.rs
-ui/generic-associated-types/issue-92096.rs
 ui/generic-associated-types/issue-92280.rs
 ui/generic-associated-types/issue-92954.rs
 ui/generic-associated-types/issue-93141.rs
diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs
index ade4055b5bd..cb875504e1e 100644
--- a/src/tools/tidy/src/lib.rs
+++ b/src/tools/tidy/src/lib.rs
@@ -13,8 +13,9 @@ use termcolor::WriteColor;
 
 macro_rules! static_regex {
     ($re:literal) => {{
-        static RE: ::std::sync::OnceLock<::regex::Regex> = ::std::sync::OnceLock::new();
-        RE.get_or_init(|| ::regex::Regex::new($re).unwrap())
+        static RE: ::std::sync::LazyLock<::regex::Regex> =
+            ::std::sync::LazyLock::new(|| ::regex::Regex::new($re).unwrap());
+        &*RE
     }};
 }
 
@@ -134,7 +135,7 @@ pub fn files_modified(ci_info: &CiInfo, pred: impl Fn(&str) -> bool) -> bool {
         eprintln!("No base commit, assuming all files are modified");
         return true;
     };
-    match crate::git_diff(&base_commit, "--name-status") {
+    match crate::git_diff(base_commit, "--name-status") {
         Some(output) => {
             let modified_files = output.lines().filter_map(|ln| {
                 let (status, name) = ln
diff --git a/src/tools/tidy/src/mir_opt_tests.rs b/src/tools/tidy/src/mir_opt_tests.rs
index 1efe71b1687..6119eb58383 100644
--- a/src/tools/tidy/src/mir_opt_tests.rs
+++ b/src/tools/tidy/src/mir_opt_tests.rs
@@ -55,22 +55,21 @@ fn check_dash_files(path: &Path, bless: bool, bad: &mut bool) {
         .filter(|e| e.file_type().is_file())
     {
         let path = file.path();
-        if path.extension() == Some("rs".as_ref()) {
-            if let Some(name) = path.file_name().and_then(|s| s.to_str()) {
-                if name.contains('-') {
-                    if !bless {
-                        tidy_error!(
-                            bad,
-                            "mir-opt test files should not have dashes in them: {}",
-                            path.display()
-                        );
-                    } else {
-                        let new_name = name.replace('-', "_");
-                        let mut new_path = path.to_owned();
-                        new_path.set_file_name(new_name);
-                        let _ = std::fs::rename(path, new_path);
-                    }
-                }
+        if path.extension() == Some("rs".as_ref())
+            && let Some(name) = path.file_name().and_then(|s| s.to_str())
+            && name.contains('-')
+        {
+            if !bless {
+                tidy_error!(
+                    bad,
+                    "mir-opt test files should not have dashes in them: {}",
+                    path.display()
+                );
+            } else {
+                let new_name = name.replace('-', "_");
+                let mut new_path = path.to_owned();
+                new_path.set_file_name(new_name);
+                let _ = std::fs::rename(path, new_path);
             }
         }
     }
diff --git a/src/tools/tidy/src/rustdoc_templates.rs b/src/tools/tidy/src/rustdoc_templates.rs
index dca3e8d9d25..597290a6a9a 100644
--- a/src/tools/tidy/src/rustdoc_templates.rs
+++ b/src/tools/tidy/src/rustdoc_templates.rs
@@ -26,7 +26,7 @@ pub fn check(librustdoc_path: &Path, bad: &mut bool) {
                         None
                     // Then we check if this a comment tag.
                     } else if *tag != "{#" {
-                        return Some(false);
+                        Some(false)
                     // And finally we check if the comment is empty (ie, only there to strip
                     // extra whitespace characters).
                     } else if let Some(start_pos) = line.rfind(tag) {
diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs
index 8dde4618ce5..35ed61eacc7 100644
--- a/src/tools/tidy/src/style.rs
+++ b/src/tools/tidy/src/style.rs
@@ -417,10 +417,10 @@ pub fn check(path: &Path, bad: &mut bool) {
             return;
         }
         // Shell completions are automatically generated
-        if let Some(p) = file.parent() {
-            if p.ends_with(Path::new("src/etc/completions")) {
-                return;
-            }
+        if let Some(p) = file.parent()
+            && p.ends_with(Path::new("src/etc/completions"))
+        {
+            return;
         }
         let [
             mut skip_cr,
@@ -604,25 +604,25 @@ pub fn check(path: &Path, bad: &mut bool) {
                         backtick_count += comment_text.chars().filter(|ch| *ch == '`').count();
                     }
                     comment_block = Some((start_line, backtick_count));
-                } else if let Some((start_line, backtick_count)) = comment_block.take() {
-                    if backtick_count % 2 == 1 {
-                        let mut err = |msg: &str| {
-                            tidy_error!(bad, "{}:{start_line}: {msg}", file.display());
-                        };
-                        let block_len = (i + 1) - start_line;
-                        if block_len == 1 {
-                            suppressible_tidy_err!(
-                                err,
-                                skip_odd_backticks,
-                                "comment with odd number of backticks"
-                            );
-                        } else {
-                            suppressible_tidy_err!(
-                                err,
-                                skip_odd_backticks,
-                                "{block_len}-line comment block with odd number of backticks"
-                            );
-                        }
+                } else if let Some((start_line, backtick_count)) = comment_block.take()
+                    && backtick_count % 2 == 1
+                {
+                    let mut err = |msg: &str| {
+                        tidy_error!(bad, "{}:{start_line}: {msg}", file.display());
+                    };
+                    let block_len = (i + 1) - start_line;
+                    if block_len == 1 {
+                        suppressible_tidy_err!(
+                            err,
+                            skip_odd_backticks,
+                            "comment with odd number of backticks"
+                        );
+                    } else {
+                        suppressible_tidy_err!(
+                            err,
+                            skip_odd_backticks,
+                            "{block_len}-line comment block with odd number of backticks"
+                        );
                     }
                 }
             }
diff --git a/src/tools/tidy/src/target_specific_tests.rs b/src/tools/tidy/src/target_specific_tests.rs
index 1a6fd3eaf2d..f4a6783abb6 100644
--- a/src/tools/tidy/src/target_specific_tests.rs
+++ b/src/tools/tidy/src/target_specific_tests.rs
@@ -30,17 +30,17 @@ pub fn check(tests_path: &Path, bad: &mut bool) {
                         comp_vec.push(component);
                     }
                 }
-            } 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([' ', '=']);
-                    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());
-                        info.target_arch.replace(arch);
-                    } else {
-                        eprintln!("{file}: seems to have a malformed --target value");
-                        *bad = true;
-                    }
+            } else if let Some(compile_flags) = directive.strip_prefix(COMPILE_FLAGS_HEADER)
+                && let Some((_, v)) = compile_flags.split_once("--target")
+            {
+                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());
+                    info.target_arch.replace(arch);
+                } else {
+                    eprintln!("{file}: seems to have a malformed --target value");
+                    *bad = true;
                 }
             }
         });
diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs
index 7e295731c56..b9d22ece597 100644
--- a/src/tools/tidy/src/ui_tests.rs
+++ b/src/tools/tidy/src/ui_tests.rs
@@ -161,31 +161,30 @@ pub fn check(root_path: &Path, bless: bool, bad: &mut bool) {
                     tidy_error!(bad, "Stray file with UI testing output: {:?}", file_path);
                 }
 
-                if let Ok(metadata) = fs::metadata(file_path) {
-                    if metadata.len() == 0 {
-                        tidy_error!(bad, "Empty file with UI testing output: {:?}", file_path);
-                    }
+                if let Ok(metadata) = fs::metadata(file_path)
+                    && metadata.len() == 0
+                {
+                    tidy_error!(bad, "Empty file with UI testing output: {:?}", file_path);
                 }
             }
 
-            if ext == "rs" {
-                if let Some(test_name) = static_regex!(r"^issues?[-_]?(\d{3,})").captures(testname)
-                {
-                    // these paths are always relative to the passed `path` and always UTF8
-                    let stripped_path = file_path
-                        .strip_prefix(path)
-                        .unwrap()
-                        .to_str()
-                        .unwrap()
-                        .replace(std::path::MAIN_SEPARATOR_STR, "/");
-
-                    if !remaining_issue_names.remove(stripped_path.as_str()) {
-                        tidy_error!(
-                            bad,
-                            "file `tests/{stripped_path}` must begin with a descriptive name, consider `{{reason}}-issue-{issue_n}.rs`",
-                            issue_n = &test_name[1],
-                        );
-                    }
+            if ext == "rs"
+                && let Some(test_name) = static_regex!(r"^issues?[-_]?(\d{3,})").captures(testname)
+            {
+                // these paths are always relative to the passed `path` and always UTF8
+                let stripped_path = file_path
+                    .strip_prefix(path)
+                    .unwrap()
+                    .to_str()
+                    .unwrap()
+                    .replace(std::path::MAIN_SEPARATOR_STR, "/");
+
+                if !remaining_issue_names.remove(stripped_path.as_str()) {
+                    tidy_error!(
+                        bad,
+                        "file `tests/{stripped_path}` must begin with a descriptive name, consider `{{reason}}-issue-{issue_n}.rs`",
+                        issue_n = &test_name[1],
+                    );
                 }
             }
         }
diff --git a/src/tools/tidy/src/x_version.rs b/src/tools/tidy/src/x_version.rs
index 6a5e9eca813..9f7f43c4000 100644
--- a/src/tools/tidy/src/x_version.rs
+++ b/src/tools/tidy/src/x_version.rs
@@ -25,12 +25,12 @@ pub fn check(root: &Path, cargo: &Path, bad: &mut bool) {
                 if let Some(version) = iter.next() {
                     // 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") {
-                            let version = version.strip_prefix("v").unwrap();
-                            installed = Some(Version::parse(version).unwrap());
-                            break;
-                        }
+                    if let Some(path) = iter.next()
+                        && path.contains("src/tools/x")
+                    {
+                        let version = version.strip_prefix("v").unwrap();
+                        installed = Some(Version::parse(version).unwrap());
+                        break;
                     };
                 }
             } else {
diff --git a/src/tools/unicode-table-generator/Cargo.toml b/src/tools/unicode-table-generator/Cargo.toml
index f8a500922d0..3ca6e9e316f 100644
--- a/src/tools/unicode-table-generator/Cargo.toml
+++ b/src/tools/unicode-table-generator/Cargo.toml
@@ -1,7 +1,7 @@
 [package]
 name = "unicode-table-generator"
 version = "0.1.0"
-edition = "2021"
+edition = "2024"
 
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
diff --git a/src/tools/unicode-table-generator/src/cascading_map.rs b/src/tools/unicode-table-generator/src/cascading_map.rs
index 1eb35e819c0..78a7bba3208 100644
--- a/src/tools/unicode-table-generator/src/cascading_map.rs
+++ b/src/tools/unicode-table-generator/src/cascading_map.rs
@@ -21,7 +21,7 @@ impl RawEmitter {
 
         let points = ranges
             .iter()
-            .flat_map(|r| (r.start..r.end).into_iter().collect::<Vec<u32>>())
+            .flat_map(|r| (r.start..r.end).collect::<Vec<u32>>())
             .collect::<Vec<u32>>();
 
         println!("there are {} points", points.len());
@@ -32,21 +32,20 @@ impl RawEmitter {
             // assert that there is no whitespace over the 0x3000 range.
             assert!(point <= 0x3000, "the highest unicode whitespace value has changed");
             let high_bytes = point as usize >> 8;
-            let codepoints = codepoints_by_high_bytes.entry(high_bytes).or_insert_with(Vec::new);
+            let codepoints = codepoints_by_high_bytes.entry(high_bytes).or_default();
             codepoints.push(point);
         }
 
         let mut bit_for_high_byte = 1u8;
         let mut arms = Vec::<String>::new();
 
-        let mut high_bytes: Vec<usize> =
-            codepoints_by_high_bytes.keys().map(|k| k.clone()).collect();
+        let mut high_bytes: Vec<usize> = codepoints_by_high_bytes.keys().copied().collect();
         high_bytes.sort();
         for high_byte in high_bytes {
             let codepoints = codepoints_by_high_bytes.get_mut(&high_byte).unwrap();
             if codepoints.len() == 1 {
                 let ch = codepoints.pop().unwrap();
-                arms.push(format!("{} => c as u32 == {:#04x}", high_byte, ch));
+                arms.push(format!("{high_byte} => c as u32 == {ch:#04x}"));
                 continue;
             }
             // more than 1 codepoint in this arm
@@ -54,8 +53,7 @@ impl RawEmitter {
                 map[(*codepoint & 0xff) as usize] |= bit_for_high_byte;
             }
             arms.push(format!(
-                "{} => WHITESPACE_MAP[c as usize & 0xff] & {} != 0",
-                high_byte, bit_for_high_byte
+                "{high_byte} => WHITESPACE_MAP[c as usize & 0xff] & {bit_for_high_byte} != 0"
             ));
             bit_for_high_byte <<= 1;
         }
@@ -68,7 +66,7 @@ impl RawEmitter {
         writeln!(&mut self.file, "pub const fn lookup(c: char) -> bool {{").unwrap();
         writeln!(&mut self.file, "    match c as u32 >> 8 {{").unwrap();
         for arm in arms {
-            writeln!(&mut self.file, "        {},", arm).unwrap();
+            writeln!(&mut self.file, "        {arm},").unwrap();
         }
         writeln!(&mut self.file, "        _ => false,").unwrap();
         writeln!(&mut self.file, "    }}").unwrap();
diff --git a/src/tools/unicode-table-generator/src/case_mapping.rs b/src/tools/unicode-table-generator/src/case_mapping.rs
index 00241b7ee0e..9c6454492e7 100644
--- a/src/tools/unicode-table-generator/src/case_mapping.rs
+++ b/src/tools/unicode-table-generator/src/case_mapping.rs
@@ -9,7 +9,7 @@ const INDEX_MASK: u32 = 1 << 22;
 pub(crate) fn generate_case_mapping(data: &UnicodeData) -> String {
     let mut file = String::new();
 
-    write!(file, "const INDEX_MASK: u32 = 0x{:x};", INDEX_MASK).unwrap();
+    write!(file, "const INDEX_MASK: u32 = 0x{INDEX_MASK:x};").unwrap();
     file.push_str("\n\n");
     file.push_str(HEADER.trim_start());
     file.push('\n');
diff --git a/src/tools/unicode-table-generator/src/main.rs b/src/tools/unicode-table-generator/src/main.rs
index 415db2c4dbc..6cdb82a87bd 100644
--- a/src/tools/unicode-table-generator/src/main.rs
+++ b/src/tools/unicode-table-generator/src/main.rs
@@ -160,15 +160,15 @@ fn load_data() -> UnicodeData {
                 .push(Codepoints::Single(row.codepoint));
         }
 
-        if let Some(mapped) = row.simple_lowercase_mapping {
-            if mapped != row.codepoint {
-                to_lower.insert(row.codepoint.value(), (mapped.value(), 0, 0));
-            }
+        if let Some(mapped) = row.simple_lowercase_mapping
+            && mapped != row.codepoint
+        {
+            to_lower.insert(row.codepoint.value(), (mapped.value(), 0, 0));
         }
-        if let Some(mapped) = row.simple_uppercase_mapping {
-            if mapped != row.codepoint {
-                to_upper.insert(row.codepoint.value(), (mapped.value(), 0, 0));
-            }
+        if let Some(mapped) = row.simple_uppercase_mapping
+            && mapped != row.codepoint
+        {
+            to_upper.insert(row.codepoint.value(), (mapped.value(), 0, 0));
         }
     }
 
@@ -196,12 +196,12 @@ fn load_data() -> UnicodeData {
                     .flat_map(|codepoints| match codepoints {
                         Codepoints::Single(c) => c
                             .scalar()
-                            .map(|ch| (ch as u32..ch as u32 + 1))
+                            .map(|ch| ch as u32..ch as u32 + 1)
                             .into_iter()
                             .collect::<Vec<_>>(),
                         Codepoints::Range(c) => c
                             .into_iter()
-                            .flat_map(|c| c.scalar().map(|ch| (ch as u32..ch as u32 + 1)))
+                            .flat_map(|c| c.scalar().map(|ch| ch as u32..ch as u32 + 1))
                             .collect::<Vec<_>>(),
                     })
                     .collect::<Vec<Range<u32>>>(),
@@ -236,7 +236,7 @@ fn main() {
     let ranges_by_property = &unicode_data.ranges;
 
     if let Some(path) = test_path {
-        std::fs::write(&path, generate_tests(&write_location, &ranges_by_property)).unwrap();
+        std::fs::write(&path, generate_tests(&write_location, ranges_by_property)).unwrap();
     }
 
     let mut total_bytes = 0;
@@ -246,9 +246,9 @@ fn main() {
 
         let mut emitter = RawEmitter::new();
         if property == &"White_Space" {
-            emit_whitespace(&mut emitter, &ranges);
+            emit_whitespace(&mut emitter, ranges);
         } else {
-            emit_codepoints(&mut emitter, &ranges);
+            emit_codepoints(&mut emitter, ranges);
         }
 
         modules.push((property.to_lowercase().to_string(), emitter.file));
@@ -288,7 +288,7 @@ fn main() {
         for line in contents.lines() {
             if !line.trim().is_empty() {
                 table_file.push_str("    ");
-                table_file.push_str(&line);
+                table_file.push_str(line);
             }
             table_file.push('\n');
         }
@@ -312,7 +312,7 @@ fn version() -> String {
     let start = readme.find(prefix).unwrap() + prefix.len();
     let end = readme.find(" of the Unicode Standard.").unwrap();
     let version =
-        readme[start..end].split('.').map(|v| v.parse::<u32>().expect(&v)).collect::<Vec<_>>();
+        readme[start..end].split('.').map(|v| v.parse::<u32>().expect(v)).collect::<Vec<_>>();
     let [major, minor, micro] = [version[0], version[1], version[2]];
 
     out.push_str(&format!("({major}, {minor}, {micro});\n"));
@@ -320,7 +320,7 @@ fn version() -> String {
 }
 
 fn fmt_list<V: std::fmt::Debug>(values: impl IntoIterator<Item = V>) -> String {
-    let pieces = values.into_iter().map(|b| format!("{:?}, ", b)).collect::<Vec<_>>();
+    let pieces = values.into_iter().map(|b| format!("{b:?}, ")).collect::<Vec<_>>();
     let mut out = String::new();
     let mut line = String::from("\n    ");
     for piece in pieces {
@@ -348,7 +348,7 @@ fn generate_tests(data_path: &str, ranges: &[(&str, Vec<Range<u32>>)]) -> String
     s.push_str("\nfn main() {\n");
 
     for (property, ranges) in ranges {
-        s.push_str(&format!(r#"    println!("Testing {}");"#, property));
+        s.push_str(&format!(r#"    println!("Testing {property}");"#));
         s.push('\n');
         s.push_str(&format!("    {}_true();\n", property.to_lowercase()));
         s.push_str(&format!("    {}_false();\n", property.to_lowercase()));
@@ -373,7 +373,7 @@ fn generate_tests(data_path: &str, ranges: &[(&str, Vec<Range<u32>>)]) -> String
         s.push_str("    }\n\n");
     }
 
-    s.push_str("}");
+    s.push('}');
     s
 }
 
@@ -388,7 +388,7 @@ fn generate_asserts(s: &mut String, property: &str, points: &[u32], truthy: bool
                 range.start,
             ));
         } else {
-            s.push_str(&format!("        for chn in {:?}u32 {{\n", range));
+            s.push_str(&format!("        for chn in {range:?}u32 {{\n"));
             s.push_str(&format!(
                 "            assert!({}unicode_data::{}::lookup(std::char::from_u32(chn).unwrap()), \"{{:?}}\", chn);\n",
                 if truthy { "" } else { "!" },
@@ -439,7 +439,7 @@ fn merge_ranges(ranges: &mut Vec<Range<u32>>) {
     let mut last_end = None;
     for range in ranges {
         if let Some(last) = last_end {
-            assert!(range.start > last, "{:?}", range);
+            assert!(range.start > last, "{range:?}");
         }
         last_end = Some(range.end);
     }
diff --git a/src/tools/unicode-table-generator/src/raw_emitter.rs b/src/tools/unicode-table-generator/src/raw_emitter.rs
index ee94d3c93a6..e9e0efc4594 100644
--- a/src/tools/unicode-table-generator/src/raw_emitter.rs
+++ b/src/tools/unicode-table-generator/src/raw_emitter.rs
@@ -156,10 +156,10 @@ pub fn emit_codepoints(emitter: &mut RawEmitter, ranges: &[Range<u32>]) {
     emitter.blank_line();
 
     let mut bitset = emitter.clone();
-    let bitset_ok = bitset.emit_bitset(&ranges).is_ok();
+    let bitset_ok = bitset.emit_bitset(ranges).is_ok();
 
     let mut skiplist = emitter.clone();
-    skiplist.emit_skiplist(&ranges);
+    skiplist.emit_skiplist(ranges);
 
     if bitset_ok && bitset.bytes_used <= skiplist.bytes_used {
         *emitter = bitset;
@@ -174,7 +174,7 @@ pub fn emit_whitespace(emitter: &mut RawEmitter, ranges: &[Range<u32>]) {
     emitter.blank_line();
 
     let mut cascading = emitter.clone();
-    cascading.emit_cascading_map(&ranges);
+    cascading.emit_cascading_map(ranges);
     *emitter = cascading;
     emitter.desc = String::from("cascading");
 }
@@ -272,7 +272,7 @@ impl Canonicalized {
         // for canonical when possible.
         while let Some((&to, _)) = mappings
             .iter()
-            .find(|(&to, _)| to == 0)
+            .find(|&(&to, _)| to == 0)
             .or_else(|| mappings.iter().max_by_key(|m| m.1.len()))
         {
             // Get the mapping with the most entries. Currently, no mapping can
@@ -311,10 +311,9 @@ impl Canonicalized {
                     }
                 }
             }
-            assert!(
-                unique_mapping
-                    .insert(to, UniqueMapping::Canonical(canonical_words.len()))
-                    .is_none()
+            assert_eq!(
+                unique_mapping.insert(to, UniqueMapping::Canonical(canonical_words.len())),
+                None
             );
             canonical_words.push(to);
 
@@ -340,14 +339,10 @@ impl Canonicalized {
         // We'll probably always have some slack though so this loop will still
         // be needed.
         for &w in unique_words {
-            if !unique_mapping.contains_key(&w) {
-                assert!(
-                    unique_mapping
-                        .insert(w, UniqueMapping::Canonical(canonical_words.len()))
-                        .is_none()
-                );
+            unique_mapping.entry(w).or_insert_with(|| {
                 canonical_words.push(w);
-            }
+                UniqueMapping::Canonical(canonical_words.len())
+            });
         }
         assert_eq!(canonicalized_words.len() + canonical_words.len(), unique_words.len());
         assert_eq!(unique_mapping.len(), unique_words.len());