about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2024-10-14 17:04:43 +0200
committerRalf Jung <post@ralfj.de>2024-10-14 17:04:43 +0200
commit9d579f5358d6722fa20cbf243e201defbd5d84b3 (patch)
tree456828aefcdd5ca1414414fdb876f8c528d82dff /src
parent5e6170b97f81b83041666170ceeadefe04d00fb4 (diff)
parent17a19e684cdf3ca088af8b4da6a6209d128913f4 (diff)
Merge from rustc
Diffstat (limited to 'src')
-rw-r--r--src/bootstrap/README.md6
-rw-r--r--src/bootstrap/src/bin/main.rs10
-rw-r--r--src/bootstrap/src/bin/rustc.rs7
-rw-r--r--src/bootstrap/src/core/build_steps/check.rs41
-rw-r--r--src/bootstrap/src/core/build_steps/compile.rs33
-rw-r--r--src/bootstrap/src/core/build_steps/doc.rs6
-rw-r--r--src/bootstrap/src/core/build_steps/format.rs1
-rw-r--r--src/bootstrap/src/core/build_steps/llvm.rs38
-rw-r--r--src/bootstrap/src/core/build_steps/setup.rs197
-rw-r--r--src/bootstrap/src/core/build_steps/setup/tests.rs9
-rw-r--r--src/bootstrap/src/core/build_steps/test.rs14
-rw-r--r--src/bootstrap/src/core/build_steps/tool.rs32
-rw-r--r--src/bootstrap/src/core/builder.rs75
-rw-r--r--src/bootstrap/src/core/builder/tests.rs33
-rw-r--r--src/bootstrap/src/core/config/config.rs84
-rw-r--r--src/bootstrap/src/core/config/flags.rs6
-rw-r--r--src/bootstrap/src/core/config/tests.rs68
-rw-r--r--src/bootstrap/src/core/download.rs40
-rw-r--r--src/bootstrap/src/core/sanity.rs7
-rw-r--r--src/bootstrap/src/lib.rs37
-rw-r--r--src/bootstrap/src/utils/change_tracker.rs5
-rw-r--r--src/bootstrap/src/utils/shared_helpers.rs2
-rw-r--r--src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile1
-rw-r--r--src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile1
-rw-r--r--src/ci/docker/host-x86_64/mingw-check/Dockerfile3
-rw-r--r--src/ci/docker/host-x86_64/x86_64-fuchsia/Dockerfile4
-rw-r--r--src/ci/docker/host-x86_64/x86_64-gnu-tools/Dockerfile3
-rwxr-xr-xsrc/ci/docker/run.sh1
-rw-r--r--src/ci/docker/scripts/emscripten.sh4
-rwxr-xr-xsrc/ci/docker/scripts/rfl-build.sh2
-rwxr-xr-xsrc/ci/docker/scripts/x86_64-gnu-llvm.sh16
-rw-r--r--src/ci/github-actions/jobs.yml3
-rwxr-xr-xsrc/ci/run.sh13
-rwxr-xr-xsrc/ci/scripts/setup-upstream-remote.sh24
-rw-r--r--src/ci/shared.sh12
m---------src/doc/book0
m---------src/doc/embedded-book0
m---------src/doc/nomicon0
m---------src/doc/reference0
m---------src/doc/rust-by-example0
m---------src/doc/rustc-dev-guide0
-rw-r--r--src/doc/rustc/src/platform-support.md4
-rw-r--r--src/doc/rustc/src/platform-support/csky-unknown-linux-gnuabiv2.md2
-rw-r--r--src/doc/rustc/src/platform-support/riscv32-unknown-none-elf.md2
-rw-r--r--src/doc/rustc/src/platform-support/riscv32e-unknown-none-elf.md30
-rw-r--r--src/doc/unstable-book/src/language-features/cfg-boolean-literals.md22
-rw-r--r--src/etc/completions/x.py.sh2
-rw-r--r--src/etc/rust_analyzer_eglot.el50
-rw-r--r--src/librustdoc/clean/cfg.rs18
-rw-r--r--src/librustdoc/clean/cfg/tests.rs51
-rw-r--r--src/librustdoc/clean/mod.rs12
-rw-r--r--src/librustdoc/clean/types.rs35
-rw-r--r--src/librustdoc/config.rs4
-rw-r--r--src/librustdoc/core.rs4
-rw-r--r--src/librustdoc/doctest.rs15
-rw-r--r--src/librustdoc/doctest/runner.rs6
-rw-r--r--src/librustdoc/formats/cache.rs16
-rw-r--r--src/librustdoc/html/escape.rs12
-rw-r--r--src/librustdoc/html/escape/tests.rs4
-rw-r--r--src/librustdoc/html/highlight.rs5
-rw-r--r--src/librustdoc/html/highlight/tests.rs4
-rw-r--r--src/librustdoc/html/layout.rs4
-rw-r--r--src/librustdoc/html/markdown.rs57
-rw-r--r--src/librustdoc/html/render/context.rs14
-rw-r--r--src/librustdoc/html/render/mod.rs38
-rw-r--r--src/librustdoc/html/render/print_item.rs4
-rw-r--r--src/librustdoc/html/render/search_index.rs10
-rw-r--r--src/librustdoc/html/render/span_map.rs5
-rw-r--r--src/librustdoc/html/render/write_shared.rs10
-rw-r--r--src/librustdoc/html/sources.rs8
-rw-r--r--src/librustdoc/html/static/css/rustdoc.css14
-rw-r--r--src/librustdoc/json/conversions.rs590
-rw-r--r--src/librustdoc/json/mod.rs23
-rw-r--r--src/librustdoc/lib.rs3
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs100
-rw-r--r--src/librustdoc/passes/collect_trait_impls.rs2
-rw-r--r--src/librustdoc/passes/lint/check_code_block_syntax.rs4
-rw-r--r--src/librustdoc/scrape_examples.rs10
-rw-r--r--src/librustdoc/theme.rs33
-rw-r--r--src/librustdoc/visit_ast.rs12
m---------src/llvm-project0
-rw-r--r--src/rustdoc-json-types/lib.rs8
-rw-r--r--src/tools/build_helper/src/git.rs80
m---------src/tools/cargo0
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs/allow_attributes_without_reason.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs/mod.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs/non_minimal_cfg.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs/utils.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/cfg_not_test.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/index_refutable_slice.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/len_zero.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/lifetimes.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_async_fn.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_doc.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_inline.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/module_style.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/returns.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/swap.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/trait_bounds.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/use_self.rs10
-rw-r--r--src/tools/clippy/clippy_utils/src/check_proc_macro.rs137
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs1
-rw-r--r--src/tools/clippy/clippy_utils/src/source.rs1
-rw-r--r--src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.fixed1
-rw-r--r--src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.rs1
-rw-r--r--src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.stderr2
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-12284.rs10
-rw-r--r--src/tools/clippy/tests/ui/dbg_macro/dbg_macro.fixed2
-rw-r--r--src/tools/clippy/tests/ui/dbg_macro/dbg_macro.rs2
-rw-r--r--src/tools/clippy/tests/ui/doc/doc-fixable.fixed2
-rw-r--r--src/tools/clippy/tests/ui/doc/doc-fixable.rs2
-rw-r--r--src/tools/clippy/tests/ui/get_unwrap.fixed1
-rw-r--r--src/tools/clippy/tests/ui/get_unwrap.rs1
-rw-r--r--src/tools/clippy/tests/ui/get_unwrap.stderr8
-rw-r--r--src/tools/clippy/tests/ui/needless_return.fixed8
-rw-r--r--src/tools/clippy/tests/ui/needless_return.rs8
-rw-r--r--src/tools/clippy/tests/ui/needless_return.stderr108
-rw-r--r--src/tools/clippy/tests/ui/transmute_float_to_int.fixed4
-rw-r--r--src/tools/clippy/tests/ui/transmute_float_to_int.rs4
-rw-r--r--src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed9
-rw-r--r--src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs9
-rw-r--r--src/tools/compiletest/src/command-list.rs23
-rw-r--r--src/tools/compiletest/src/common.rs12
-rw-r--r--src/tools/compiletest/src/debuggers.rs272
-rw-r--r--src/tools/compiletest/src/header.rs256
-rw-r--r--src/tools/compiletest/src/header/auxiliary.rs60
-rw-r--r--src/tools/compiletest/src/header/cfg.rs17
-rw-r--r--src/tools/compiletest/src/header/needs.rs8
-rw-r--r--src/tools/compiletest/src/header/test-auxillary/known_legacy_directive.rs1
-rw-r--r--src/tools/compiletest/src/header/tests.rs57
-rw-r--r--src/tools/compiletest/src/lib.rs294
-rw-r--r--src/tools/compiletest/src/main.rs6
-rw-r--r--src/tools/compiletest/src/runtest.rs166
-rw-r--r--src/tools/compiletest/src/runtest/debuginfo.rs2
-rw-r--r--src/tools/compiletest/src/runtest/incremental.rs38
-rw-r--r--src/tools/compiletest/src/runtest/ui.rs4
-rw-r--r--src/tools/compiletest/src/runtest/valgrind.rs34
-rw-r--r--src/tools/compiletest/src/tests.rs7
-rw-r--r--src/tools/coverage-dump/src/covfun.rs42
-rw-r--r--src/tools/jsondoclint/src/validator.rs2
-rw-r--r--src/tools/jsondoclint/src/validator/tests.rs38
-rw-r--r--src/tools/miri/src/diagnostics.rs4
-rw-r--r--src/tools/miri/src/eval.rs2
-rw-r--r--src/tools/miri/src/intrinsics/mod.rs31
-rw-r--r--src/tools/miri/src/lib.rs1
-rw-r--r--src/tools/miri/src/machine.rs10
-rw-r--r--src/tools/miri/tests/pass/float.rs18
-rw-r--r--src/tools/miri/tests/pass/intrinsics/fmuladd_nondeterministic.rs44
-rw-r--r--src/tools/miri/tests/pass/underscore_pattern.rs51
-rw-r--r--src/tools/opt-dist/src/tests.rs1
-rw-r--r--src/tools/run-make-support/src/diff/tests.rs66
-rw-r--r--src/tools/run-make-support/src/macros.rs24
-rw-r--r--src/tools/rust-analyzer/.github/workflows/release.yaml6
-rw-r--r--src/tools/rust-analyzer/Cargo.lock13
-rw-r--r--src/tools/rust-analyzer/Cargo.toml2
-rw-r--r--src/tools/rust-analyzer/crates/cfg/src/lib.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs14
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs21
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/db.rs17
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs4
-rw-r--r--src/tools/rust-analyzer/crates/hir-expand/src/lib.rs24
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs3
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/db.rs10
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check/case_conv.rs8
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs (renamed from src/tools/rust-analyzer/crates/hir-ty/src/object_safety.rs)57
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility/tests.rs (renamed from src/tools/rust-analyzer/crates/hir-ty/src/object_safety/tests.rs)78
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/lib.rs2
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs125
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/lib.rs13
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/semantics.rs130
-rw-r--r--src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs16
-rw-r--r--src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs101
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/config.rs3
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/context.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/lib.rs25
-rw-r--r--src/tools/rust-analyzer/crates/ide-completion/src/tests.rs5
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs16
-rw-r--r--src/tools/rust-analyzer/crates/ide-db/src/search.rs10
-rw-r--r--src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs30
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/goto_definition.rs20
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs2
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover/render.rs48
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/hover/tests.rs38
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/lib.rs4
-rw-r--r--src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs3
-rw-r--r--src/tools/rust-analyzer/crates/load-cargo/src/lib.rs7
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/lexed_str.rs6
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs1
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/tests.rs2
-rw-r--r--src/tools/rust-analyzer/crates/project-model/src/workspace.rs44
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs1
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs9
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs7
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs1
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs1
-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/cli/scip.rs10
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs4
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs27
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs8
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs14
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs2
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs94
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs12
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs1
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/capabilities.rs20
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs1
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs123
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs25
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs12
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs46
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/tracing/config.rs78
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/tracing/hprof.rs70
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/tracing/json.rs90
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs1
-rw-r--r--src/tools/rust-analyzer/crates/span/src/hygiene.rs9
-rw-r--r--src/tools/rust-analyzer/crates/span/src/map.rs11
-rw-r--r--src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs32
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast.rs3
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory.rs45
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs110
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs123
-rw-r--r--src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs8
-rw-r--r--src/tools/rust-analyzer/crates/vfs/src/loader.rs4
-rw-r--r--src/tools/rust-analyzer/docs/dev/README.md10
-rw-r--r--src/tools/rust-analyzer/docs/dev/architecture.md2
-rw-r--r--src/tools/rust-analyzer/docs/dev/lsp-extensions.md2
-rw-r--r--src/tools/rust-analyzer/docs/dev/syntax.md2
-rw-r--r--src/tools/rust-analyzer/docs/user/generated_config.adoc5
-rw-r--r--src/tools/rust-analyzer/editors/code/package.json15
-rw-r--r--src/tools/rust-analyzer/editors/code/src/bootstrap.ts7
-rw-r--r--src/tools/rust-analyzer/editors/code/src/config.ts2
-rw-r--r--src/tools/rust-analyzer/editors/code/src/debug.ts55
-rw-r--r--src/tools/rust-analyzer/editors/code/src/main.ts5
-rw-r--r--src/tools/rust-analyzer/editors/code/src/run.ts2
-rw-r--r--src/tools/rust-analyzer/editors/code/src/toolchain.ts2
-rw-r--r--src/tools/rust-analyzer/rust-version2
-rw-r--r--src/tools/rustdoc-gui/tester.js26
-rw-r--r--src/tools/rustfmt/src/attr.rs20
-rw-r--r--src/tools/rustfmt/src/overflow.rs24
-rw-r--r--src/tools/rustfmt/src/parse/macros/asm.rs2
-rw-r--r--src/tools/rustfmt/src/parse/session.rs8
-rw-r--r--src/tools/rustfmt/src/skip.rs4
-rw-r--r--src/tools/rustfmt/src/spanned.rs2
-rw-r--r--src/tools/rustfmt/src/types.rs2
-rw-r--r--src/tools/rustfmt/src/utils.rs8
-rw-r--r--src/tools/rustfmt/tests/target/anonymous-types.rs31
-rw-r--r--src/tools/tidy/src/allowed_run_make_makefiles.txt1
-rw-r--r--src/tools/tidy/src/deps.rs3
-rw-r--r--src/tools/tidy/src/issues.txt5
-rw-r--r--src/tools/unicode-table-generator/src/raw_emitter.rs8
-rw-r--r--src/tools/wasm-component-ld/Cargo.toml2
-rw-r--r--src/version2
267 files changed, 3778 insertions, 2656 deletions
diff --git a/src/bootstrap/README.md b/src/bootstrap/README.md
index 0ac58645d2d..f036603ee70 100644
--- a/src/bootstrap/README.md
+++ b/src/bootstrap/README.md
@@ -182,10 +182,8 @@ Some general areas that you may be interested in modifying are:
   `Config` struct.
 * Adding a sanity check? Take a look at `bootstrap/src/core/sanity.rs`.
 
-If you make a major change on bootstrap configuration, please remember to:
-
-+ Update `CONFIG_CHANGE_HISTORY` in `src/bootstrap/src/utils/change_tracker.rs`.
-* Update `change-id = {pull-request-id}` in `config.example.toml`.
+If you make a major change on bootstrap configuration, please add a new entry to
+`CONFIG_CHANGE_HISTORY` in `src/bootstrap/src/utils/change_tracker.rs`.
 
 A 'major change' includes
 
diff --git a/src/bootstrap/src/bin/main.rs b/src/bootstrap/src/bin/main.rs
index b9df7336cca..e9ec79e417b 100644
--- a/src/bootstrap/src/bin/main.rs
+++ b/src/bootstrap/src/bin/main.rs
@@ -14,6 +14,7 @@ use bootstrap::{
     Build, CONFIG_CHANGE_HISTORY, Config, Flags, Subcommand, find_recent_config_change_ids,
     human_readable_changes, t,
 };
+use build_helper::ci::CiEnv;
 
 fn main() {
     let args = env::args().skip(1).collect::<Vec<_>>();
@@ -54,9 +55,12 @@ fn main() {
         };
     }
 
-    // check_version warnings are not printed during setup
-    let changelog_suggestion =
-        if matches!(config.cmd, Subcommand::Setup { .. }) { None } else { check_version(&config) };
+    // check_version warnings are not printed during setup, or during CI
+    let changelog_suggestion = if matches!(config.cmd, Subcommand::Setup { .. }) || CiEnv::is_ci() {
+        None
+    } else {
+        check_version(&config)
+    };
 
     // NOTE: Since `./configure` generates a `config.toml`, distro maintainers will see the
     // changelog warning, not the `x.py setup` message.
diff --git a/src/bootstrap/src/bin/rustc.rs b/src/bootstrap/src/bin/rustc.rs
index d04e2fbeb78..18f5a1a58db 100644
--- a/src/bootstrap/src/bin/rustc.rs
+++ b/src/bootstrap/src/bin/rustc.rs
@@ -95,7 +95,6 @@ fn main() {
     // When statically linking `std` into `rustc_driver`, remove `-C prefer-dynamic`
     if env::var("RUSTC_LINK_STD_INTO_RUSTC_DRIVER").unwrap() == "1"
         && crate_name == Some("rustc_driver")
-        && stage != "0"
     {
         if let Some(pos) = args.iter().enumerate().position(|(i, a)| {
             a == "-C" && args.get(i + 1).map(|a| a == "prefer-dynamic").unwrap_or(false)
@@ -137,6 +136,12 @@ fn main() {
         cmd.args(lint_flags.split_whitespace());
     }
 
+    // Conditionally pass `-Zon-broken-pipe=kill` to underlying rustc. Not all binaries want
+    // `-Zon-broken-pipe=kill`, which includes cargo itself.
+    if env::var_os("FORCE_ON_BROKEN_PIPE_KILL").is_some() {
+        cmd.arg("-Z").arg("on-broken-pipe=kill");
+    }
+
     if target.is_some() {
         // The stage0 compiler has a special sysroot distinct from what we
         // actually downloaded, so we just always pass the `--sysroot` option,
diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs
index 7671fc7e013..f419bebdc12 100644
--- a/src/bootstrap/src/core/build_steps/check.rs
+++ b/src/bootstrap/src/core/build_steps/check.rs
@@ -398,7 +398,14 @@ impl Step for RustAnalyzer {
 }
 
 macro_rules! tool_check_step {
-    ($name:ident, $path:literal, $($alias:literal, )* $source_type:path $(, $default:literal )?) => {
+    (
+        $name:ident,
+        $display_name:literal,
+        $path:literal,
+        $($alias:literal, )*
+        $source_type:path
+        $(, $default:literal )?
+    ) => {
         #[derive(Debug, Clone, PartialEq, Eq, Hash)]
         pub struct $name {
             pub target: TargetSelection,
@@ -441,7 +448,7 @@ macro_rules! tool_check_step {
                     cargo.arg("--all-targets");
                 }
 
-                let _guard = builder.msg_check(&concat!(stringify!($name), " artifacts").to_lowercase(), target);
+                let _guard = builder.msg_check(&format!("{} artifacts", $display_name), target);
                 run_cargo(
                     builder,
                     cargo,
@@ -468,20 +475,30 @@ macro_rules! tool_check_step {
     };
 }
 
-tool_check_step!(Rustdoc, "src/tools/rustdoc", "src/librustdoc", SourceType::InTree);
+tool_check_step!(Rustdoc, "rustdoc", "src/tools/rustdoc", "src/librustdoc", SourceType::InTree);
 // Clippy, miri and Rustfmt are hybrids. They are external tools, but use a git subtree instead
 // of a submodule. Since the SourceType only drives the deny-warnings
 // behavior, treat it as in-tree so that any new warnings in clippy will be
 // rejected.
-tool_check_step!(Clippy, "src/tools/clippy", SourceType::InTree);
-tool_check_step!(Miri, "src/tools/miri", SourceType::InTree);
-tool_check_step!(CargoMiri, "src/tools/miri/cargo-miri", SourceType::InTree);
-tool_check_step!(Rls, "src/tools/rls", SourceType::InTree);
-tool_check_step!(Rustfmt, "src/tools/rustfmt", SourceType::InTree);
-tool_check_step!(MiroptTestTools, "src/tools/miropt-test-tools", SourceType::InTree);
-tool_check_step!(TestFloatParse, "src/etc/test-float-parse", SourceType::InTree);
-
-tool_check_step!(Bootstrap, "src/bootstrap", SourceType::InTree, false);
+tool_check_step!(Clippy, "clippy", "src/tools/clippy", SourceType::InTree);
+tool_check_step!(Miri, "miri", "src/tools/miri", SourceType::InTree);
+tool_check_step!(CargoMiri, "cargo-miri", "src/tools/miri/cargo-miri", SourceType::InTree);
+tool_check_step!(Rls, "rls", "src/tools/rls", SourceType::InTree);
+tool_check_step!(Rustfmt, "rustfmt", "src/tools/rustfmt", SourceType::InTree);
+tool_check_step!(
+    MiroptTestTools,
+    "miropt-test-tools",
+    "src/tools/miropt-test-tools",
+    SourceType::InTree
+);
+tool_check_step!(
+    TestFloatParse,
+    "test-float-parse",
+    "src/etc/test-float-parse",
+    SourceType::InTree
+);
+
+tool_check_step!(Bootstrap, "bootstrap", "src/bootstrap", SourceType::InTree, false);
 
 /// Cargo's output path for the standard library in a given stage, compiled
 /// by a particular compiler for the specified target.
diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs
index eaa982d4e2b..27bbc8bd8ff 100644
--- a/src/bootstrap/src/core/build_steps/compile.rs
+++ b/src/bootstrap/src/core/build_steps/compile.rs
@@ -1053,8 +1053,19 @@ pub fn rustc_cargo(
 
     cargo.rustdocflag("-Zcrate-attr=warn(rust_2018_idioms)");
 
-    // If the rustc output is piped to e.g. `head -n1` we want the process to be
-    // killed, rather than having an error bubble up and cause a panic.
+    // If the rustc output is piped to e.g. `head -n1` we want the process to be killed, rather than
+    // having an error bubble up and cause a panic.
+    //
+    // FIXME(jieyouxu): this flag is load-bearing for rustc to not ICE on broken pipes, because
+    // rustc internally sometimes uses std `println!` -- but std `println!` by default will panic on
+    // broken pipes, and uncaught panics will manifest as an ICE. The compiler *should* handle this
+    // properly, but this flag is set in the meantime to paper over the I/O errors.
+    //
+    // See <https://github.com/rust-lang/rust/issues/131059> for details.
+    //
+    // Also see the discussion for properly handling I/O errors related to broken pipes, i.e. safe
+    // variants of `println!` in
+    // <https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Internal.20lint.20for.20raw.20.60print!.60.20and.20.60println!.60.3F>.
     cargo.rustflag("-Zon-broken-pipe=kill");
 
     if builder.config.llvm_enzyme {
@@ -1923,8 +1934,24 @@ impl Step for Assemble {
         let src_libdir = builder.sysroot_libdir(build_compiler, host);
         for f in builder.read_dir(&src_libdir) {
             let filename = f.file_name().into_string().unwrap();
-            if (is_dylib(&filename) || is_debug_info(&filename)) && !proc_macros.contains(&filename)
+
+            let is_proc_macro = proc_macros.contains(&filename);
+            let is_dylib_or_debug = is_dylib(&filename) || is_debug_info(&filename);
+
+            // If we link statically to stdlib, do not copy the libstd dynamic library file
+            // FIXME: Also do this for Windows once incremental post-optimization stage0 tests
+            // work without std.dll (see https://github.com/rust-lang/rust/pull/131188).
+            let can_be_rustc_dynamic_dep = if builder
+                .link_std_into_rustc_driver(target_compiler.host)
+                && !target_compiler.host.is_windows()
             {
+                let is_std = filename.starts_with("std-") || filename.starts_with("libstd-");
+                !is_std
+            } else {
+                true
+            };
+
+            if is_dylib_or_debug && can_be_rustc_dynamic_dep && !is_proc_macro {
                 builder.copy_link(&f.path(), &rustc_libdir.join(&filename));
             }
         }
diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs
index 3d504c3771f..ca2b8742647 100644
--- a/src/bootstrap/src/core/build_steps/doc.rs
+++ b/src/bootstrap/src/core/build_steps/doc.rs
@@ -82,7 +82,7 @@ book!(
     EditionGuide, "src/doc/edition-guide", "edition-guide", &[], submodule;
     EmbeddedBook, "src/doc/embedded-book", "embedded-book", &[], submodule;
     Nomicon, "src/doc/nomicon", "nomicon", &[], submodule;
-    RustByExample, "src/doc/rust-by-example", "rust-by-example", &["ja"], submodule;
+    RustByExample, "src/doc/rust-by-example", "rust-by-example", &["ja", "zh"], submodule;
     RustdocBook, "src/doc/rustdoc", "rustdoc", &[];
     StyleGuide, "src/doc/style-guide", "style-guide", &[];
 );
@@ -718,6 +718,10 @@ fn doc_std(
         .arg("--target-dir")
         .arg(&*target_dir.to_string_lossy())
         .arg("-Zskip-rustdoc-fingerprint")
+        .arg("-Zrustdoc-map")
+        .rustdocflag("--extern-html-root-url")
+        .rustdocflag("std_detect=https://docs.rs/std_detect/latest/")
+        .rustdocflag("--extern-html-root-takes-precedence")
         .rustdocflag("--resource-suffix")
         .rustdocflag(&builder.version);
     for arg in extra_args {
diff --git a/src/bootstrap/src/core/build_steps/format.rs b/src/bootstrap/src/core/build_steps/format.rs
index 952d8d73328..5ca4321d855 100644
--- a/src/bootstrap/src/core/build_steps/format.rs
+++ b/src/bootstrap/src/core/build_steps/format.rs
@@ -93,6 +93,7 @@ fn get_modified_rs_files(build: &Builder<'_>) -> Result<Option<Vec<String>>, Str
     if !verify_rustfmt_version(build) {
         return Ok(None);
     }
+
     get_git_modified_files(&build.config.git_config(), Some(&build.config.src), &["rs"])
 }
 
diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs
index 30213584157..a2d40f6fbd8 100644
--- a/src/bootstrap/src/core/build_steps/llvm.rs
+++ b/src/bootstrap/src/core/build_steps/llvm.rs
@@ -20,10 +20,9 @@ use build_helper::git::get_closest_merge_commit;
 
 use crate::core::builder::{Builder, RunConfig, ShouldRun, Step};
 use crate::core::config::{Config, TargetSelection};
-use crate::utils::channel;
 use crate::utils::exec::command;
 use crate::utils::helpers::{
-    self, HashStamp, exe, get_clang_cl_resource_dir, output, t, unhashed_basename, up_to_date,
+    self, HashStamp, exe, get_clang_cl_resource_dir, t, unhashed_basename, up_to_date,
 };
 use crate::{CLang, GitRepo, Kind, generate_smart_stamp_hash};
 
@@ -166,7 +165,7 @@ pub(crate) fn detect_llvm_sha(config: &Config, is_git: bool) -> String {
             config.src.join("src/version"),
         ])
         .unwrap()
-    } else if let Some(info) = channel::read_commit_info_file(&config.src) {
+    } else if let Some(info) = crate::utils::channel::read_commit_info_file(&config.src) {
         info.sha.trim().to_owned()
     } else {
         "".to_owned()
@@ -242,15 +241,29 @@ pub(crate) fn is_ci_llvm_available(config: &Config, asserts: bool) -> bool {
 
 /// Returns true if we're running in CI with modified LLVM (and thus can't download it)
 pub(crate) fn is_ci_llvm_modified(config: &Config) -> bool {
-    CiEnv::is_rust_lang_managed_ci_job() && config.rust_info.is_managed_git_subrepository() && {
-        // We assume we have access to git, so it's okay to unconditionally pass
-        // `true` here.
-        let llvm_sha = detect_llvm_sha(config, true);
-        let head_sha =
-            output(helpers::git(Some(&config.src)).arg("rev-parse").arg("HEAD").as_command_mut());
-        let head_sha = head_sha.trim();
-        llvm_sha == head_sha
+    // If not running in a CI environment, return false.
+    if !CiEnv::is_ci() {
+        return false;
+    }
+
+    // In rust-lang/rust managed CI, assert the existence of the LLVM submodule.
+    if CiEnv::is_rust_lang_managed_ci_job() {
+        assert!(
+            config.in_tree_llvm_info.is_managed_git_subrepository(),
+            "LLVM submodule must be fetched in rust-lang/rust managed CI builders."
+        );
     }
+    // If LLVM submodule isn't present, skip the change check as it won't work.
+    else if !config.in_tree_llvm_info.is_managed_git_subrepository() {
+        return false;
+    }
+
+    let llvm_sha = detect_llvm_sha(config, true);
+    let head_sha = crate::output(
+        helpers::git(Some(&config.src)).arg("rev-parse").arg("HEAD").as_command_mut(),
+    );
+    let head_sha = head_sha.trim();
+    llvm_sha == head_sha
 }
 
 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
@@ -1215,6 +1228,9 @@ fn supported_sanitizers(
         "aarch64-unknown-linux-ohos" => {
             common_libs("linux", "aarch64", &["asan", "lsan", "msan", "tsan", "hwasan"])
         }
+        "loongarch64-unknown-linux-gnu" | "loongarch64-unknown-linux-musl" => {
+            common_libs("linux", "loongarch64", &["asan", "lsan", "msan", "tsan"])
+        }
         "x86_64-apple-darwin" => darwin_libs("osx", &["asan", "lsan", "tsan"]),
         "x86_64-unknown-fuchsia" => common_libs("fuchsia", "x86_64", &["asan"]),
         "x86_64-apple-ios" => darwin_libs("iossim", &["asan", "tsan"]),
diff --git a/src/bootstrap/src/core/build_steps/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs
index 0ee2cb451f3..51964977933 100644
--- a/src/bootstrap/src/core/build_steps/setup.rs
+++ b/src/bootstrap/src/core/build_steps/setup.rs
@@ -35,21 +35,6 @@ pub enum Profile {
 
 static PROFILE_DIR: &str = "src/bootstrap/defaults";
 
-/// A list of historical hashes of `src/etc/rust_analyzer_settings.json`.
-/// New entries should be appended whenever this is updated so we can detect
-/// outdated vs. user-modified settings files.
-static SETTINGS_HASHES: &[&str] = &[
-    "ea67e259dedf60d4429b6c349a564ffcd1563cf41c920a856d1f5b16b4701ac8",
-    "56e7bf011c71c5d81e0bf42e84938111847a810eee69d906bba494ea90b51922",
-    "af1b5efe196aed007577899db9dae15d6dbc923d6fa42fa0934e68617ba9bbe0",
-    "3468fea433c25fff60be6b71e8a215a732a7b1268b6a83bf10d024344e140541",
-    "47d227f424bf889b0d899b9cc992d5695e1b78c406e183cd78eafefbe5488923",
-    "b526bd58d0262dd4dda2bff5bc5515b705fb668a46235ace3e057f807963a11a",
-    "828666b021d837a33e78d870b56d34c88a5e2c85de58b693607ec574f0c27000",
-    "811fb3b063c739d261fd8590dd30242e117908f5a095d594fa04585daa18ec4d",
-];
-static RUST_ANALYZER_SETTINGS: &str = include_str!("../../../../etc/rust_analyzer_settings.json");
-
 impl Profile {
     fn include_path(&self, src_path: &Path) -> PathBuf {
         PathBuf::from(format!("{}/{PROFILE_DIR}/config.{}.toml", src_path.display(), self))
@@ -533,46 +518,162 @@ undesirable, simply delete the `pre-push` file from .git/hooks."
     Ok(())
 }
 
-/// Sets up or displays `src/etc/rust_analyzer_settings.json`
+/// Handles editor-specific setup differences
+#[derive(Clone, Debug, Eq, PartialEq)]
+enum EditorKind {
+    Vscode,
+    Vim,
+    Emacs,
+    Helix,
+}
+
+impl EditorKind {
+    fn prompt_user() -> io::Result<Option<EditorKind>> {
+        let prompt_str = "Available editors:
+1. vscode
+2. vim
+3. emacs
+4. helix
+
+Select which editor you would like to set up [default: None]: ";
+
+        let mut input = String::new();
+        loop {
+            print!("{}", prompt_str);
+            io::stdout().flush()?;
+            input.clear();
+            io::stdin().read_line(&mut input)?;
+            match input.trim().to_lowercase().as_str() {
+                "1" | "vscode" => return Ok(Some(EditorKind::Vscode)),
+                "2" | "vim" => return Ok(Some(EditorKind::Vim)),
+                "3" | "emacs" => return Ok(Some(EditorKind::Emacs)),
+                "4" | "helix" => return Ok(Some(EditorKind::Helix)),
+                "" => return Ok(None),
+                _ => {
+                    eprintln!("ERROR: unrecognized option '{}'", input.trim());
+                    eprintln!("NOTE: press Ctrl+C to exit");
+                }
+            };
+        }
+    }
+
+    /// A list of historical hashes of each LSP settings file
+    /// New entries should be appended whenever this is updated so we can detect
+    /// outdated vs. user-modified settings files.
+    fn hashes(&self) -> Vec<&str> {
+        match self {
+            EditorKind::Vscode | EditorKind::Vim => vec![
+                "ea67e259dedf60d4429b6c349a564ffcd1563cf41c920a856d1f5b16b4701ac8",
+                "56e7bf011c71c5d81e0bf42e84938111847a810eee69d906bba494ea90b51922",
+                "af1b5efe196aed007577899db9dae15d6dbc923d6fa42fa0934e68617ba9bbe0",
+                "3468fea433c25fff60be6b71e8a215a732a7b1268b6a83bf10d024344e140541",
+                "47d227f424bf889b0d899b9cc992d5695e1b78c406e183cd78eafefbe5488923",
+                "b526bd58d0262dd4dda2bff5bc5515b705fb668a46235ace3e057f807963a11a",
+                "828666b021d837a33e78d870b56d34c88a5e2c85de58b693607ec574f0c27000",
+                "811fb3b063c739d261fd8590dd30242e117908f5a095d594fa04585daa18ec4d",
+            ],
+            EditorKind::Emacs => vec![
+                "51068d4747a13732440d1a8b8f432603badb1864fa431d83d0fd4f8fa57039e0",
+                "d29af4d949bbe2371eac928a3c31cf9496b1701aa1c45f11cd6c759865ad5c45",
+            ],
+            EditorKind::Helix => {
+                vec!["2d3069b8cf1b977e5d4023965eb6199597755e6c96c185ed5f2854f98b83d233"]
+            }
+        }
+    }
+
+    fn settings_path(&self, config: &Config) -> PathBuf {
+        config.src.join(self.settings_short_path())
+    }
+
+    fn settings_short_path(&self) -> PathBuf {
+        self.settings_folder().join(match self {
+            EditorKind::Vscode => "settings.json",
+            EditorKind::Vim => "coc-settings.json",
+            EditorKind::Emacs => ".dir-locals.el",
+            EditorKind::Helix => "languages.toml",
+        })
+    }
+
+    fn settings_folder(&self) -> PathBuf {
+        match self {
+            EditorKind::Vscode => PathBuf::from(".vscode"),
+            EditorKind::Vim => PathBuf::from(".vim"),
+            EditorKind::Emacs => PathBuf::new(),
+            EditorKind::Helix => PathBuf::from(".helix"),
+        }
+    }
+
+    fn settings_template(&self) -> &str {
+        match self {
+            EditorKind::Vscode | EditorKind::Vim => {
+                include_str!("../../../../etc/rust_analyzer_settings.json")
+            }
+            EditorKind::Emacs => include_str!("../../../../etc/rust_analyzer_eglot.el"),
+            EditorKind::Helix => include_str!("../../../../etc/rust_analyzer_helix.toml"),
+        }
+    }
+
+    fn backup_extension(&self) -> String {
+        format!("{}.bak", self.settings_short_path().extension().unwrap().to_str().unwrap())
+    }
+}
+
+/// Sets up or displays the LSP config for one of the supported editors
 #[derive(Clone, Debug, Eq, PartialEq, Hash)]
-pub struct Vscode;
+pub struct Editor;
 
-impl Step for Vscode {
+impl Step for Editor {
     type Output = ();
     const DEFAULT: bool = true;
+
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
-        run.alias("vscode")
+        run.alias("editor")
     }
+
     fn make_run(run: RunConfig<'_>) {
         if run.builder.config.dry_run() {
             return;
         }
         if let [cmd] = &run.paths[..] {
-            if cmd.assert_single_path().path.as_path().as_os_str() == "vscode" {
-                run.builder.ensure(Vscode);
+            if cmd.assert_single_path().path.as_path().as_os_str() == "editor" {
+                run.builder.ensure(Editor);
             }
         }
     }
+
     fn run(self, builder: &Builder<'_>) -> Self::Output {
         let config = &builder.config;
         if config.dry_run() {
             return;
         }
-        while !t!(create_vscode_settings_maybe(config)) {}
+        match EditorKind::prompt_user() {
+            Ok(editor_kind) => {
+                if let Some(editor_kind) = editor_kind {
+                    while !t!(create_editor_settings_maybe(config, editor_kind.clone())) {}
+                } else {
+                    println!("Ok, skipping editor setup!");
+                }
+            }
+            Err(e) => eprintln!("Could not determine the editor: {e}"),
+        }
     }
 }
 
-/// Create a `.vscode/settings.json` file for rustc development, or just print it
+/// Create the recommended editor LSP config file for rustc development, or just print it
 /// If this method should be re-called, it returns `false`.
-fn create_vscode_settings_maybe(config: &Config) -> io::Result<bool> {
-    let (current_hash, historical_hashes) = SETTINGS_HASHES.split_last().unwrap();
-    let vscode_settings = config.src.join(".vscode").join("settings.json");
-    // If None, no settings.json exists
+fn create_editor_settings_maybe(config: &Config, editor: EditorKind) -> io::Result<bool> {
+    let hashes = editor.hashes();
+    let (current_hash, historical_hashes) = hashes.split_last().unwrap();
+    let settings_path = editor.settings_path(config);
+    let settings_short_path = editor.settings_short_path();
+    let settings_filename = settings_short_path.to_str().unwrap();
+    // If None, no settings file exists
     // If Some(true), is a previous version of settings.json
     // If Some(false), is not a previous version (i.e. user modified)
     // If it's up to date we can just skip this
     let mut mismatched_settings = None;
-    if let Ok(current) = fs::read_to_string(&vscode_settings) {
+    if let Ok(current) = fs::read_to_string(&settings_path) {
         let mut hasher = sha2::Sha256::new();
         hasher.update(&current);
         let hash = hex_encode(hasher.finalize().as_slice());
@@ -585,20 +686,21 @@ fn create_vscode_settings_maybe(config: &Config) -> io::Result<bool> {
         }
     }
     println!(
-        "\nx.py can automatically install the recommended `.vscode/settings.json` file for rustc development"
+        "\nx.py can automatically install the recommended `{settings_filename}` file for rustc development"
     );
+
     match mismatched_settings {
-        Some(true) => eprintln!(
-            "WARNING: existing `.vscode/settings.json` is out of date, x.py will update it"
-        ),
+        Some(true) => {
+            eprintln!("WARNING: existing `{settings_filename}` is out of date, x.py will update it")
+        }
         Some(false) => eprintln!(
-            "WARNING: existing `.vscode/settings.json` has been modified by user, x.py will back it up and replace it"
+            "WARNING: existing `{settings_filename}` has been modified by user, x.py will back it up and replace it"
         ),
         _ => (),
     }
-    let should_create = match prompt_user(
-        "Would you like to create/update settings.json? (Press 'p' to preview values): [y/N]",
-    )? {
+    let should_create = match prompt_user(&format!(
+        "Would you like to create/update `{settings_filename}`? (Press 'p' to preview values): [y/N]"
+    ))? {
         Some(PromptResult::Yes) => true,
         Some(PromptResult::Print) => false,
         _ => {
@@ -607,9 +709,9 @@ fn create_vscode_settings_maybe(config: &Config) -> io::Result<bool> {
         }
     };
     if should_create {
-        let path = config.src.join(".vscode");
-        if !path.exists() {
-            fs::create_dir(&path)?;
+        let settings_folder_path = config.src.join(editor.settings_folder());
+        if !settings_folder_path.exists() {
+            fs::create_dir(settings_folder_path)?;
         }
         let verb = match mismatched_settings {
             // exists but outdated, we can replace this
@@ -617,18 +719,21 @@ fn create_vscode_settings_maybe(config: &Config) -> io::Result<bool> {
             // exists but user modified, back it up
             Some(false) => {
                 // exists and is not current version or outdated, so back it up
-                let mut backup = vscode_settings.clone();
-                backup.set_extension("json.bak");
-                eprintln!("WARNING: copying `settings.json` to `settings.json.bak`");
-                fs::copy(&vscode_settings, &backup)?;
+                let backup = settings_path.clone().with_extension(editor.backup_extension());
+                eprintln!(
+                    "WARNING: copying `{}` to `{}`",
+                    settings_path.file_name().unwrap().to_str().unwrap(),
+                    backup.file_name().unwrap().to_str().unwrap(),
+                );
+                fs::copy(&settings_path, &backup)?;
                 "Updated"
             }
             _ => "Created",
         };
-        fs::write(&vscode_settings, RUST_ANALYZER_SETTINGS)?;
-        println!("{verb} `.vscode/settings.json`");
+        fs::write(&settings_path, editor.settings_template())?;
+        println!("{verb} `{}`", settings_filename);
     } else {
-        println!("\n{RUST_ANALYZER_SETTINGS}");
+        println!("\n{}", editor.settings_template());
     }
     Ok(should_create)
 }
diff --git a/src/bootstrap/src/core/build_steps/setup/tests.rs b/src/bootstrap/src/core/build_steps/setup/tests.rs
index 3552224f33b..f3d4b6aa4db 100644
--- a/src/bootstrap/src/core/build_steps/setup/tests.rs
+++ b/src/bootstrap/src/core/build_steps/setup/tests.rs
@@ -1,16 +1,17 @@
 use sha2::Digest;
 
-use super::{RUST_ANALYZER_SETTINGS, SETTINGS_HASHES};
+use super::EditorKind;
 use crate::utils::helpers::hex_encode;
 
 #[test]
 fn check_matching_settings_hash() {
+    let editor = EditorKind::Vscode;
     let mut hasher = sha2::Sha256::new();
-    hasher.update(&RUST_ANALYZER_SETTINGS);
+    hasher.update(&editor.settings_template());
     let hash = hex_encode(hasher.finalize().as_slice());
     assert_eq!(
         &hash,
-        SETTINGS_HASHES.last().unwrap(),
-        "Update `SETTINGS_HASHES` with the new hash of `src/etc/rust_analyzer_settings.json`"
+        editor.hashes().last().unwrap(),
+        "Update `EditorKind::hashes()` with the new hash of `src/etc/rust_analyzer_settings.json`"
     );
 }
diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs
index 7283b0e9574..e25b571acba 100644
--- a/src/bootstrap/src/core/build_steps/test.rs
+++ b/src/bootstrap/src/core/build_steps/test.rs
@@ -1394,12 +1394,6 @@ default_test!(Ui { path: "tests/ui", mode: "ui", suite: "ui" });
 
 default_test!(Crashes { path: "tests/crashes", mode: "crashes", suite: "crashes" });
 
-default_test!(RunPassValgrind {
-    path: "tests/run-pass-valgrind",
-    mode: "run-pass-valgrind",
-    suite: "run-pass-valgrind"
-});
-
 default_test!(Codegen { path: "tests/codegen", mode: "codegen", suite: "codegen" });
 
 default_test!(CodegenUnits {
@@ -1703,7 +1697,11 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
         builder.ensure(TestHelpers { target: compiler.host });
 
         // ensure that `libproc_macro` is available on the host.
-        builder.ensure(compile::Std::new(compiler, compiler.host));
+        if suite == "mir-opt" {
+            builder.ensure(compile::Std::new_for_mir_opt_tests(compiler, compiler.host));
+        } else {
+            builder.ensure(compile::Std::new(compiler, compiler.host));
+        }
 
         // As well as the target
         if suite != "mir-opt" {
@@ -2088,7 +2086,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
         }
 
         if builder.config.profiler_enabled(target) {
-            cmd.arg("--profiler-support");
+            cmd.arg("--profiler-runtime");
         }
 
         cmd.env("RUST_TEST_TMPDIR", builder.tempdir());
diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs
index 64dfe054d9c..a01497c2bb9 100644
--- a/src/bootstrap/src/core/build_steps/tool.rs
+++ b/src/bootstrap/src/core/build_steps/tool.rs
@@ -209,11 +209,28 @@ pub fn prepare_tool_cargo(
     // See https://github.com/rust-lang/rust/issues/116538
     cargo.rustflag("-Zunstable-options");
 
-    // `-Zon-broken-pipe=kill` breaks cargo tests
+    // NOTE: The root cause of needing `-Zon-broken-pipe=kill` in the first place is because `rustc`
+    // and `rustdoc` doesn't gracefully handle I/O errors due to usages of raw std `println!` macros
+    // which panics upon encountering broken pipes. `-Zon-broken-pipe=kill` just papers over that
+    // and stops rustc/rustdoc ICEing on e.g. `rustc --print=sysroot | false`.
+    //
+    // cargo explicitly does not want the `-Zon-broken-pipe=kill` paper because it does actually use
+    // variants of `println!` that handles I/O errors gracefully. It's also a breaking change for a
+    // spawn process not written in Rust, especially if the language default handler is not
+    // `SIG_IGN`. Thankfully cargo tests will break if we do set the flag.
+    //
+    // For the cargo discussion, see
+    // <https://rust-lang.zulipchat.com/#narrow/stream/246057-t-cargo/topic/Applying.20.60-Zon-broken-pipe.3Dkill.60.20flags.20in.20bootstrap.3F>.
+    //
+    // For the rustc discussion, see
+    // <https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Internal.20lint.20for.20raw.20.60print!.60.20and.20.60println!.60.3F>
+    // for proper solutions.
     if !path.ends_with("cargo") {
-        // If the output is piped to e.g. `head -n1` we want the process to be killed,
-        // rather than having an error bubble up and cause a panic.
-        cargo.rustflag("-Zon-broken-pipe=kill");
+        // Use an untracked env var `FORCE_ON_BROKEN_PIPE_KILL` here instead of `RUSTFLAGS`.
+        // `RUSTFLAGS` is tracked by cargo. Conditionally omitting `-Zon-broken-pipe=kill` from
+        // `RUSTFLAGS` causes unnecessary tool rebuilds due to cache invalidation from building e.g.
+        // cargo *without* `-Zon-broken-pipe=kill` but then rustdoc *with* `-Zon-broken-pipe=kill`.
+        cargo.env("FORCE_ON_BROKEN_PIPE_KILL", "-Zon-broken-pipe=kill");
     }
 
     cargo
@@ -872,8 +889,11 @@ impl Step for LlvmBitcodeLinker {
     fn run(self, builder: &Builder<'_>) -> PathBuf {
         let bin_name = "llvm-bitcode-linker";
 
-        builder.ensure(compile::Std::new(self.compiler, self.compiler.host));
-        builder.ensure(compile::Rustc::new(self.compiler, self.target));
+        // If enabled, use ci-rustc and skip building the in-tree compiler.
+        if !builder.download_rustc() {
+            builder.ensure(compile::Std::new(self.compiler, self.compiler.host));
+            builder.ensure(compile::Rustc::new(self.compiler, self.target));
+        }
 
         let cargo = prepare_tool_cargo(
             builder,
diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs
index c35384ce3c0..9ac0b0a01f7 100644
--- a/src/bootstrap/src/core/builder.rs
+++ b/src/bootstrap/src/core/builder.rs
@@ -72,36 +72,40 @@ impl<'a> Deref for Builder<'a> {
 }
 
 pub trait Step: 'static + Clone + Debug + PartialEq + Eq + Hash {
-    /// `PathBuf` when directories are created or to return a `Compiler` once
-    /// it's been assembled.
+    /// Result type of `Step::run`.
     type Output: Clone;
 
-    /// Whether this step is run by default as part of its respective phase.
-    /// `true` here can still be overwritten by `should_run` calling `default_condition`.
+    /// Whether this step is run by default as part of its respective phase, as defined by the `describe`
+    /// macro in [`Builder::get_step_descriptions`].
+    ///
+    /// Note: Even if set to `true`, it can still be overridden with [`ShouldRun::default_condition`]
+    /// by `Step::should_run`.
     const DEFAULT: bool = false;
 
     /// If true, then this rule should be skipped if --target was specified, but --host was not
     const ONLY_HOSTS: bool = false;
 
-    /// Primary function to execute this rule. Can call `builder.ensure()`
-    /// with other steps to run those.
+    /// Primary function to implement `Step` logic.
+    ///
+    /// This function can be triggered in two ways:
+    ///     1. Directly from [`Builder::execute_cli`].
+    ///     2. Indirectly by being called from other `Step`s using [`Builder::ensure`].
     ///
-    /// This gets called twice during a normal `./x.py` execution: first
-    /// with `dry_run() == true`, and then for real.
+    /// When called with [`Builder::execute_cli`] (as done by `Build::build`), this function executed twice:
+    ///     - First in "dry-run" mode to validate certain things (like cyclic Step invocations,
+    ///         directory creation, etc) super quickly.
+    ///     - Then it's called again to run the actual, very expensive process.
+    ///
+    /// When triggered indirectly from other `Step`s, it may still run twice (as dry-run and real mode)
+    /// depending on the `Step::run` implementation of the caller.
     fn run(self, builder: &Builder<'_>) -> Self::Output;
 
-    /// When bootstrap is passed a set of paths, this controls whether this rule
-    /// will execute. However, it does not get called in a "default" context
-    /// when we are not passed any paths; in that case, `make_run` is called
-    /// directly.
+    /// Determines if this `Step` should be run when given specific paths (e.g., `x build $path`).
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_>;
 
-    /// Builds up a "root" rule, either as a default rule or from a path passed
-    /// to us.
-    ///
-    /// When path is `None`, we are executing in a context where no paths were
-    /// passed. When `./x.py build` is run, for example, this rule could get
-    /// called if it is in the correct list below with a path of `None`.
+    /// Called directly by the bootstrap `Step` handler when not triggered indirectly by other `Step`s using [`Builder::ensure`].
+    /// For example, `./x.py test bootstrap` runs this for `test::Bootstrap`. Similarly, `./x.py test` runs it for every step
+    /// that is listed by the `describe` macro in [`Builder::get_step_descriptions`].
     fn make_run(_run: RunConfig<'_>) {
         // It is reasonable to not have an implementation of make_run for rules
         // who do not want to get called from the root context. This means that
@@ -327,7 +331,6 @@ const PATH_REMAP: &[(&str, &[&str])] = &[
         "tests/mir-opt",
         "tests/pretty",
         "tests/run-make",
-        "tests/run-pass-valgrind",
         "tests/rustdoc",
         "tests/rustdoc-gui",
         "tests/rustdoc-js",
@@ -415,6 +418,15 @@ impl StepDescription {
             .map(|desc| (desc.should_run)(ShouldRun::new(builder, desc.kind)))
             .collect::<Vec<_>>();
 
+        if builder.download_rustc() && (builder.kind == Kind::Dist || builder.kind == Kind::Install)
+        {
+            eprintln!(
+                "ERROR: '{}' subcommand is incompatible with `rust.download-rustc`.",
+                builder.kind.as_str()
+            );
+            crate::exit!(1);
+        }
+
         // sanity checks on rules
         for (desc, should_run) in v.iter().zip(&should_runs) {
             assert!(
@@ -852,7 +864,6 @@ impl<'a> Builder<'a> {
                 test::Tidy,
                 test::Ui,
                 test::Crashes,
-                test::RunPassValgrind,
                 test::Coverage,
                 test::CoverageMap,
                 test::CoverageRun,
@@ -1000,7 +1011,9 @@ impl<'a> Builder<'a> {
                 run::GenerateWindowsSys,
                 run::GenerateCompletions,
             ),
-            Kind::Setup => describe!(setup::Profile, setup::Hook, setup::Link, setup::Vscode),
+            Kind::Setup => {
+                describe!(setup::Profile, setup::Hook, setup::Link, setup::Editor)
+            }
             Kind::Clean => describe!(clean::CleanAll, clean::Rustc, clean::Std),
             Kind::Vendor => describe!(vendor::Vendor),
             // special-cased in Build::build()
@@ -1685,10 +1698,24 @@ impl<'a> Builder<'a> {
         match mode {
             Mode::Std | Mode::ToolBootstrap | Mode::ToolStd => {}
             Mode::Rustc | Mode::Codegen | Mode::ToolRustc => {
-                // Build proc macros both for the host and the target
+                // Build proc macros both for the host and the target unless proc-macros are not
+                // supported by the target.
                 if target != compiler.host && cmd_kind != Kind::Check {
-                    cargo.arg("-Zdual-proc-macros");
-                    rustflags.arg("-Zdual-proc-macros");
+                    let error = command(self.rustc(compiler))
+                        .arg("--target")
+                        .arg(target.rustc_target_arg())
+                        .arg("--print=file-names")
+                        .arg("--crate-type=proc-macro")
+                        .arg("-")
+                        .run_capture(self)
+                        .stderr();
+                    let not_supported = error
+                        .lines()
+                        .any(|line| line.contains("unsupported crate type `proc-macro`"));
+                    if !not_supported {
+                        cargo.arg("-Zdual-proc-macros");
+                        rustflags.arg("-Zdual-proc-macros");
+                    }
                 }
             }
         }
diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs
index bd81dc930be..695a66834d4 100644
--- a/src/bootstrap/src/core/builder/tests.rs
+++ b/src/bootstrap/src/core/builder/tests.rs
@@ -212,6 +212,39 @@ fn alias_and_path_for_library() {
     assert_eq!(first(cache.all::<doc::Std>()), &[doc_std!(A => A, stage = 0)]);
 }
 
+#[test]
+fn ci_rustc_if_unchanged_logic() {
+    let config = Config::parse_inner(
+        Flags::parse(&[
+            "build".to_owned(),
+            "--dry-run".to_owned(),
+            "--set=rust.download-rustc='if-unchanged'".to_owned(),
+        ]),
+        |&_| Ok(Default::default()),
+    );
+
+    let build = Build::new(config.clone());
+    let builder = Builder::new(&build);
+
+    if config.out.exists() {
+        fs::remove_dir_all(&config.out).unwrap();
+    }
+
+    builder.run_step_descriptions(&Builder::get_step_descriptions(config.cmd.kind()), &[]);
+
+    // Make sure "if-unchanged" logic doesn't try to use CI rustc while there are changes
+    // in compiler and/or library.
+    if config.download_rustc_commit.is_some() {
+        let has_changes =
+            config.last_modified_commit(&["compiler", "library"], "download-rustc", true).is_none();
+
+        assert!(
+            !has_changes,
+            "CI-rustc can't be used with 'if-unchanged' while there are changes in compiler and/or library."
+        );
+    }
+}
+
 mod defaults {
     use pretty_assertions::assert_eq;
 
diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs
index 07460b81412..f768470c4ff 100644
--- a/src/bootstrap/src/core/config/config.rs
+++ b/src/bootstrap/src/core/config/config.rs
@@ -4,7 +4,7 @@
 //! how the build runs.
 
 use std::cell::{Cell, RefCell};
-use std::collections::{HashMap, HashSet};
+use std::collections::{BTreeSet, HashMap, HashSet};
 use std::fmt::{self, Display};
 use std::io::IsTerminal;
 use std::path::{Path, PathBuf, absolute};
@@ -13,6 +13,7 @@ use std::str::FromStr;
 use std::sync::OnceLock;
 use std::{cmp, env, fs};
 
+use build_helper::ci::CiEnv;
 use build_helper::exit;
 use build_helper::git::{GitConfig, get_closest_merge_commit, output_result};
 use serde::{Deserialize, Deserializer};
@@ -22,6 +23,7 @@ use crate::core::build_steps::compile::CODEGEN_BACKEND_PREFIX;
 use crate::core::build_steps::llvm;
 pub use crate::core::config::flags::Subcommand;
 use crate::core::config::flags::{Color, Flags, Warnings};
+use crate::core::download::is_download_ci_available;
 use crate::utils::cache::{INTERNER, Interned};
 use crate::utils::channel::{self, GitInfo};
 use crate::utils::helpers::{self, exe, output, t};
@@ -287,6 +289,7 @@ pub struct Config {
     pub rust_profile_generate: Option<String>,
     pub rust_lto: RustcLto,
     pub rust_validate_mir_opts: Option<u32>,
+    pub rust_std_features: BTreeSet<String>,
     pub llvm_profile_use: Option<String>,
     pub llvm_profile_generate: bool,
     pub llvm_libunwind_default: Option<LlvmLibunwind>,
@@ -1152,6 +1155,7 @@ define_config! {
         download_rustc: Option<StringOrBool> = "download-rustc",
         lto: Option<String> = "lto",
         validate_mir_opts: Option<u32> = "validate-mir-opts",
+        std_features: Option<BTreeSet<String>> = "std-features",
     }
 }
 
@@ -1625,9 +1629,11 @@ impl Config {
             config.mandir = mandir.map(PathBuf::from);
         }
 
+        config.llvm_assertions =
+            toml.llvm.as_ref().map_or(false, |llvm| llvm.assertions.unwrap_or(false));
+
         // Store off these values as options because if they're not provided
         // we'll infer default values for them later
-        let mut llvm_assertions = None;
         let mut llvm_tests = None;
         let mut llvm_enzyme = None;
         let mut llvm_plugins = None;
@@ -1645,6 +1651,7 @@ impl Config {
         let mut optimize = None;
         let mut omit_git_hash = None;
         let mut lld_enabled = None;
+        let mut std_features = None;
 
         let mut is_user_configured_rust_channel = false;
 
@@ -1703,12 +1710,14 @@ impl Config {
                 stack_protector,
                 strip,
                 lld_mode,
+                std_features: std_features_toml,
             } = rust;
 
             is_user_configured_rust_channel = channel.is_some();
             set(&mut config.channel, channel.clone());
 
-            config.download_rustc_commit = config.download_ci_rustc_commit(download_rustc);
+            config.download_rustc_commit =
+                config.download_ci_rustc_commit(download_rustc, config.llvm_assertions);
 
             debug = debug_toml;
             debug_assertions = debug_assertions_toml;
@@ -1722,6 +1731,7 @@ impl Config {
             debuginfo_level_tools = debuginfo_level_tools_toml;
             debuginfo_level_tests = debuginfo_level_tests_toml;
             lld_enabled = lld_enabled_toml;
+            std_features = std_features_toml;
 
             optimize = optimize_toml;
             omit_git_hash = omit_git_hash_toml;
@@ -1843,7 +1853,7 @@ impl Config {
                 optimize: optimize_toml,
                 thin_lto,
                 release_debuginfo,
-                assertions,
+                assertions: _,
                 tests,
                 enzyme,
                 plugins,
@@ -1877,7 +1887,6 @@ impl Config {
                 Some(StringOrBool::Bool(false)) | None => {}
             }
             set(&mut config.ninja_in_file, ninja);
-            llvm_assertions = assertions;
             llvm_tests = tests;
             llvm_enzyme = enzyme;
             llvm_plugins = plugins;
@@ -1906,8 +1915,8 @@ impl Config {
             config.llvm_enable_warnings = enable_warnings.unwrap_or(false);
             config.llvm_build_config = build_config.clone().unwrap_or(Default::default());
 
-            let asserts = llvm_assertions.unwrap_or(false);
-            config.llvm_from_ci = config.parse_download_ci_llvm(download_ci_llvm, asserts);
+            config.llvm_from_ci =
+                config.parse_download_ci_llvm(download_ci_llvm, config.llvm_assertions);
 
             if config.llvm_from_ci {
                 let warn = |option: &str| {
@@ -2075,7 +2084,6 @@ impl Config {
         // Now that we've reached the end of our configuration, infer the
         // default values for all options that we haven't otherwise stored yet.
 
-        config.llvm_assertions = llvm_assertions.unwrap_or(false);
         config.llvm_tests = llvm_tests.unwrap_or(false);
         config.llvm_enzyme = llvm_enzyme.unwrap_or(false);
         config.llvm_plugins = llvm_plugins.unwrap_or(false);
@@ -2118,6 +2126,9 @@ impl Config {
             );
         }
 
+        let default_std_features = BTreeSet::from([String::from("panic-unwind")]);
+        config.rust_std_features = std_features.unwrap_or(default_std_features);
+
         let default = debug == Some(true);
         config.rust_debug_assertions = debug_assertions.unwrap_or(default);
         config.rust_debug_assertions_std =
@@ -2388,6 +2399,20 @@ impl Config {
                 Some(commit) => {
                     self.download_ci_rustc(commit);
 
+                    // CI-rustc can't be used without CI-LLVM. If `self.llvm_from_ci` is false, it means the "if-unchanged"
+                    // logic has detected some changes in the LLVM submodule (download-ci-llvm=false can't happen here as
+                    // we don't allow it while parsing the configuration).
+                    if !self.llvm_from_ci {
+                        // This happens when LLVM submodule is updated in CI, we should disable ci-rustc without an error
+                        // to not break CI. For non-CI environments, we should return an error.
+                        if CiEnv::is_ci() {
+                            println!("WARNING: LLVM submodule has changes, `download-rustc` will be disabled.");
+                            return None;
+                        } else {
+                            panic!("ERROR: LLVM submodule has changes, `download-rustc` can't be used.");
+                        }
+                    }
+
                     if let Some(config_path) = &self.config {
                         let ci_config_toml = match self.get_builder_toml("ci-rustc") {
                             Ok(ci_config_toml) => ci_config_toml,
@@ -2411,8 +2436,9 @@ impl Config {
                             ci_config_toml,
                         );
 
-                        let disable_ci_rustc_if_incompatible =
-                            env::var_os("DISABLE_CI_RUSTC_IF_INCOMPATIBLE")
+                        // Primarily used by CI runners to avoid handling download-rustc incompatible
+                        // options one by one on shell scripts.
+                        let disable_ci_rustc_if_incompatible = env::var_os("DISABLE_CI_RUSTC_IF_INCOMPATIBLE")
                             .is_some_and(|s| s == "1" || s == "true");
 
                         if disable_ci_rustc_if_incompatible && res.is_err() {
@@ -2703,7 +2729,15 @@ impl Config {
     }
 
     /// Returns the commit to download, or `None` if we shouldn't download CI artifacts.
-    fn download_ci_rustc_commit(&self, download_rustc: Option<StringOrBool>) -> Option<String> {
+    fn download_ci_rustc_commit(
+        &self,
+        download_rustc: Option<StringOrBool>,
+        llvm_assertions: bool,
+    ) -> Option<String> {
+        if !is_download_ci_available(&self.build.triple, llvm_assertions) {
+            return None;
+        }
+
         // If `download-rustc` is not set, default to rebuilding.
         let if_unchanged = match download_rustc {
             None | Some(StringOrBool::Bool(false)) => return None,
@@ -2714,9 +2748,18 @@ impl Config {
             }
         };
 
+        let files_to_track = &[
+            self.src.join("compiler"),
+            self.src.join("library"),
+            self.src.join("src/version"),
+            self.src.join("src/stage0"),
+            self.src.join("src/ci/channel"),
+        ];
+
         // Look for a version to compare to based on the current commit.
         // Only commits merged by bors will have CI artifacts.
-        let commit = get_closest_merge_commit(Some(&self.src), &self.git_config(), &[]).unwrap();
+        let commit =
+            get_closest_merge_commit(Some(&self.src), &self.git_config(), files_to_track).unwrap();
         if commit.is_empty() {
             println!("ERROR: could not find commit hash for downloading rustc");
             println!("HELP: maybe your repository history is too shallow?");
@@ -2725,11 +2768,24 @@ impl Config {
             crate::exit!(1);
         }
 
+        if CiEnv::is_ci() && {
+            let head_sha =
+                output(helpers::git(Some(&self.src)).arg("rev-parse").arg("HEAD").as_command_mut());
+            let head_sha = head_sha.trim();
+            commit == head_sha
+        } {
+            eprintln!("CI rustc commit matches with HEAD and we are in CI.");
+            eprintln!(
+                "`rustc.download-ci` functionality will be skipped as artifacts are not available."
+            );
+            return None;
+        }
+
         // Warn if there were changes to the compiler or standard library since the ancestor commit.
         let has_changes = !t!(helpers::git(Some(&self.src))
             .args(["diff-index", "--quiet", &commit])
             .arg("--")
-            .args([self.src.join("compiler"), self.src.join("library")])
+            .args(files_to_track)
             .as_command_mut()
             .status())
         .success();
@@ -3016,6 +3072,7 @@ fn check_incompatible_options_for_ci_rustc(
         description,
         incremental,
         default_linker,
+        std_features,
 
         // Rest of the options can simply be ignored.
         debug: _,
@@ -3077,6 +3134,7 @@ fn check_incompatible_options_for_ci_rustc(
     err!(current_rust_config.default_linker, default_linker);
     err!(current_rust_config.stack_protector, stack_protector);
     err!(current_rust_config.lto, lto);
+    err!(current_rust_config.std_features, std_features);
 
     warn!(current_rust_config.channel, channel);
     warn!(current_rust_config.description, description);
diff --git a/src/bootstrap/src/core/config/flags.rs b/src/bootstrap/src/core/config/flags.rs
index 87db5f93fb0..3aefe517a5b 100644
--- a/src/bootstrap/src/core/config/flags.rs
+++ b/src/bootstrap/src/core/config/flags.rs
@@ -447,14 +447,14 @@ Arguments:
     The profile is optional and you will be prompted interactively if it is not given.
     The following profiles are available:
 {}
-    To only set up the git hook, VS Code config or toolchain link, you may use
+    To only set up the git hook, editor config or toolchain link, you may use
         ./x.py setup hook
-        ./x.py setup vscode
+        ./x.py setup editor
         ./x.py setup link", Profile::all_for_help("        ").trim_end()))]
     Setup {
         /// Either the profile for `config.toml` or another setup action.
         /// May be omitted to set up interactively
-        #[arg(value_name = "<PROFILE>|hook|vscode|link")]
+        #[arg(value_name = "<PROFILE>|hook|editor|link")]
         profile: Option<PathBuf>,
     },
     /// Suggest a subset of tests to run, based on modified files
diff --git a/src/bootstrap/src/core/config/tests.rs b/src/bootstrap/src/core/config/tests.rs
index e38d4eac051..2611b6cf51b 100644
--- a/src/bootstrap/src/core/config/tests.rs
+++ b/src/bootstrap/src/core/config/tests.rs
@@ -1,3 +1,4 @@
+use std::collections::BTreeSet;
 use std::env;
 use std::fs::{File, remove_file};
 use std::io::Write;
@@ -9,9 +10,10 @@ use serde::Deserialize;
 use super::flags::Flags;
 use super::{ChangeIdWrapper, Config};
 use crate::core::build_steps::clippy::get_clippy_rules_in_order;
+use crate::core::build_steps::llvm;
 use crate::core::config::{LldMode, Target, TargetSelection, TomlConfig};
 
-fn parse(config: &str) -> Config {
+pub(crate) fn parse(config: &str) -> Config {
     Config::parse_inner(
         Flags::parse(&["check".to_string(), "--config=/does/not/exist".to_string()]),
         |&_| toml::from_str(&config),
@@ -20,29 +22,32 @@ fn parse(config: &str) -> Config {
 
 #[test]
 fn download_ci_llvm() {
-    if crate::core::build_steps::llvm::is_ci_llvm_modified(&parse("")) {
-        eprintln!("Detected LLVM as non-available: running in CI and modified LLVM in this change");
-        return;
+    let config = parse("");
+    let is_available = llvm::is_ci_llvm_available(&config, config.llvm_assertions);
+    if is_available {
+        assert!(config.llvm_from_ci);
     }
 
-    let parse_llvm = |s| parse(s).llvm_from_ci;
-    let if_unchanged = parse_llvm("llvm.download-ci-llvm = \"if-unchanged\"");
+    let config = parse("llvm.download-ci-llvm = true");
+    let is_available = llvm::is_ci_llvm_available(&config, config.llvm_assertions);
+    if is_available {
+        assert!(config.llvm_from_ci);
+    }
 
-    assert!(parse_llvm("llvm.download-ci-llvm = true"));
-    assert!(!parse_llvm("llvm.download-ci-llvm = false"));
-    assert_eq!(parse_llvm(""), if_unchanged);
-    assert_eq!(parse_llvm("rust.channel = \"dev\""), if_unchanged);
-    assert!(parse_llvm("rust.channel = \"stable\""));
-    assert_eq!(parse_llvm("build.build = \"x86_64-unknown-linux-gnu\""), if_unchanged);
-    assert_eq!(
-        parse_llvm(
-            "llvm.assertions = true \r\n build.build = \"x86_64-unknown-linux-gnu\" \r\n llvm.download-ci-llvm = \"if-unchanged\""
-        ),
-        if_unchanged
-    );
-    assert!(!parse_llvm(
-        "llvm.assertions = true \r\n build.build = \"aarch64-apple-darwin\" \r\n llvm.download-ci-llvm = \"if-unchanged\""
-    ));
+    let config = parse("llvm.download-ci-llvm = false");
+    assert!(!config.llvm_from_ci);
+
+    let if_unchanged_config = parse("llvm.download-ci-llvm = \"if-unchanged\"");
+    if if_unchanged_config.llvm_from_ci {
+        let has_changes = if_unchanged_config
+            .last_modified_commit(&["src/llvm-project"], "download-ci-llvm", true)
+            .is_none();
+
+        assert!(
+            !has_changes,
+            "CI LLVM can't be enabled with 'if-unchanged' while there are changes in LLVM submodule."
+        );
+    }
 }
 
 // FIXME(onur-ozkan): extend scope of the test
@@ -326,3 +331,24 @@ fn verbose_tests_default_value() {
     let config = Config::parse(Flags::parse(&["build".into(), "compiler".into(), "-v".into()]));
     assert_eq!(config.verbose_tests, true);
 }
+
+#[test]
+fn parse_rust_std_features() {
+    let config = parse("rust.std-features = [\"panic-unwind\", \"backtrace\"]");
+    let expected_features: BTreeSet<String> =
+        ["panic-unwind", "backtrace"].into_iter().map(|s| s.to_string()).collect();
+    assert_eq!(config.rust_std_features, expected_features);
+}
+
+#[test]
+fn parse_rust_std_features_empty() {
+    let config = parse("rust.std-features = []");
+    let expected_features: BTreeSet<String> = BTreeSet::new();
+    assert_eq!(config.rust_std_features, expected_features);
+}
+
+#[test]
+#[should_panic]
+fn parse_rust_std_features_invalid() {
+    parse("rust.std-features = \"backtrace\"");
+}
diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs
index 444b75876f2..db1f5b08338 100644
--- a/src/bootstrap/src/core/download.rs
+++ b/src/bootstrap/src/core/download.rs
@@ -832,3 +832,43 @@ fn path_is_dylib(path: &Path) -> bool {
     // The .so is not necessarily the extension, it might be libLLVM.so.18.1
     path.to_str().map_or(false, |path| path.contains(".so"))
 }
+
+/// Checks whether the CI rustc is available for the given target triple.
+pub(crate) fn is_download_ci_available(target_triple: &str, llvm_assertions: bool) -> bool {
+    // All tier 1 targets and tier 2 targets with host tools.
+    const SUPPORTED_PLATFORMS: &[&str] = &[
+        "aarch64-apple-darwin",
+        "aarch64-pc-windows-msvc",
+        "aarch64-unknown-linux-gnu",
+        "aarch64-unknown-linux-musl",
+        "arm-unknown-linux-gnueabi",
+        "arm-unknown-linux-gnueabihf",
+        "armv7-unknown-linux-gnueabihf",
+        "i686-pc-windows-gnu",
+        "i686-pc-windows-msvc",
+        "i686-unknown-linux-gnu",
+        "loongarch64-unknown-linux-gnu",
+        "powerpc-unknown-linux-gnu",
+        "powerpc64-unknown-linux-gnu",
+        "powerpc64le-unknown-linux-gnu",
+        "riscv64gc-unknown-linux-gnu",
+        "s390x-unknown-linux-gnu",
+        "x86_64-apple-darwin",
+        "x86_64-pc-windows-gnu",
+        "x86_64-pc-windows-msvc",
+        "x86_64-unknown-freebsd",
+        "x86_64-unknown-illumos",
+        "x86_64-unknown-linux-gnu",
+        "x86_64-unknown-linux-musl",
+        "x86_64-unknown-netbsd",
+    ];
+
+    const SUPPORTED_PLATFORMS_WITH_ASSERTIONS: &[&str] =
+        &["x86_64-unknown-linux-gnu", "x86_64-pc-windows-msvc"];
+
+    if llvm_assertions {
+        SUPPORTED_PLATFORMS_WITH_ASSERTIONS.contains(&target_triple)
+    } else {
+        SUPPORTED_PLATFORMS.contains(&target_triple)
+    }
+}
diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs
index 888ba8e2a3f..6fbdd76ed5b 100644
--- a/src/bootstrap/src/core/sanity.rs
+++ b/src/bootstrap/src/core/sanity.rs
@@ -13,8 +13,6 @@ use std::ffi::{OsStr, OsString};
 use std::path::PathBuf;
 use std::{env, fs};
 
-use build_helper::git::warn_old_master_branch;
-
 use crate::Build;
 #[cfg(not(feature = "bootstrap-self-test"))]
 use crate::builder::Builder;
@@ -37,6 +35,9 @@ pub struct Finder {
 const STAGE0_MISSING_TARGETS: &[&str] = &[
     // just a dummy comment so the list doesn't get onelined
     "armv7-rtems-eabihf",
+    "riscv32e-unknown-none-elf",
+    "riscv32em-unknown-none-elf",
+    "riscv32emc-unknown-none-elf",
 ];
 
 /// Minimum version threshold for libstdc++ required when using prebuilt LLVM
@@ -379,6 +380,4 @@ $ pacman -R cmake && pacman -S mingw-w64-x86_64-cmake
     if let Some(ref s) = build.config.ccache {
         cmd_finder.must_have(s);
     }
-
-    warn_old_master_branch(&build.config.git_config(), &build.config.src);
 }
diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs
index 75659f46431..3924a6d714e 100644
--- a/src/bootstrap/src/lib.rs
+++ b/src/bootstrap/src/lib.rs
@@ -17,7 +17,7 @@
 //! also check out the `src/bootstrap/README.md` file for more information.
 
 use std::cell::{Cell, RefCell};
-use std::collections::{HashMap, HashSet};
+use std::collections::{BTreeSet, HashMap, HashSet};
 use std::fmt::Display;
 use std::fs::{self, File};
 use std::path::{Path, PathBuf};
@@ -470,7 +470,7 @@ impl Build {
             crate::core::metadata::build(&mut build);
         }
 
-        // Make a symbolic link so we can use a consistent directory in the documentation.
+        // Create symbolic link to use host sysroot from a consistent path (e.g., in the rust-analyzer config file).
         let build_triple = build.out.join(build.build);
         t!(fs::create_dir_all(&build_triple));
         let host = build.out.join("host");
@@ -645,28 +645,31 @@ impl Build {
         &self.config.rust_info
     }
 
-    /// Gets the space-separated set of activated features for the standard
-    /// library.
+    /// Gets the space-separated set of activated features for the standard library.
+    /// This can be configured with the `std-features` key in config.toml.
     fn std_features(&self, target: TargetSelection) -> String {
-        let mut features = " panic-unwind".to_string();
+        let mut features: BTreeSet<&str> =
+            self.config.rust_std_features.iter().map(|s| s.as_str()).collect();
 
         match self.config.llvm_libunwind(target) {
-            LlvmLibunwind::InTree => features.push_str(" llvm-libunwind"),
-            LlvmLibunwind::System => features.push_str(" system-llvm-libunwind"),
-            LlvmLibunwind::No => {}
-        }
+            LlvmLibunwind::InTree => features.insert("llvm-libunwind"),
+            LlvmLibunwind::System => features.insert("system-llvm-libunwind"),
+            LlvmLibunwind::No => false,
+        };
+
         if self.config.backtrace {
-            features.push_str(" backtrace");
+            features.insert("backtrace");
         }
         if self.config.profiler_enabled(target) {
-            features.push_str(" profiler");
+            features.insert("profiler");
         }
         // Generate memcpy, etc.  FIXME: Remove this once compiler-builtins
         // automatically detects this target.
         if target.contains("zkvm") {
-            features.push_str(" compiler-builtins-mem");
+            features.insert("compiler-builtins-mem");
         }
-        features
+
+        features.into_iter().collect::<Vec<_>>().join(" ")
     }
 
     /// Gets the space-separated set of activated features for the compiler.
@@ -1572,9 +1575,11 @@ Executed at: {executed_at}"#,
     fn rust_version(&self) -> String {
         let mut version = self.rust_info().version(self, &self.version);
         if let Some(ref s) = self.config.description {
-            version.push_str(" (");
-            version.push_str(s);
-            version.push(')');
+            if !s.is_empty() {
+                version.push_str(" (");
+                version.push_str(s);
+                version.push(')');
+            }
         }
         version
     }
diff --git a/src/bootstrap/src/utils/change_tracker.rs b/src/bootstrap/src/utils/change_tracker.rs
index e6f7f105fa2..b37786496cb 100644
--- a/src/bootstrap/src/utils/change_tracker.rs
+++ b/src/bootstrap/src/utils/change_tracker.rs
@@ -270,4 +270,9 @@ pub const CONFIG_CHANGE_HISTORY: &[ChangeInfo] = &[
         severity: ChangeSeverity::Info,
         summary: "If `llvm.download-ci-llvm` is not defined, it defaults to `true`.",
     },
+    ChangeInfo {
+        change_id: 131075,
+        severity: ChangeSeverity::Info,
+        summary: "New option `./x setup editor` added, replacing `./x setup vscode` and adding support for vim, emacs and helix.",
+    },
 ];
diff --git a/src/bootstrap/src/utils/shared_helpers.rs b/src/bootstrap/src/utils/shared_helpers.rs
index 7150c84313c..6d3c276cc05 100644
--- a/src/bootstrap/src/utils/shared_helpers.rs
+++ b/src/bootstrap/src/utils/shared_helpers.rs
@@ -49,6 +49,8 @@ pub fn exe(name: &str, target: &str) -> String {
         format!("{name}.exe")
     } else if target.contains("uefi") {
         format!("{name}.efi")
+    } else if target.contains("wasm") {
+        format!("{name}.wasm")
     } else {
         name.to_string()
     }
diff --git a/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile
index 865a9e32fa9..71eb72686b0 100644
--- a/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile
+++ b/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile
@@ -47,6 +47,7 @@ ENV RUST_CONFIGURE_ARGS \
       --enable-extended \
       --enable-full-tools \
       --enable-profiler \
+      --enable-sanitizers \
       --disable-docs
 
 ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $TARGETS
diff --git a/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile b/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile
index 62dbfaaa673..5081f25e567 100644
--- a/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile
+++ b/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile
@@ -29,6 +29,7 @@ ENV RUST_CONFIGURE_ARGS \
       --enable-extended \
       --enable-full-tools \
       --enable-profiler \
+      --enable-sanitizers \
       --disable-docs \
       --set target.loongarch64-unknown-linux-musl.crt-static=false \
       --musl-root-loongarch64=/x-tools/loongarch64-unknown-linux-musl/loongarch64-unknown-linux-musl/sysroot/usr
diff --git a/src/ci/docker/host-x86_64/mingw-check/Dockerfile b/src/ci/docker/host-x86_64/mingw-check/Dockerfile
index 571378774be..0f8ebb987c3 100644
--- a/src/ci/docker/host-x86_64/mingw-check/Dockerfile
+++ b/src/ci/docker/host-x86_64/mingw-check/Dockerfile
@@ -46,7 +46,8 @@ ENV RUN_CHECK_WITH_PARALLEL_QUERIES 1
 # Check library crates on all tier 1 targets.
 # We disable optimized compiler built-ins because that requires a C toolchain for the target.
 # We also skip the x86_64-unknown-linux-gnu target as it is well-tested by other jobs.
-ENV SCRIPT python3 ../x.py check --stage 0 --set build.optimized-compiler-builtins=false core alloc std --target=aarch64-unknown-linux-gnu,i686-pc-windows-msvc,i686-unknown-linux-gnu,x86_64-apple-darwin,x86_64-pc-windows-gnu,x86_64-pc-windows-msvc && \
+ENV SCRIPT \
+           python3 ../x.py check --stage 0 --set build.optimized-compiler-builtins=false core alloc std --target=aarch64-unknown-linux-gnu,i686-pc-windows-msvc,i686-unknown-linux-gnu,x86_64-apple-darwin,x86_64-pc-windows-gnu,x86_64-pc-windows-msvc && \
            /scripts/check-default-config-profiles.sh && \
            python3 ../x.py check --target=i686-pc-windows-gnu --host=i686-pc-windows-gnu && \
            python3 ../x.py clippy bootstrap -Dwarnings && \
diff --git a/src/ci/docker/host-x86_64/x86_64-fuchsia/Dockerfile b/src/ci/docker/host-x86_64/x86_64-fuchsia/Dockerfile
index ba3e8bdb687..0cae83a85b3 100644
--- a/src/ci/docker/host-x86_64/x86_64-fuchsia/Dockerfile
+++ b/src/ci/docker/host-x86_64/x86_64-fuchsia/Dockerfile
@@ -58,6 +58,9 @@ RUN mkdir -p $RUST_INSTALL_DIR/etc
 # Fuchsia only supports LLVM.
 ENV CODEGEN_BACKENDS llvm
 
+# download-rustc is not allowed for `x install`
+ENV NO_DOWNLOAD_CI_RUSTC 1
+
 ENV RUST_CONFIGURE_ARGS \
   --prefix=$RUST_INSTALL_DIR \
   --sysconfdir=etc \
@@ -70,6 +73,7 @@ ENV RUST_CONFIGURE_ARGS \
   --set target.x86_64-unknown-fuchsia.ar=/usr/local/bin/llvm-ar \
   --set target.x86_64-unknown-fuchsia.ranlib=/usr/local/bin/llvm-ranlib \
   --set target.x86_64-unknown-fuchsia.linker=/usr/local/bin/ld.lld
+
 ENV SCRIPT \
     python3 ../x.py install --target $TARGETS compiler/rustc library/std clippy && \
     bash ../src/ci/docker/host-x86_64/x86_64-fuchsia/build-fuchsia.sh
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 6a09ab3065f..17fc1a57492 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
@@ -84,6 +84,7 @@ ENV RUST_CONFIGURE_ARGS \
   --enable-new-symbol-mangling
 
 ENV HOST_TARGET x86_64-unknown-linux-gnu
+ENV FORCE_CI_RUSTC 1
 
 COPY host-x86_64/dist-x86_64-linux/shared.sh /scripts/
 COPY host-x86_64/dist-x86_64-linux/build-gccjit.sh /scripts/
@@ -100,4 +101,4 @@ RUN /scripts/build-gccjit.sh /scripts
 # 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 test tests/rustdoc-gui --stage 2 --test-args "'--no-sandbox --jobs 1'"
+  python3 ../x.py test tests/rustdoc-gui --stage 2 --test-args "'--jobs 1'"
diff --git a/src/ci/docker/run.sh b/src/ci/docker/run.sh
index fad4b5af095..28487bce482 100755
--- a/src/ci/docker/run.sh
+++ b/src/ci/docker/run.sh
@@ -343,6 +343,7 @@ docker \
   --env PR_CI_JOB \
   --env OBJDIR_ON_HOST="$objdir" \
   --env CODEGEN_BACKENDS \
+  --env DISABLE_CI_RUSTC_IF_INCOMPATIBLE="$DISABLE_CI_RUSTC_IF_INCOMPATIBLE" \
   --init \
   --rm \
   rust-ci \
diff --git a/src/ci/docker/scripts/emscripten.sh b/src/ci/docker/scripts/emscripten.sh
index 3f5e2c6ff1d..8b2b39ee162 100644
--- a/src/ci/docker/scripts/emscripten.sh
+++ b/src/ci/docker/scripts/emscripten.sh
@@ -20,5 +20,5 @@ exit 1
 
 git clone https://github.com/emscripten-core/emsdk.git /emsdk-portable
 cd /emsdk-portable
-hide_output ./emsdk install 2.0.5
-./emsdk activate 2.0.5
+hide_output ./emsdk install 3.1.68
+./emsdk activate 3.1.68
diff --git a/src/ci/docker/scripts/rfl-build.sh b/src/ci/docker/scripts/rfl-build.sh
index 8011e07e92e..27dbfc6040c 100755
--- a/src/ci/docker/scripts/rfl-build.sh
+++ b/src/ci/docker/scripts/rfl-build.sh
@@ -2,7 +2,7 @@
 
 set -euo pipefail
 
-LINUX_VERSION=4c7864e81d8bbd51036dacf92fb0a400e13aaeee
+LINUX_VERSION=v6.12-rc2
 
 # Build rustc, rustdoc, cargo, clippy-driver and rustfmt
 ../x.py build --stage 2 library rustdoc clippy rustfmt
diff --git a/src/ci/docker/scripts/x86_64-gnu-llvm.sh b/src/ci/docker/scripts/x86_64-gnu-llvm.sh
index 98290f5a72c..dea38b6fd2a 100755
--- a/src/ci/docker/scripts/x86_64-gnu-llvm.sh
+++ b/src/ci/docker/scripts/x86_64-gnu-llvm.sh
@@ -2,6 +2,22 @@
 
 set -ex
 
+if [ "$READ_ONLY_SRC" = "0" ]; then
+    # `core::builder::tests::ci_rustc_if_unchanged_logic` bootstrap test ensures that
+    # "download-rustc=if-unchanged" logic don't use CI rustc while there are changes on
+    # compiler and/or library. Here we are adding a dummy commit on compiler and running
+    # that test to make sure we never download CI rustc with a change on the compiler tree.
+    echo "" >> ../compiler/rustc/src/main.rs
+    git config --global user.email "dummy@dummy.com"
+    git config --global user.name "dummy"
+    git add ../compiler/rustc/src/main.rs
+    git commit -m "test commit for rust.download-rustc=if-unchanged logic"
+    DISABLE_CI_RUSTC_IF_INCOMPATIBLE=0 ../x.py test bootstrap \
+        -- core::builder::tests::ci_rustc_if_unchanged_logic
+    # Revert the dummy commit
+    git reset --hard HEAD~1
+fi
+
 # Only run the stage 1 tests on merges, not on PR CI jobs.
 if [[ -z "${PR_CI_JOB}" ]]; then
     ../x.py --stage 1 test --skip src/tools/tidy
diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml
index 6379f1ade1c..8f49f623afa 100644
--- a/src/ci/github-actions/jobs.yml
+++ b/src/ci/github-actions/jobs.yml
@@ -92,6 +92,8 @@ pr:
   - image: x86_64-gnu-llvm-18
     env:
       ENABLE_GCC_CODEGEN: "1"
+      # We are adding (temporarily) a dummy commit on the compiler
+      READ_ONLY_SRC: "0"
     <<: *job-linux-16c
   - image: x86_64-gnu-tools
     <<: *job-linux-16c
@@ -259,6 +261,7 @@ auto:
   - image: x86_64-gnu-llvm-18
     env:
       RUST_BACKTRACE: 1
+      READ_ONLY_SRC: "0"
     <<: *job-linux-8c
 
   - image: x86_64-gnu-nopt
diff --git a/src/ci/run.sh b/src/ci/run.sh
index c8201d9bcfd..3962c354c10 100755
--- a/src/ci/run.sh
+++ b/src/ci/run.sh
@@ -52,6 +52,13 @@ if [ "$CI" != "" ]; then
     RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set change-id=99999999"
 fi
 
+# If runner uses an incompatible option and `FORCE_CI_RUSTC` is not defined,
+# switch to in-tree rustc.
+if [ "$FORCE_CI_RUSTC" == "" ]; then
+    echo 'debug: `DISABLE_CI_RUSTC_IF_INCOMPATIBLE` configured.'
+    DISABLE_CI_RUSTC_IF_INCOMPATIBLE=1
+fi
+
 if ! isCI || isCiBranch auto || isCiBranch beta || isCiBranch try || isCiBranch try-perf || \
   isCiBranch automation/bors/try; then
     RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set build.print-step-timings --enable-verbose-tests"
@@ -169,10 +176,16 @@ else
   if [ "$NO_DOWNLOAD_CI_LLVM" = "" ]; then
     RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set llvm.download-ci-llvm=if-unchanged"
   else
+    # CI rustc requires CI LLVM to be enabled (see https://github.com/rust-lang/rust/issues/123586).
+    NO_DOWNLOAD_CI_RUSTC=1
     # When building for CI we want to use the static C++ Standard library
     # included with LLVM, since a dynamic libstdcpp may not be available.
     RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set llvm.static-libstdcpp"
   fi
+
+  if [ "$NO_DOWNLOAD_CI_RUSTC" = "" ]; then
+    RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.download-rustc=if-unchanged"
+  fi
 fi
 
 if [ "$ENABLE_GCC_CODEGEN" = "1" ]; then
diff --git a/src/ci/scripts/setup-upstream-remote.sh b/src/ci/scripts/setup-upstream-remote.sh
new file mode 100755
index 00000000000..52b4c98a890
--- /dev/null
+++ b/src/ci/scripts/setup-upstream-remote.sh
@@ -0,0 +1,24 @@
+#!/bin/bash
+# In CI environments, bootstrap is forced to use the remote upstream based
+# on "git_repository" and "nightly_branch" values from src/stage0 file.
+# This script configures the remote as it may not exist by default.
+
+set -euo pipefail
+IFS=$'\n\t'
+
+ci_dir=$(cd $(dirname $0) && pwd)/..
+source "$ci_dir/shared.sh"
+
+git_repository=$(parse_stage0_file_by_key "git_repository")
+nightly_branch=$(parse_stage0_file_by_key "nightly_branch")
+
+# Configure "rust-lang/rust" upstream remote only when it's not origin.
+if [ -z "$(git config remote.origin.url | grep $git_repository)" ]; then
+    echo "Configuring https://github.com/$git_repository remote as upstream."
+    git remote add upstream "https://github.com/$git_repository"
+    REMOTE_NAME="upstream"
+else
+    REMOTE_NAME="origin"
+fi
+
+git fetch $REMOTE_NAME $nightly_branch
diff --git a/src/ci/shared.sh b/src/ci/shared.sh
index 2b0a10e4d08..1e6a008a5de 100644
--- a/src/ci/shared.sh
+++ b/src/ci/shared.sh
@@ -136,3 +136,15 @@ function releaseChannel {
         echo $RUST_CI_OVERRIDE_RELEASE_CHANNEL
     fi
 }
+
+# Parse values from src/stage0 file by key
+function parse_stage0_file_by_key {
+    local key="$1"
+    local file="$ci_dir/../stage0"
+    local value=$(awk -F= '{a[$1]=$2} END {print(a["'$key'"])}' $file)
+    if [ -z "$value" ]; then
+        echo "ERROR: Key '$key' not found in '$file'."
+        exit 1
+    fi
+    echo "$value"
+}
diff --git a/src/doc/book b/src/doc/book
-Subproject 99cf75a5414fa8adbe3974bd0836661ca901708
+Subproject f38ce8baef98cb20229e56f1be2d50e345f1179
diff --git a/src/doc/embedded-book b/src/doc/embedded-book
-Subproject dbae36bf3f8410aa4313b3bad42e374735d48a9
+Subproject f40a8b420ec4b4505d9489965e261f1d5c28ba2
diff --git a/src/doc/nomicon b/src/doc/nomicon
-Subproject 14649f15d232d509478206ee9ed5105641aa60d
+Subproject 456b904f791751892b01282fd2757904993c4c2
diff --git a/src/doc/reference b/src/doc/reference
-Subproject 24fb2687cdbc54fa18ae4acf5d879cfceca77b2
+Subproject c64e52a3d306eac0129f3ad6c6d8806ab99ae2e
diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example
-Subproject c79ec345f08a1e94494cdc8c999709a90203fd8
+Subproject 8bede1b919a81ab7d0c961f6bbf68d3efa297bd
diff --git a/src/doc/rustc-dev-guide b/src/doc/rustc-dev-guide
-Subproject 555f3de2fa0d61c4294b74d245f1cbad6fcbf58
+Subproject 07bc9ca9eb1cd6d9fbbf758c2753b748804a134
diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md
index 207eb5d6d4f..0ef95ba64a1 100644
--- a/src/doc/rustc/src/platform-support.md
+++ b/src/doc/rustc/src/platform-support.md
@@ -388,6 +388,7 @@ target | std | host | notes
 [`x86_64-unknown-hermit`](platform-support/hermit.md) | ✓ |  | x86_64 Hermit
 `x86_64-unknown-l4re-uclibc` | ? |  |
 [`x86_64-unknown-openbsd`](platform-support/openbsd.md) | ✓ | ✓ | 64-bit OpenBSD
+[`x86_64-unknown-trusty`](platform-support/trusty.md) | ? |  |
 `x86_64-uwp-windows-gnu` | ✓ |  |
 `x86_64-uwp-windows-msvc` | ✓ |  |
 [`x86_64-win7-windows-msvc`](platform-support/win7-windows-msvc.md) | ✓ |   | 64-bit Windows 7 support
@@ -412,5 +413,8 @@ target | std | host | notes
 [`riscv32imafc-unknown-nuttx-elf`](platform-support/nuttx.md) | * |  | RISC-V 32bit with NuttX
 [`riscv64imac-unknown-nuttx-elf`](platform-support/nuttx.md) | * |  | RISC-V 64bit with NuttX
 [`riscv64gc-unknown-nuttx-elf`](platform-support/nuttx.md) | * |  | RISC-V 64bit with NuttX
+[`riscv32e-unknown-none-elf`](platform-support/riscv32-unknown-none-elf.md) | * |  | Bare RISC-V (RV32E ISA)
+[`riscv32em-unknown-none-elf`](platform-support/riscv32-unknown-none-elf.md) | * |  | Bare RISC-V (RV32EM ISA)
+[`riscv32emc-unknown-none-elf`](platform-support/riscv32-unknown-none-elf.md) | * |  | Bare RISC-V (RV32EMC ISA)
 
 [runs on NVIDIA GPUs]: https://github.com/japaric-archived/nvptx#targets
diff --git a/src/doc/rustc/src/platform-support/csky-unknown-linux-gnuabiv2.md b/src/doc/rustc/src/platform-support/csky-unknown-linux-gnuabiv2.md
index e72bfb8bae7..32e4f855313 100644
--- a/src/doc/rustc/src/platform-support/csky-unknown-linux-gnuabiv2.md
+++ b/src/doc/rustc/src/platform-support/csky-unknown-linux-gnuabiv2.md
@@ -80,7 +80,7 @@ cd hello_world
 ```sh
 CARGO_TARGET_CSKY_UNKNOWN_LINUX_GNUABIV2_RUNNER=${QEMU_PATH}/bin/qemu-cskyv2 -L ${TOOLCHAIN_PATH}/csky-linux-gnuabiv2/libc \
 CARGO_TARGET_CSKY_UNKNOWN_LINUX_GNUABIV2_LINKER=${TOOLCHAIN_PATH}/bin/csky-linux-gnuabiv2-gcc \
-RUSTFLAGS="-C target-features=+crt-static" \
+RUSTFLAGS="-C target-feature=+crt-static" \
 cargo +stage2 run --target csky-unknown-linux-gnuabiv2
 ```
 
diff --git a/src/doc/rustc/src/platform-support/riscv32-unknown-none-elf.md b/src/doc/rustc/src/platform-support/riscv32-unknown-none-elf.md
index 9a27a568b57..38742143c4b 100644
--- a/src/doc/rustc/src/platform-support/riscv32-unknown-none-elf.md
+++ b/src/doc/rustc/src/platform-support/riscv32-unknown-none-elf.md
@@ -35,4 +35,4 @@ Rust test-suite on this target.
 ## Cross-compilation toolchains and C code
 
 This target supports C code. If interlinking with C or C++, you may need to use
-`riscv64-unknown-elf-gcc` as a linker instead of `rust-lld`.
+`riscv32-unknown-elf-gcc` as a linker instead of `rust-lld`.
diff --git a/src/doc/rustc/src/platform-support/riscv32e-unknown-none-elf.md b/src/doc/rustc/src/platform-support/riscv32e-unknown-none-elf.md
new file mode 100644
index 00000000000..69f08774f83
--- /dev/null
+++ b/src/doc/rustc/src/platform-support/riscv32e-unknown-none-elf.md
@@ -0,0 +1,30 @@
+# `riscv32{e,em,emc}-unknown-none-elf`
+
+**Tier: 3**
+
+Bare-metal target for RISC-V CPUs with the RV32E, RV32EM and RV32EMC ISAs.
+
+## Target maintainers
+
+* Henri Lunnikivi, <henri.lunnikivi@gmail.com>, [@hegza](https://github.com/hegza)
+
+## Requirements
+
+The target is cross-compiled, and uses static linking. No external toolchain is
+required and the default `rust-lld` linker works, but you must specify a linker
+script.
+
+## Building the target
+
+This target is included in Rust and can be installed via `rustup`.
+
+## Testing
+
+This is a cross-compiled `no-std` target, which must be run either in a
+simulator or by programming them onto suitable hardware. It is not possible to
+run the Rust test-suite on this target.
+
+## Cross-compilation toolchains and C code
+
+This target supports C code. If interlinking with C or C++, you may need to use
+`riscv32-unknown-elf-gcc` as a linker instead of `rust-lld`.
diff --git a/src/doc/unstable-book/src/language-features/cfg-boolean-literals.md b/src/doc/unstable-book/src/language-features/cfg-boolean-literals.md
new file mode 100644
index 00000000000..ad795ff9d9b
--- /dev/null
+++ b/src/doc/unstable-book/src/language-features/cfg-boolean-literals.md
@@ -0,0 +1,22 @@
+# `cfg_boolean_literals`
+
+The tracking issue for this feature is: [#131204]
+
+[#131204]: https://github.com/rust-lang/rust/issues/131204
+
+------------------------
+
+The `cfg_boolean_literals` feature makes it possible to use the `true`/`false`
+literal as cfg predicate. They always evaluate to true/false respectively.
+
+## Examples
+
+```rust
+#![feature(cfg_boolean_literals)]
+
+#[cfg(true)]
+const A: i32 = 5;
+
+#[cfg(all(false))]
+const A: i32 = 58 * 89;
+```
diff --git a/src/etc/completions/x.py.sh b/src/etc/completions/x.py.sh
index 2aa0d9f69cc..ba7f2c9fb5d 100644
--- a/src/etc/completions/x.py.sh
+++ b/src/etc/completions/x.py.sh
@@ -2741,7 +2741,7 @@ _x.py() {
             return 0
             ;;
         x.py__setup)
-            opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [<PROFILE>|hook|vscode|link] [PATHS]... [ARGS]..."
+            opts="-v -i -j -h --verbose --incremental --config --build-dir --build --host --target --exclude --skip --include-default-paths --rustc-error-format --on-fail --dry-run --dump-bootstrap-shims --stage --keep-stage --keep-stage-std --src --jobs --warnings --error-format --json-output --color --bypass-bootstrap-lock --rust-profile-generate --rust-profile-use --llvm-profile-use --llvm-profile-generate --enable-bolt-settings --skip-stage0-validation --reproducible-artifact --set --help [<PROFILE>|hook|editor|link] [PATHS]... [ARGS]..."
             if [[ ${cur} == -* || ${COMP_CWORD} -eq 2 ]] ; then
                 COMPREPLY=( $(compgen -W "${opts}" -- "${cur}") )
                 return 0
diff --git a/src/etc/rust_analyzer_eglot.el b/src/etc/rust_analyzer_eglot.el
index e55d80d98de..7b4309f8e18 100644
--- a/src/etc/rust_analyzer_eglot.el
+++ b/src/etc/rust_analyzer_eglot.el
@@ -2,28 +2,28 @@
   .((eglot-workspace-configuration
      . (:rust-analyzer
         ( :check ( :invocationLocation "root"
-                   :invocationStrategy "once"
-                   :overrideCommand ["python3"
-                                     "x.py"
-                                     "check"
-                                     "--json-output"])
-          :linkedProjects ["Cargo.toml"
-                           "src/tools/x/Cargo.toml"
-                           "src/bootstrap/Cargo.toml"
-                           "src/tools/rust-analyzer/Cargo.toml"
-                           "compiler/rustc_codegen_cranelift/Cargo.toml"
-                           "compiler/rustc_codegen_gcc/Cargo.toml"]
-          :rustfmt ( :overrideCommand ["build/host/rustfmt/bin/rustfmt"
-                                       "--edition=2021"])
-          :procMacro ( :server "build/host/stage0/libexec/rust-analyzer-proc-macro-srv"
-                       :enable t)
-          :cargo ( :buildScripts ( :enable t
-                                   :invocationLocation "root"
-                                   :invocationStrategy "once"
-                                   :overrideCommand ["python3"
-                                                     "x.py"
-                                                     "check"
-                                                     "--json-output"])
-                   :sysrootSrc "./library"
-                   :extraEnv (:RUSTC_BOOTSTRAP "1"))
-          :rustc ( :source "./Cargo.toml" )))))))
+                                       :invocationStrategy "once"
+                                       :overrideCommand ["python3"
+                                                         "x.py"
+                                                         "check"
+                                                         "--json-output"])
+                 :linkedProjects ["Cargo.toml"
+                                  "src/tools/x/Cargo.toml"
+                                  "src/bootstrap/Cargo.toml"
+                                  "src/tools/rust-analyzer/Cargo.toml"
+                                  "compiler/rustc_codegen_cranelift/Cargo.toml"
+                                  "compiler/rustc_codegen_gcc/Cargo.toml"]
+                 :rustfmt ( :overrideCommand ["build/host/rustfmt/bin/rustfmt"
+                                              "--edition=2021"])
+                 :procMacro ( :server "build/host/stage0/libexec/rust-analyzer-proc-macro-srv"
+                                      :enable t)
+                 :cargo ( :buildScripts ( :enable t
+                                                  :invocationLocation "root"
+                                                  :invocationStrategy "once"
+                                                  :overrideCommand ["python3"
+                                                                    "x.py"
+                                                                    "check"
+                                                                    "--json-output"])
+                                        :sysrootSrc "./library"
+                                        :extraEnv (:RUSTC_BOOTSTRAP "1"))
+                 :rustc ( :source "./Cargo.toml" )))))))
diff --git a/src/librustdoc/clean/cfg.rs b/src/librustdoc/clean/cfg.rs
index 26739219085..9255242611a 100644
--- a/src/librustdoc/clean/cfg.rs
+++ b/src/librustdoc/clean/cfg.rs
@@ -6,7 +6,7 @@
 use std::fmt::{self, Write};
 use std::{mem, ops};
 
-use rustc_ast::{LitKind, MetaItem, MetaItemKind, NestedMetaItem};
+use rustc_ast::{LitKind, MetaItem, MetaItemInner, MetaItemKind, MetaItemLit};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_feature::Features;
 use rustc_session::parse::ParseSess;
@@ -41,14 +41,18 @@ pub(crate) struct InvalidCfgError {
 }
 
 impl Cfg {
-    /// Parses a `NestedMetaItem` into a `Cfg`.
+    /// Parses a `MetaItemInner` into a `Cfg`.
     fn parse_nested(
-        nested_cfg: &NestedMetaItem,
+        nested_cfg: &MetaItemInner,
         exclude: &FxHashSet<Cfg>,
     ) -> Result<Option<Cfg>, InvalidCfgError> {
         match nested_cfg {
-            NestedMetaItem::MetaItem(ref cfg) => Cfg::parse_without(cfg, exclude),
-            NestedMetaItem::Lit(ref lit) => {
+            MetaItemInner::MetaItem(ref cfg) => Cfg::parse_without(cfg, exclude),
+            MetaItemInner::Lit(MetaItemLit { kind: LitKind::Bool(b), .. }) => match *b {
+                true => Ok(Some(Cfg::True)),
+                false => Ok(Some(Cfg::False)),
+            },
+            MetaItemInner::Lit(ref lit) => {
                 Err(InvalidCfgError { msg: "unexpected literal", span: lit.span })
             }
         }
@@ -120,8 +124,8 @@ impl Cfg {
     ///
     /// If the content is not properly formatted, it will return an error indicating what and where
     /// the error is.
-    pub(crate) fn parse(cfg: &MetaItem) -> Result<Cfg, InvalidCfgError> {
-        Self::parse_without(cfg, &FxHashSet::default()).map(|ret| ret.unwrap())
+    pub(crate) fn parse(cfg: &MetaItemInner) -> Result<Cfg, InvalidCfgError> {
+        Self::parse_nested(cfg, &FxHashSet::default()).map(|ret| ret.unwrap())
     }
 
     /// Checks whether the given configuration can be matched in the current session.
diff --git a/src/librustdoc/clean/cfg/tests.rs b/src/librustdoc/clean/cfg/tests.rs
index 0ab655103e2..2c62e12c96d 100644
--- a/src/librustdoc/clean/cfg/tests.rs
+++ b/src/librustdoc/clean/cfg/tests.rs
@@ -1,4 +1,5 @@
-use rustc_ast::{MetaItemLit, Path, Safety, StrStyle};
+use rustc_ast::ast::LitIntType;
+use rustc_ast::{MetaItemInner, MetaItemLit, Path, Safety, StrStyle};
 use rustc_span::symbol::{Ident, kw};
 use rustc_span::{DUMMY_SP, create_default_session_globals_then};
 use thin_vec::thin_vec;
@@ -13,52 +14,52 @@ fn name_value_cfg(name: &str, value: &str) -> Cfg {
     Cfg::Cfg(Symbol::intern(name), Some(Symbol::intern(value)))
 }
 
-fn dummy_meta_item_word(name: &str) -> MetaItem {
-    MetaItem {
+fn dummy_lit(symbol: Symbol, kind: LitKind) -> MetaItemInner {
+    MetaItemInner::Lit(MetaItemLit { symbol, suffix: None, kind, span: DUMMY_SP })
+}
+
+fn dummy_meta_item_word(name: &str) -> MetaItemInner {
+    MetaItemInner::MetaItem(MetaItem {
         unsafety: Safety::Default,
         path: Path::from_ident(Ident::from_str(name)),
         kind: MetaItemKind::Word,
         span: DUMMY_SP,
-    }
+    })
 }
 
-fn dummy_meta_item_name_value(name: &str, symbol: Symbol, kind: LitKind) -> MetaItem {
+fn dummy_meta_item_name_value(name: &str, symbol: Symbol, kind: LitKind) -> MetaItemInner {
     let lit = MetaItemLit { symbol, suffix: None, kind, span: DUMMY_SP };
-    MetaItem {
+    MetaItemInner::MetaItem(MetaItem {
         unsafety: Safety::Default,
         path: Path::from_ident(Ident::from_str(name)),
         kind: MetaItemKind::NameValue(lit),
         span: DUMMY_SP,
-    }
+    })
 }
 
 macro_rules! dummy_meta_item_list {
     ($name:ident, [$($list:ident),* $(,)?]) => {
-        MetaItem {
+        MetaItemInner::MetaItem(MetaItem {
             unsafety: Safety::Default,
             path: Path::from_ident(Ident::from_str(stringify!($name))),
             kind: MetaItemKind::List(thin_vec![
                 $(
-                    NestedMetaItem::MetaItem(
-                        dummy_meta_item_word(stringify!($list)),
-                    ),
+                    dummy_meta_item_word(stringify!($list)),
                 )*
             ]),
             span: DUMMY_SP,
-        }
+        })
     };
 
     ($name:ident, [$($list:expr),* $(,)?]) => {
-        MetaItem {
+        MetaItemInner::MetaItem(MetaItem {
             unsafety: Safety::Default,
             path: Path::from_ident(Ident::from_str(stringify!($name))),
             kind: MetaItemKind::List(thin_vec![
-                $(
-                    NestedMetaItem::MetaItem($list),
-                )*
+                $($list,)*
             ]),
             span: DUMMY_SP,
-        }
+        })
     };
 }
 
@@ -251,6 +252,14 @@ fn test_cfg_or() {
 #[test]
 fn test_parse_ok() {
     create_default_session_globals_then(|| {
+        let r#true = Symbol::intern("true");
+        let mi = dummy_lit(r#true, LitKind::Bool(true));
+        assert_eq!(Cfg::parse(&mi), Ok(Cfg::True));
+
+        let r#false = Symbol::intern("false");
+        let mi = dummy_lit(r#false, LitKind::Bool(false));
+        assert_eq!(Cfg::parse(&mi), Ok(Cfg::False));
+
         let mi = dummy_meta_item_word("all");
         assert_eq!(Cfg::parse(&mi), Ok(word_cfg("all")));
 
@@ -309,6 +318,14 @@ fn test_parse_err() {
 
         let mi = dummy_meta_item_list!(not, [dummy_meta_item_list!(foo, []),]);
         assert!(Cfg::parse(&mi).is_err());
+
+        let c = Symbol::intern("e");
+        let mi = dummy_lit(c, LitKind::Char('e'));
+        assert!(Cfg::parse(&mi).is_err());
+
+        let five = Symbol::intern("5");
+        let mi = dummy_lit(five, LitKind::Int(5.into(), LitIntType::Unsuffixed));
+        assert!(Cfg::parse(&mi).is_err());
     })
 }
 
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index b79458eaa78..fa73733360c 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -1828,13 +1828,8 @@ pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> T
             Array(Box::new(clean_ty(ty, cx)), length.into())
         }
         TyKind::Tup(tys) => Tuple(tys.iter().map(|ty| clean_ty(ty, cx)).collect()),
-        TyKind::OpaqueDef(item_id, _) => {
-            let item = cx.tcx.hir().item(item_id);
-            if let hir::ItemKind::OpaqueTy(ty) = item.kind {
-                ImplTrait(ty.bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect())
-            } else {
-                unreachable!()
-            }
+        TyKind::OpaqueDef(ty, _) => {
+            ImplTrait(ty.bounds.iter().filter_map(|x| clean_generic_bound(x, cx)).collect())
         }
         TyKind::Path(_) => clean_qpath(ty, cx),
         TyKind::TraitObject(bounds, lifetime, _) => {
@@ -2736,9 +2731,6 @@ fn clean_maybe_renamed_item<'tcx>(
                 type_: clean_ty(ty, cx),
                 kind: ConstantKind::Local { body: body_id, def_id },
             })),
-            // clean_ty changes types which reference an OpaqueTy item to instead be
-            // an ImplTrait, so it's ok to return nothing here.
-            ItemKind::OpaqueTy(_) => return vec![],
             ItemKind::TyAlias(hir_ty, generics) => {
                 *cx.current_type_aliases.entry(def_id).or_insert(0) += 1;
                 let rustdoc_ty = clean_ty(hir_ty, cx);
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index a3277e8ca92..bc5bf4c0583 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -5,10 +5,11 @@ use std::sync::{Arc, OnceLock as OnceCell};
 use std::{fmt, iter};
 
 use arrayvec::ArrayVec;
+use rustc_ast::MetaItemInner;
 use rustc_ast_pretty::pprust;
 use rustc_attr::{ConstStability, Deprecation, Stability, StableSince};
 use rustc_const_eval::const_eval::is_unstable_const_fn;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId};
 use rustc_hir::lang_items::LangItem;
@@ -113,7 +114,7 @@ impl From<DefId> for ItemId {
 pub(crate) struct Crate {
     pub(crate) module: Item,
     /// Only here so that they can be filtered through the rustdoc passes.
-    pub(crate) external_traits: Box<FxHashMap<DefId, Trait>>,
+    pub(crate) external_traits: Box<FxIndexMap<DefId, Trait>>,
 }
 
 impl Crate {
@@ -952,7 +953,7 @@ pub(crate) struct Module {
 }
 
 pub(crate) trait AttributesExt {
-    type AttributeIterator<'a>: Iterator<Item = ast::NestedMetaItem>
+    type AttributeIterator<'a>: Iterator<Item = ast::MetaItemInner>
     where
         Self: 'a;
     type Attributes<'a>: Iterator<Item = &'a ast::Attribute>
@@ -986,7 +987,7 @@ pub(crate) trait AttributesExt {
                 .peekable();
             if doc_cfg.peek().is_some() && doc_cfg_active {
                 doc_cfg
-                    .filter_map(|attr| Cfg::parse(attr.meta_item()?).ok())
+                    .filter_map(|attr| Cfg::parse(&attr).ok())
                     .fold(Cfg::True, |cfg, new_cfg| cfg & new_cfg)
             } else if doc_auto_cfg_active {
                 // If there is no `doc(cfg())`, then we retrieve the `cfg()` attributes (because
@@ -1042,7 +1043,7 @@ pub(crate) trait AttributesExt {
                     let mut meta = attr.meta_item().unwrap().clone();
                     meta.path = ast::Path::from_ident(Ident::with_dummy_span(sym::target_feature));
 
-                    if let Ok(feat_cfg) = Cfg::parse(&meta) {
+                    if let Ok(feat_cfg) = Cfg::parse(&MetaItemInner::MetaItem(meta)) {
                         cfg &= feat_cfg;
                     }
                 }
@@ -1054,7 +1055,7 @@ pub(crate) trait AttributesExt {
 }
 
 impl AttributesExt for [ast::Attribute] {
-    type AttributeIterator<'a> = impl Iterator<Item = ast::NestedMetaItem> + 'a;
+    type AttributeIterator<'a> = impl Iterator<Item = ast::MetaItemInner> + 'a;
     type Attributes<'a> = impl Iterator<Item = &'a ast::Attribute> + 'a;
 
     fn lists(&self, name: Symbol) -> Self::AttributeIterator<'_> {
@@ -1071,7 +1072,7 @@ impl AttributesExt for [ast::Attribute] {
 
 impl AttributesExt for [(Cow<'_, ast::Attribute>, Option<DefId>)] {
     type AttributeIterator<'a>
-        = impl Iterator<Item = ast::NestedMetaItem> + 'a
+        = impl Iterator<Item = ast::MetaItemInner> + 'a
     where
         Self: 'a;
     type Attributes<'a>
@@ -1105,11 +1106,11 @@ pub(crate) trait NestedAttributesExt {
 
     /// Returns `Some(attr)` if the attribute list contains 'attr'
     /// corresponding to a specific `word`
-    fn get_word_attr(self, word: Symbol) -> Option<ast::NestedMetaItem>;
+    fn get_word_attr(self, word: Symbol) -> Option<ast::MetaItemInner>;
 }
 
-impl<I: Iterator<Item = ast::NestedMetaItem>> NestedAttributesExt for I {
-    fn get_word_attr(mut self, word: Symbol) -> Option<ast::NestedMetaItem> {
+impl<I: Iterator<Item = ast::MetaItemInner>> NestedAttributesExt for I {
+    fn get_word_attr(mut self, word: Symbol) -> Option<ast::MetaItemInner> {
         self.find(|attr| attr.is_word() && attr.has_name(word))
     }
 }
@@ -1156,7 +1157,7 @@ pub(crate) struct Attributes {
 }
 
 impl Attributes {
-    pub(crate) fn lists(&self, name: Symbol) -> impl Iterator<Item = ast::NestedMetaItem> + '_ {
+    pub(crate) fn lists(&self, name: Symbol) -> impl Iterator<Item = ast::MetaItemInner> + '_ {
         self.other_attrs.lists(name)
     }
 
@@ -1222,7 +1223,7 @@ impl Attributes {
     }
 
     pub(crate) fn get_doc_aliases(&self) -> Box<[Symbol]> {
-        let mut aliases = FxHashSet::default();
+        let mut aliases = FxIndexSet::default();
 
         for attr in self.other_attrs.lists(sym::doc).filter(|a| a.has_name(sym::alias)) {
             if let Some(values) = attr.meta_item_list() {
@@ -1758,7 +1759,7 @@ pub(crate) enum PrimitiveType {
     Never,
 }
 
-type SimplifiedTypes = FxHashMap<PrimitiveType, ArrayVec<SimplifiedType, 3>>;
+type SimplifiedTypes = FxIndexMap<PrimitiveType, ArrayVec<SimplifiedType, 3>>;
 impl PrimitiveType {
     pub(crate) fn from_hir(prim: hir::PrimTy) -> PrimitiveType {
         use ast::{FloatTy, IntTy, UintTy};
@@ -1926,10 +1927,10 @@ impl PrimitiveType {
     /// In particular, if a crate depends on both `std` and another crate that also defines
     /// `rustc_doc_primitive`, then it's entirely random whether `std` or the other crate is picked.
     /// (no_std crates are usually fine unless multiple dependencies define a primitive.)
-    pub(crate) fn primitive_locations(tcx: TyCtxt<'_>) -> &FxHashMap<PrimitiveType, DefId> {
-        static PRIMITIVE_LOCATIONS: OnceCell<FxHashMap<PrimitiveType, DefId>> = OnceCell::new();
+    pub(crate) fn primitive_locations(tcx: TyCtxt<'_>) -> &FxIndexMap<PrimitiveType, DefId> {
+        static PRIMITIVE_LOCATIONS: OnceCell<FxIndexMap<PrimitiveType, DefId>> = OnceCell::new();
         PRIMITIVE_LOCATIONS.get_or_init(|| {
-            let mut primitive_locations = FxHashMap::default();
+            let mut primitive_locations = FxIndexMap::default();
             // NOTE: technically this misses crates that are only passed with `--extern` and not loaded when checking the crate.
             // This is a degenerate case that I don't plan to support.
             for &crate_num in tcx.crates(()) {
@@ -2459,7 +2460,7 @@ pub(crate) struct Impl {
 }
 
 impl Impl {
-    pub(crate) fn provided_trait_methods(&self, tcx: TyCtxt<'_>) -> FxHashSet<Symbol> {
+    pub(crate) fn provided_trait_methods(&self, tcx: TyCtxt<'_>) -> FxIndexSet<Symbol> {
         self.trait_
             .as_ref()
             .map(|t| t.def_id())
diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs
index 7a37f5c70a5..2cd69474b1c 100644
--- a/src/librustdoc/config.rs
+++ b/src/librustdoc/config.rs
@@ -5,7 +5,7 @@ use std::path::{Path, PathBuf};
 use std::str::FromStr;
 use std::{fmt, io};
 
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::FxIndexMap;
 use rustc_errors::DiagCtxtHandle;
 use rustc_session::config::{
     self, CodegenOptions, CrateType, ErrorOutputType, Externs, Input, JsonUnusedExterns,
@@ -249,7 +249,7 @@ pub(crate) struct RenderOptions {
     pub(crate) extern_html_root_takes_precedence: bool,
     /// A map of the default settings (values are as for DOM storage API). Keys should lack the
     /// `rustdoc-` prefix.
-    pub(crate) default_settings: FxHashMap<String, String>,
+    pub(crate) default_settings: FxIndexMap<String, String>,
     /// If present, suffix added to CSS/JavaScript files when referencing them in generated pages.
     pub(crate) resource_suffix: String,
     /// Whether to create an index page in the root of the output directory. If this is true but
diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs
index 4c48c075a94..aaf4c80f997 100644
--- a/src/librustdoc/core.rs
+++ b/src/librustdoc/core.rs
@@ -2,7 +2,7 @@ use std::sync::atomic::AtomicBool;
 use std::sync::{Arc, LazyLock};
 use std::{io, mem};
 
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
 use rustc_data_structures::sync::Lrc;
 use rustc_data_structures::unord::UnordSet;
 use rustc_errors::codes::*;
@@ -39,7 +39,7 @@ pub(crate) struct DocContext<'tcx> {
     /// Most of this logic is copied from rustc_lint::late.
     pub(crate) param_env: ParamEnv<'tcx>,
     /// Later on moved through `clean::Crate` into `cache`
-    pub(crate) external_traits: FxHashMap<DefId, clean::Trait>,
+    pub(crate) external_traits: FxIndexMap<DefId, clean::Trait>,
     /// Used while populating `external_traits` to ensure we don't process the same trait twice at
     /// the same time.
     pub(crate) active_extern_traits: DefIdSet,
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index 4e7571a4803..d5bc2a93fa8 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -14,7 +14,7 @@ use std::{panic, str};
 pub(crate) use make::DocTestBuilder;
 pub(crate) use markdown::test as test_markdown;
 use rustc_ast as ast;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::fx::{FxHashMap, FxIndexMap, FxIndexSet};
 use rustc_errors::{ColorConfig, DiagCtxtHandle, ErrorGuaranteed, FatalError};
 use rustc_hir::CRATE_HIR_ID;
 use rustc_hir::def_id::LOCAL_CRATE;
@@ -213,12 +213,13 @@ pub(crate) fn run(
         let unused_extern_reports: Vec<_> =
             std::mem::take(&mut unused_extern_reports.lock().unwrap());
         if unused_extern_reports.len() == compiling_test_count {
-            let extern_names = externs.iter().map(|(name, _)| name).collect::<FxHashSet<&String>>();
+            let extern_names =
+                externs.iter().map(|(name, _)| name).collect::<FxIndexSet<&String>>();
             let mut unused_extern_names = unused_extern_reports
                 .iter()
-                .map(|uexts| uexts.unused_extern_names.iter().collect::<FxHashSet<&String>>())
+                .map(|uexts| uexts.unused_extern_names.iter().collect::<FxIndexSet<&String>>())
                 .fold(extern_names, |uextsa, uextsb| {
-                    uextsa.intersection(&uextsb).copied().collect::<FxHashSet<&String>>()
+                    uextsa.intersection(&uextsb).copied().collect::<FxIndexSet<&String>>()
                 })
                 .iter()
                 .map(|v| (*v).clone())
@@ -253,7 +254,7 @@ pub(crate) fn run_tests(
     rustdoc_options: &Arc<RustdocOptions>,
     unused_extern_reports: &Arc<Mutex<Vec<UnusedExterns>>>,
     mut standalone_tests: Vec<test::TestDescAndFn>,
-    mergeable_tests: FxHashMap<Edition, Vec<(DocTestBuilder, ScrapedDocTest)>>,
+    mergeable_tests: FxIndexMap<Edition, Vec<(DocTestBuilder, ScrapedDocTest)>>,
 ) {
     let mut test_args = Vec::with_capacity(rustdoc_options.test_args.len() + 1);
     test_args.insert(0, "rustdoctest".to_string());
@@ -775,7 +776,7 @@ pub(crate) trait DocTestVisitor {
 
 struct CreateRunnableDocTests {
     standalone_tests: Vec<test::TestDescAndFn>,
-    mergeable_tests: FxHashMap<Edition, Vec<(DocTestBuilder, ScrapedDocTest)>>,
+    mergeable_tests: FxIndexMap<Edition, Vec<(DocTestBuilder, ScrapedDocTest)>>,
 
     rustdoc_options: Arc<RustdocOptions>,
     opts: GlobalTestOptions,
@@ -790,7 +791,7 @@ impl CreateRunnableDocTests {
         let can_merge_doctests = rustdoc_options.edition >= Edition::Edition2024;
         CreateRunnableDocTests {
             standalone_tests: Vec::new(),
-            mergeable_tests: FxHashMap::default(),
+            mergeable_tests: FxIndexMap::default(),
             rustdoc_options: Arc::new(rustdoc_options),
             opts,
             visited_tests: FxHashMap::default(),
diff --git a/src/librustdoc/doctest/runner.rs b/src/librustdoc/doctest/runner.rs
index 326ca4ee1e6..942ec8d9936 100644
--- a/src/librustdoc/doctest/runner.rs
+++ b/src/librustdoc/doctest/runner.rs
@@ -1,6 +1,6 @@
 use std::fmt::Write;
 
-use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::fx::FxIndexSet;
 use rustc_span::edition::Edition;
 
 use crate::doctest::{
@@ -11,7 +11,7 @@ use crate::html::markdown::{Ignore, LangString};
 
 /// Convenient type to merge compatible doctests into one.
 pub(crate) struct DocTestRunner {
-    crate_attrs: FxHashSet<String>,
+    crate_attrs: FxIndexSet<String>,
     ids: String,
     output: String,
     supports_color: bool,
@@ -21,7 +21,7 @@ pub(crate) struct DocTestRunner {
 impl DocTestRunner {
     pub(crate) fn new() -> Self {
         Self {
-            crate_attrs: FxHashSet::default(),
+            crate_attrs: FxIndexSet::default(),
             ids: String::new(),
             output: String::new(),
             supports_color: true,
diff --git a/src/librustdoc/formats/cache.rs b/src/librustdoc/formats/cache.rs
index db1a0bd0af9..ff0d537b19f 100644
--- a/src/librustdoc/formats/cache.rs
+++ b/src/librustdoc/formats/cache.rs
@@ -1,6 +1,6 @@
 use std::mem;
 
-use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
+use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet};
 use rustc_middle::ty::{self, TyCtxt};
 use rustc_span::Symbol;
@@ -42,7 +42,7 @@ pub(crate) struct Cache {
     /// URLs when a type is being linked to. External paths are not located in
     /// this map because the `External` type itself has all the information
     /// necessary.
-    pub(crate) paths: FxHashMap<DefId, (Vec<Symbol>, ItemType)>,
+    pub(crate) paths: FxIndexMap<DefId, (Vec<Symbol>, ItemType)>,
 
     /// Similar to `paths`, but only holds external paths. This is only used for
     /// generating explicit hyperlinks to other crates.
@@ -64,18 +64,18 @@ pub(crate) struct Cache {
     /// Implementations of a crate should inherit the documentation of the
     /// parent trait if no extra documentation is specified, and default methods
     /// should show up in documentation about trait implementations.
-    pub(crate) traits: FxHashMap<DefId, clean::Trait>,
+    pub(crate) traits: FxIndexMap<DefId, clean::Trait>,
 
     /// When rendering traits, it's often useful to be able to list all
     /// implementors of the trait, and this mapping is exactly, that: a mapping
     /// of trait ids to the list of known implementors of the trait
-    pub(crate) implementors: FxHashMap<DefId, Vec<Impl>>,
+    pub(crate) implementors: FxIndexMap<DefId, Vec<Impl>>,
 
     /// Cache of where external crate documentation can be found.
-    pub(crate) extern_locations: FxHashMap<CrateNum, ExternalLocation>,
+    pub(crate) extern_locations: FxIndexMap<CrateNum, ExternalLocation>,
 
     /// Cache of where documentation for primitives can be found.
-    pub(crate) primitive_locations: FxHashMap<clean::PrimitiveType, DefId>,
+    pub(crate) primitive_locations: FxIndexMap<clean::PrimitiveType, DefId>,
 
     // Note that external items for which `doc(hidden)` applies to are shown as
     // non-reachable while local items aren't. This is because we're reusing
@@ -118,7 +118,7 @@ pub(crate) struct Cache {
     // crawl. In order to prevent crashes when looking for notable traits or
     // when gathering trait documentation on a type, hold impls here while
     // folding and add them to the cache later on if we find the trait.
-    orphan_trait_impls: Vec<(DefId, FxHashSet<DefId>, Impl)>,
+    orphan_trait_impls: Vec<(DefId, FxIndexSet<DefId>, Impl)>,
 
     /// All intra-doc links resolved so far.
     ///
@@ -376,7 +376,7 @@ impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> {
             // Figure out the id of this impl. This may map to a
             // primitive rather than always to a struct/enum.
             // Note: matching twice to restrict the lifetime of the `i` borrow.
-            let mut dids = FxHashSet::default();
+            let mut dids = FxIndexSet::default();
             match i.for_ {
                 clean::Type::Path { ref path }
                 | clean::BorrowedRef { type_: box clean::Type::Path { ref path }, .. } => {
diff --git a/src/librustdoc/html/escape.rs b/src/librustdoc/html/escape.rs
index 691f86847b5..31a2701f06a 100644
--- a/src/librustdoc/html/escape.rs
+++ b/src/librustdoc/html/escape.rs
@@ -108,7 +108,17 @@ impl<'a> fmt::Display for EscapeBodyTextWithWbr<'a> {
                 || pk.map_or(true, |(_, t)| t.chars().any(|c| c.is_uppercase()));
             let next_is_underscore = || pk.map_or(true, |(_, t)| t.contains('_'));
             let next_is_colon = || pk.map_or(true, |(_, t)| t.contains(':'));
-            if i - last > 3 && is_uppercase() && !next_is_uppercase() {
+            // Check for CamelCase.
+            //
+            // `i - last > 3` avoids turning FmRadio into Fm<wbr>Radio, which is technically
+            // correct, but needlessly bloated.
+            //
+            // is_uppercase && !next_is_uppercase checks for camelCase. HTTPSProxy,
+            // for example, should become HTTPS<wbr>Proxy.
+            //
+            // !next_is_underscore avoids turning TEST_RUN into TEST<wbr>_<wbr>RUN, which is also
+            // needlessly bloated.
+            if i - last > 3 && is_uppercase() && !next_is_uppercase() && !next_is_underscore() {
                 EscapeBodyText(&text[last..i]).fmt(fmt)?;
                 fmt.write_str("<wbr>")?;
                 last = i;
diff --git a/src/librustdoc/html/escape/tests.rs b/src/librustdoc/html/escape/tests.rs
index a09649e9e18..de702e16063 100644
--- a/src/librustdoc/html/escape/tests.rs
+++ b/src/librustdoc/html/escape/tests.rs
@@ -24,6 +24,10 @@ fn escape_body_text_with_wbr() {
     assert_eq!(&E("first:second").to_string(), "first:<wbr>second");
     assert_eq!(&E("first::second").to_string(), "first::<wbr>second");
     assert_eq!(&E("MY_CONSTANT").to_string(), "MY_<wbr>CONSTANT");
+    assert_eq!(
+        &E("_SIDD_MASKED_NEGATIVE_POLARITY").to_string(),
+        "_SIDD_<wbr>MASKED_<wbr>NEGATIVE_<wbr>POLARITY"
+    );
     // a string won't get wrapped if it's less than 8 bytes
     assert_eq!(&E("HashSet").to_string(), "HashSet");
     // an individual word won't get wrapped if it's less than 4 bytes
diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs
index 69b3421f888..b68b7295096 100644
--- a/src/librustdoc/html/highlight.rs
+++ b/src/librustdoc/html/highlight.rs
@@ -8,7 +8,7 @@
 use std::collections::VecDeque;
 use std::fmt::{Display, Write};
 
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::FxIndexMap;
 use rustc_lexer::{Cursor, LiteralKind, TokenKind};
 use rustc_span::edition::Edition;
 use rustc_span::symbol::Symbol;
@@ -34,7 +34,7 @@ pub(crate) struct HrefContext<'a, 'tcx> {
 /// Decorations are represented as a map from CSS class to vector of character ranges.
 /// Each range will be wrapped in a span with that class.
 #[derive(Default)]
-pub(crate) struct DecorationInfo(pub(crate) FxHashMap<&'static str, Vec<(u32, u32)>>);
+pub(crate) struct DecorationInfo(pub(crate) FxIndexMap<&'static str, Vec<(u32, u32)>>);
 
 #[derive(Eq, PartialEq, Clone, Copy)]
 pub(crate) enum Tooltip {
@@ -845,6 +845,7 @@ impl<'src> Classifier<'src> {
                 // Number literals.
                 LiteralKind::Float { .. } | LiteralKind::Int { .. } => Class::Number,
             },
+            TokenKind::GuardedStrPrefix => return no_highlight(sink),
             TokenKind::Ident | TokenKind::RawIdent if lookahead == Some(TokenKind::Bang) => {
                 self.in_macro = true;
                 sink(Highlight::EnterSpan { class: Class::Macro(self.new_span(before, text)) });
diff --git a/src/librustdoc/html/highlight/tests.rs b/src/librustdoc/html/highlight/tests.rs
index 75328e724fe..fd5275189d6 100644
--- a/src/librustdoc/html/highlight/tests.rs
+++ b/src/librustdoc/html/highlight/tests.rs
@@ -1,5 +1,5 @@
 use expect_test::expect_file;
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::FxIndexMap;
 use rustc_span::create_default_session_globals_then;
 
 use super::{DecorationInfo, write_code};
@@ -73,7 +73,7 @@ fn test_decorations() {
 let y = 2;
 let z = 3;
 let a = 4;";
-        let mut decorations = FxHashMap::default();
+        let mut decorations = FxIndexMap::default();
         decorations.insert("example", vec![(0, 10), (11, 21)]);
         decorations.insert("example2", vec![(22, 32)]);
 
diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs
index 3684dc42ac7..31ccfeb3873 100644
--- a/src/librustdoc/html/layout.rs
+++ b/src/librustdoc/html/layout.rs
@@ -1,7 +1,7 @@
 use std::path::PathBuf;
 
 use rinja::Template;
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::FxIndexMap;
 
 use super::static_files::{STATIC_FILES, StaticFiles};
 use crate::externalfiles::ExternalHtml;
@@ -13,7 +13,7 @@ pub(crate) struct Layout {
     pub(crate) logo: String,
     pub(crate) favicon: String,
     pub(crate) external_html: ExternalHtml,
-    pub(crate) default_settings: FxHashMap<String, String>,
+    pub(crate) default_settings: FxIndexMap<String, String>,
     pub(crate) krate: String,
     pub(crate) krate_version: String,
     /// The given user css file which allow to customize the generated
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index 8ae5484feda..5dacabd031e 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -35,10 +35,9 @@ use std::str::{self, CharIndices};
 use std::sync::OnceLock;
 
 use pulldown_cmark::{
-    BrokenLink, BrokenLinkCallback, CodeBlockKind, CowStr, Event, LinkType, OffsetIter, Options,
-    Parser, Tag, TagEnd, html,
+    BrokenLink, CodeBlockKind, CowStr, Event, LinkType, Options, Parser, Tag, TagEnd, html,
 };
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
 use rustc_errors::{Diag, DiagMessage};
 use rustc_hir::def_id::LocalDefId;
 use rustc_middle::ty::TyCtxt;
@@ -651,12 +650,12 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for SummaryLine<'a, I> {
 /// references.
 struct Footnotes<'a, I> {
     inner: I,
-    footnotes: FxHashMap<String, (Vec<Event<'a>>, u16)>,
+    footnotes: FxIndexMap<String, (Vec<Event<'a>>, u16)>,
 }
 
 impl<'a, I> Footnotes<'a, I> {
     fn new(iter: I) -> Self {
-        Footnotes { inner: iter, footnotes: FxHashMap::default() }
+        Footnotes { inner: iter, footnotes: FxIndexMap::default() }
     }
 
     fn get_entry(&mut self, key: &str) -> &mut (Vec<Event<'a>>, u16) {
@@ -694,7 +693,7 @@ impl<'a, I: Iterator<Item = SpannedEvent<'a>>> Iterator for Footnotes<'a, I> {
                 Some(e) => return Some(e),
                 None => {
                     if !self.footnotes.is_empty() {
-                        let mut v: Vec<_> = self.footnotes.drain().map(|(_, x)| x).collect();
+                        let mut v: Vec<_> = self.footnotes.drain(..).map(|(_, x)| x).collect();
                         v.sort_by(|a, b| a.1.cmp(&b.1));
                         let mut ret = String::from("<div class=\"footnotes\"><hr><ol>");
                         for (mut content, id) in v {
@@ -1686,7 +1685,6 @@ pub(crate) fn html_text_from_events<'a>(
 pub(crate) struct MarkdownLink {
     pub kind: LinkType,
     pub link: String,
-    pub display_text: Option<String>,
     pub range: MarkdownLinkRange,
 }
 
@@ -1848,23 +1846,9 @@ pub(crate) fn markdown_links<'md, R>(
                     LinkType::Autolink | LinkType::Email => unreachable!(),
                 };
 
-                let display_text = if matches!(
-                    link_type,
-                    LinkType::Inline
-                        | LinkType::ReferenceUnknown
-                        | LinkType::Reference
-                        | LinkType::Shortcut
-                        | LinkType::ShortcutUnknown
-                ) {
-                    collect_link_data(&mut event_iter)
-                } else {
-                    None
-                };
-
                 if let Some(link) = preprocess_link(MarkdownLink {
                     kind: link_type,
                     link: dest_url.into_string(),
-                    display_text,
                     range,
                 }) {
                     links.push(link);
@@ -1877,37 +1861,6 @@ pub(crate) fn markdown_links<'md, R>(
     links
 }
 
-/// Collects additional data of link.
-fn collect_link_data<'input, F: BrokenLinkCallback<'input>>(
-    event_iter: &mut OffsetIter<'input, F>,
-) -> Option<String> {
-    let mut display_text: Option<String> = None;
-    let mut append_text = |text: CowStr<'_>| {
-        if let Some(display_text) = &mut display_text {
-            display_text.push_str(&text);
-        } else {
-            display_text = Some(text.to_string());
-        }
-    };
-
-    while let Some((event, _span)) = event_iter.next() {
-        match event {
-            Event::Text(text) => {
-                append_text(text);
-            }
-            Event::Code(code) => {
-                append_text(code);
-            }
-            Event::End(_) => {
-                break;
-            }
-            _ => {}
-        }
-    }
-
-    display_text
-}
-
 #[derive(Debug)]
 pub(crate) struct RustCodeBlock {
     /// The range in the markdown that the code block occupies. Note that this includes the fences
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index bce3f218908..dc4d45e592e 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -6,7 +6,7 @@ use std::rc::Rc;
 use std::sync::mpsc::{Receiver, channel};
 
 use rinja::Template;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_hir::def_id::{DefIdMap, LOCAL_CRATE};
 use rustc_middle::ty::TyCtxt;
 use rustc_session::Session;
@@ -69,16 +69,16 @@ pub(crate) struct Context<'tcx> {
     /// `true`.
     pub(crate) include_sources: bool,
     /// Collection of all types with notable traits referenced in the current module.
-    pub(crate) types_with_notable_traits: FxHashSet<clean::Type>,
+    pub(crate) types_with_notable_traits: FxIndexSet<clean::Type>,
     /// Field used during rendering, to know if we're inside an inlined item.
     pub(crate) is_inside_inlined_module: bool,
 }
 
 // `Context` is cloned a lot, so we don't want the size to grow unexpectedly.
 #[cfg(all(not(windows), target_pointer_width = "64"))]
-rustc_data_structures::static_assert_size!(Context<'_>, 160);
+rustc_data_structures::static_assert_size!(Context<'_>, 184);
 #[cfg(all(windows, target_pointer_width = "64"))]
-rustc_data_structures::static_assert_size!(Context<'_>, 168);
+rustc_data_structures::static_assert_size!(Context<'_>, 192);
 
 /// Shared mutable state used in [`Context`] and elsewhere.
 pub(crate) struct SharedContext<'tcx> {
@@ -90,7 +90,7 @@ pub(crate) struct SharedContext<'tcx> {
     /// creation of the context (contains info like the favicon and added html).
     pub(crate) layout: layout::Layout,
     /// The local file sources we've emitted and their respective url-paths.
-    pub(crate) local_sources: FxHashMap<PathBuf, String>,
+    pub(crate) local_sources: FxIndexMap<PathBuf, String>,
     /// Show the memory layout of types in the docs.
     pub(super) show_type_layout: bool,
     /// The base-URL of the issue tracker for when an item has been tagged with
@@ -567,7 +567,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
             deref_id_map: Default::default(),
             shared: Rc::new(scx),
             include_sources,
-            types_with_notable_traits: FxHashSet::default(),
+            types_with_notable_traits: FxIndexSet::default(),
             is_inside_inlined_module: false,
         };
 
@@ -591,7 +591,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
             id_map: IdMap::new(),
             shared: Rc::clone(&self.shared),
             include_sources: self.include_sources,
-            types_with_notable_traits: FxHashSet::default(),
+            types_with_notable_traits: FxIndexSet::default(),
             is_inside_inlined_module: self.is_inside_inlined_module,
         }
     }
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 227df0c5f39..399730a01c8 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -47,7 +47,7 @@ use std::{fs, str};
 use rinja::Template;
 use rustc_attr::{ConstStability, DeprecatedSince, Deprecation, StabilityLevel, StableSince};
 use rustc_data_structures::captures::Captures;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_hir::Mutability;
 use rustc_hir::def_id::{DefId, DefIdSet};
 use rustc_middle::ty::print::PrintTraitRefExt;
@@ -328,24 +328,24 @@ impl Ord for ItemEntry {
 
 #[derive(Debug)]
 struct AllTypes {
-    structs: FxHashSet<ItemEntry>,
-    enums: FxHashSet<ItemEntry>,
-    unions: FxHashSet<ItemEntry>,
-    primitives: FxHashSet<ItemEntry>,
-    traits: FxHashSet<ItemEntry>,
-    macros: FxHashSet<ItemEntry>,
-    functions: FxHashSet<ItemEntry>,
-    type_aliases: FxHashSet<ItemEntry>,
-    statics: FxHashSet<ItemEntry>,
-    constants: FxHashSet<ItemEntry>,
-    attribute_macros: FxHashSet<ItemEntry>,
-    derive_macros: FxHashSet<ItemEntry>,
-    trait_aliases: FxHashSet<ItemEntry>,
+    structs: FxIndexSet<ItemEntry>,
+    enums: FxIndexSet<ItemEntry>,
+    unions: FxIndexSet<ItemEntry>,
+    primitives: FxIndexSet<ItemEntry>,
+    traits: FxIndexSet<ItemEntry>,
+    macros: FxIndexSet<ItemEntry>,
+    functions: FxIndexSet<ItemEntry>,
+    type_aliases: FxIndexSet<ItemEntry>,
+    statics: FxIndexSet<ItemEntry>,
+    constants: FxIndexSet<ItemEntry>,
+    attribute_macros: FxIndexSet<ItemEntry>,
+    derive_macros: FxIndexSet<ItemEntry>,
+    trait_aliases: FxIndexSet<ItemEntry>,
 }
 
 impl AllTypes {
     fn new() -> AllTypes {
-        let new_set = |cap| FxHashSet::with_capacity_and_hasher(cap, Default::default());
+        let new_set = |cap| FxIndexSet::with_capacity_and_hasher(cap, Default::default());
         AllTypes {
             structs: new_set(100),
             enums: new_set(100),
@@ -437,7 +437,7 @@ impl AllTypes {
     }
 
     fn print(self, f: &mut Buffer) {
-        fn print_entries(f: &mut Buffer, e: &FxHashSet<ItemEntry>, kind: ItemSection) {
+        fn print_entries(f: &mut Buffer, e: &FxIndexSet<ItemEntry>, kind: ItemSection) {
             if !e.is_empty() {
                 let mut e: Vec<&ItemEntry> = e.iter().collect();
                 e.sort();
@@ -1151,7 +1151,7 @@ fn render_attributes_in_code(w: &mut impl fmt::Write, it: &clean::Item, cx: &Con
 #[derive(Copy, Clone)]
 enum AssocItemLink<'a> {
     Anchor(Option<&'a str>),
-    GotoSource(ItemId, &'a FxHashSet<Symbol>),
+    GotoSource(ItemId, &'a FxIndexSet<Symbol>),
 }
 
 impl<'a> AssocItemLink<'a> {
@@ -1494,7 +1494,7 @@ fn notable_traits_decl(ty: &clean::Type, cx: &Context<'_>) -> (String, String) {
                 for it in &impl_.items {
                     if let clean::AssocTypeItem(ref tydef, ref _bounds) = it.kind {
                         out.push_str("<div class=\"where\">    ");
-                        let empty_set = FxHashSet::default();
+                        let empty_set = FxIndexSet::default();
                         let src_link = AssocItemLink::GotoSource(trait_did.into(), &empty_set);
                         assoc_type(
                             &mut out,
@@ -2526,7 +2526,7 @@ fn render_call_locations<W: fmt::Write>(mut w: W, cx: &mut Context<'_>, item: &c
         })()
         .unwrap_or(DUMMY_SP);
 
-        let mut decoration_info = FxHashMap::default();
+        let mut decoration_info = FxIndexMap::default();
         decoration_info.insert("highlight focus", vec![byte_ranges.remove(0)]);
         decoration_info.insert("highlight", byte_ranges);
 
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 38276e4d20c..3c96f873681 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -6,7 +6,7 @@ use std::rc::Rc;
 use itertools::Itertools;
 use rinja::Template;
 use rustc_data_structures::captures::Captures;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::fx::{FxHashMap, FxIndexSet};
 use rustc_hir as hir;
 use rustc_hir::def::CtorKind;
 use rustc_hir::def_id::DefId;
@@ -932,7 +932,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean:
 
     let cloned_shared = Rc::clone(&cx.shared);
     let cache = &cloned_shared.cache;
-    let mut extern_crates = FxHashSet::default();
+    let mut extern_crates = FxIndexSet::default();
 
     if !t.is_object_safe(cx.tcx()) {
         write_section_heading(
diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs
index 660ca3b2594..c958458b662 100644
--- a/src/librustdoc/html/render/search_index.rs
+++ b/src/librustdoc/html/render/search_index.rs
@@ -774,7 +774,7 @@ pub(crate) fn get_function_type_for_search<'tcx>(
 fn get_index_type(
     clean_type: &clean::Type,
     generics: Vec<RenderType>,
-    rgen: &mut FxHashMap<SimplifiedParam, (isize, Vec<RenderType>)>,
+    rgen: &mut FxIndexMap<SimplifiedParam, (isize, Vec<RenderType>)>,
 ) -> RenderType {
     RenderType {
         id: get_index_type_id(clean_type, rgen),
@@ -785,7 +785,7 @@ fn get_index_type(
 
 fn get_index_type_id(
     clean_type: &clean::Type,
-    rgen: &mut FxHashMap<SimplifiedParam, (isize, Vec<RenderType>)>,
+    rgen: &mut FxIndexMap<SimplifiedParam, (isize, Vec<RenderType>)>,
 ) -> Option<RenderTypeId> {
     use rustc_hir::def::{DefKind, Res};
     match *clean_type {
@@ -854,7 +854,7 @@ fn simplify_fn_type<'a, 'tcx>(
     tcx: TyCtxt<'tcx>,
     recurse: usize,
     res: &mut Vec<RenderType>,
-    rgen: &mut FxHashMap<SimplifiedParam, (isize, Vec<RenderType>)>,
+    rgen: &mut FxIndexMap<SimplifiedParam, (isize, Vec<RenderType>)>,
     is_return: bool,
     cache: &Cache,
 ) {
@@ -1198,7 +1198,7 @@ fn simplify_fn_constraint<'a, 'tcx>(
     tcx: TyCtxt<'tcx>,
     recurse: usize,
     res: &mut Vec<(RenderTypeId, Vec<RenderType>)>,
-    rgen: &mut FxHashMap<SimplifiedParam, (isize, Vec<RenderType>)>,
+    rgen: &mut FxIndexMap<SimplifiedParam, (isize, Vec<RenderType>)>,
     is_return: bool,
     cache: &Cache,
 ) {
@@ -1285,7 +1285,7 @@ fn get_fn_inputs_and_outputs<'tcx>(
 ) -> (Vec<RenderType>, Vec<RenderType>, Vec<Vec<RenderType>>) {
     let decl = &func.decl;
 
-    let mut rgen: FxHashMap<SimplifiedParam, (isize, Vec<RenderType>)> = Default::default();
+    let mut rgen: FxIndexMap<SimplifiedParam, (isize, Vec<RenderType>)> = Default::default();
 
     let combined_generics;
     let (self_, generics) = if let Some((impl_self, impl_generics)) = impl_or_trait_generics {
diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/render/span_map.rs
index 2143f1ff236..b314b060368 100644
--- a/src/librustdoc/html/render/span_map.rs
+++ b/src/librustdoc/html/render/span_map.rs
@@ -1,6 +1,6 @@
 use std::path::{Path, PathBuf};
 
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefId, LOCAL_CRATE};
 use rustc_hir::intravisit::{self, Visitor};
@@ -44,7 +44,7 @@ pub(crate) fn collect_spans_and_sources(
     src_root: &Path,
     include_sources: bool,
     generate_link_to_definition: bool,
-) -> (FxHashMap<PathBuf, String>, FxHashMap<Span, LinkFromSrc>) {
+) -> (FxIndexMap<PathBuf, String>, FxHashMap<Span, LinkFromSrc>) {
     let mut visitor = SpanMapVisitor { tcx, matches: FxHashMap::default() };
 
     if include_sources {
@@ -243,7 +243,6 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
             | ItemKind::ExternCrate(_)
             | ItemKind::ForeignMod { .. }
             | ItemKind::GlobalAsm(_)
-            | ItemKind::OpaqueTy(_)
             // We already have "visit_mod" above so no need to check it here.
             | ItemKind::Mod(_) => {}
         }
diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs
index af94b1042c0..12b63460056 100644
--- a/src/librustdoc/html/render/write_shared.rs
+++ b/src/librustdoc/html/render/write_shared.rs
@@ -28,7 +28,7 @@ use indexmap::IndexMap;
 use itertools::Itertools;
 use regex::Regex;
 use rustc_data_structures::flock;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_middle::ty::TyCtxt;
 use rustc_middle::ty::fast_reject::DeepRejectCtxt;
 use rustc_span::Symbol;
@@ -505,8 +505,8 @@ createSrcSidebar();",
 struct Hierarchy {
     parent: Weak<Self>,
     elem: OsString,
-    children: RefCell<FxHashMap<OsString, Rc<Self>>>,
-    elems: RefCell<FxHashSet<OsString>>,
+    children: RefCell<FxIndexMap<OsString, Rc<Self>>>,
+    elems: RefCell<FxIndexSet<OsString>>,
 }
 
 impl Hierarchy {
@@ -961,8 +961,8 @@ impl Serialize for AliasSerializableImpl {
 fn get_path_parts<T: CciPart>(
     dst: &Path,
     crates_info: &[CrateInfo],
-) -> FxHashMap<PathBuf, Vec<String>> {
-    let mut templates: FxHashMap<PathBuf, Vec<String>> = FxHashMap::default();
+) -> FxIndexMap<PathBuf, Vec<String>> {
+    let mut templates: FxIndexMap<PathBuf, Vec<String>> = FxIndexMap::default();
     crates_info
         .iter()
         .map(|crate_info| T::from_crate_info(crate_info).parts.iter())
diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs
index 7be9cc0b885..f4a0ef01c25 100644
--- a/src/librustdoc/html/sources.rs
+++ b/src/librustdoc/html/sources.rs
@@ -6,7 +6,7 @@ use std::rc::Rc;
 use std::{fmt, fs};
 
 use rinja::Template;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
 use rustc_hir::def_id::LOCAL_CRATE;
 use rustc_middle::ty::TyCtxt;
 use rustc_session::Session;
@@ -39,15 +39,15 @@ pub(crate) fn collect_local_sources<'tcx>(
     tcx: TyCtxt<'tcx>,
     src_root: &Path,
     krate: &clean::Crate,
-) -> FxHashMap<PathBuf, String> {
-    let mut lsc = LocalSourcesCollector { tcx, local_sources: FxHashMap::default(), src_root };
+) -> FxIndexMap<PathBuf, String> {
+    let mut lsc = LocalSourcesCollector { tcx, local_sources: FxIndexMap::default(), src_root };
     lsc.visit_crate(krate);
     lsc.local_sources
 }
 
 struct LocalSourcesCollector<'a, 'tcx> {
     tcx: TyCtxt<'tcx>,
-    local_sources: FxHashMap<PathBuf, String>,
+    local_sources: FxIndexMap<PathBuf, String>,
     src_root: &'a Path,
 }
 
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index beac7e73c62..df9776ff5f8 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -230,6 +230,9 @@ h4.code-header {
 	padding: 0;
 	white-space: pre-wrap;
 }
+.structfield {
+	margin: 0.6em 0;
+}
 
 #crate-search,
 h1, h2, h3, h4, h5, h6,
@@ -961,10 +964,13 @@ pre, .rustdoc.src .example-wrap, .example-wrap .src-line-numbers {
 }
 
 .docblock li {
-	margin-bottom: .8em;
+	margin-bottom: .4em;
 }
-.docblock li p {
-	margin-bottom: .1em;
+.docblock li p:not(:last-child) {
+	/* This margin is voluntarily smaller than `.docblock li` to keep the visual
+	   list element items separated while also having a visual separation (although
+	   smaller) for paragraphs. */
+	margin-bottom: .3em;
 }
 
 /* "where ..." clauses with block display are also smaller */
@@ -2432,7 +2438,7 @@ in src-script.js and main.js
 	}
 
 	/* Position of the "[-]" element. */
-	details.toggle:not(.top-doc) > summary {
+	details.toggle:not(.top-doc) > summary, .impl-items > section  {
 		margin-left: 10px;
 	}
 	.impl-items > details.toggle > summary:not(.hideme)::before,
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index b411f9a1a52..b8791c9918b 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -4,20 +4,17 @@
 
 #![allow(rustc::default_hash_types)]
 
-use std::fmt;
-
 use rustc_ast::ast;
 use rustc_attr::DeprecatedSince;
 use rustc_hir::def::{CtorKind, DefKind};
 use rustc_hir::def_id::DefId;
 use rustc_metadata::rendered_const;
-use rustc_middle::bug;
-use rustc_middle::ty::{self, TyCtxt};
-use rustc_span::symbol::sym;
-use rustc_span::{Pos, Symbol};
+use rustc_middle::{bug, ty};
+use rustc_span::{Pos, Symbol, sym};
 use rustc_target::spec::abi::Abi as RustcAbi;
 use rustdoc_json_types::*;
 
+use super::FullItemId;
 use crate::clean::{self, ItemId};
 use crate::formats::FormatRenderer;
 use crate::formats::item_type::ItemType;
@@ -40,7 +37,7 @@ impl JsonRenderer<'_> {
                     Some(UrlFragment::UserWritten(_)) | None => *page_id,
                 };
 
-                (String::from(&**link), id_from_item_default(id.into(), self.tcx))
+                (String::from(&**link), self.id_from_item_default(id.into()))
             })
             .collect();
         let docs = item.opt_doc_value();
@@ -48,7 +45,7 @@ impl JsonRenderer<'_> {
         let span = item.span(self.tcx);
         let visibility = item.visibility(self.tcx);
         let clean::Item { name, item_id, .. } = item;
-        let id = id_from_item(&item, self.tcx);
+        let id = self.id_from_item(&item);
         let inner = match item.kind {
             clean::KeywordItem => return None,
             clean::StrippedItem(ref inner) => {
@@ -59,12 +56,12 @@ impl JsonRenderer<'_> {
                     clean::ModuleItem(_)
                         if self.imported_items.contains(&item_id.expect_def_id()) =>
                     {
-                        from_clean_item(item, self.tcx)
+                        from_clean_item(item, self)
                     }
                     _ => return None,
                 }
             }
-            _ => from_clean_item(item, self.tcx),
+            _ => from_clean_item(item, self),
         };
         Some(Item {
             id,
@@ -105,37 +102,116 @@ impl JsonRenderer<'_> {
             Some(ty::Visibility::Public) => Visibility::Public,
             Some(ty::Visibility::Restricted(did)) if did.is_crate_root() => Visibility::Crate,
             Some(ty::Visibility::Restricted(did)) => Visibility::Restricted {
-                parent: id_from_item_default(did.into(), self.tcx),
+                parent: self.id_from_item_default(did.into()),
                 path: self.tcx.def_path(did).to_string_no_crate_verbose(),
             },
         }
     }
+
+    pub(crate) fn id_from_item_default(&self, item_id: ItemId) -> Id {
+        self.id_from_item_inner(item_id, None, None)
+    }
+
+    pub(crate) fn id_from_item_inner(
+        &self,
+        item_id: ItemId,
+        name: Option<Symbol>,
+        extra: Option<Id>,
+    ) -> Id {
+        let make_part = |def_id: DefId, name: Option<Symbol>, extra: Option<Id>| {
+            let name = match name {
+                Some(name) => Some(name),
+                None => {
+                    // We need this workaround because primitive types' DefId actually refers to
+                    // their parent module, which isn't present in the output JSON items. So
+                    // instead, we directly get the primitive symbol
+                    if matches!(self.tcx.def_kind(def_id), DefKind::Mod)
+                        && let Some(prim) = self
+                            .tcx
+                            .get_attrs(def_id, sym::rustc_doc_primitive)
+                            .find_map(|attr| attr.value_str())
+                    {
+                        Some(prim)
+                    } else {
+                        self.tcx.opt_item_name(def_id)
+                    }
+                }
+            };
+
+            FullItemId { def_id, name, extra }
+        };
+
+        let key = match item_id {
+            ItemId::DefId(did) => (make_part(did, name, extra), None),
+            ItemId::Blanket { for_, impl_id } => {
+                (make_part(impl_id, None, None), Some(make_part(for_, name, extra)))
+            }
+            ItemId::Auto { for_, trait_ } => {
+                (make_part(trait_, None, None), Some(make_part(for_, name, extra)))
+            }
+        };
+
+        let mut interner = self.id_interner.borrow_mut();
+        let len = interner.len();
+        *interner
+            .entry(key)
+            .or_insert_with(|| Id(len.try_into().expect("too many items in a crate")))
+    }
+
+    pub(crate) fn id_from_item(&self, item: &clean::Item) -> Id {
+        match item.kind {
+            clean::ItemKind::ImportItem(ref import) => {
+                let extra =
+                    import.source.did.map(ItemId::from).map(|i| self.id_from_item_default(i));
+                self.id_from_item_inner(item.item_id, item.name, extra)
+            }
+            _ => self.id_from_item_inner(item.item_id, item.name, None),
+        }
+    }
+
+    fn ids(&self, items: impl IntoIterator<Item = clean::Item>) -> Vec<Id> {
+        items
+            .into_iter()
+            .filter(|x| !x.is_stripped() && !x.is_keyword())
+            .map(|i| self.id_from_item(&i))
+            .collect()
+    }
+
+    fn ids_keeping_stripped(
+        &self,
+        items: impl IntoIterator<Item = clean::Item>,
+    ) -> Vec<Option<Id>> {
+        items
+            .into_iter()
+            .map(|i| (!i.is_stripped() && !i.is_keyword()).then(|| self.id_from_item(&i)))
+            .collect()
+    }
 }
 
-pub(crate) trait FromWithTcx<T> {
-    fn from_tcx(f: T, tcx: TyCtxt<'_>) -> Self;
+pub(crate) trait FromClean<T> {
+    fn from_clean(f: T, renderer: &JsonRenderer<'_>) -> Self;
 }
 
-pub(crate) trait IntoWithTcx<T> {
-    fn into_tcx(self, tcx: TyCtxt<'_>) -> T;
+pub(crate) trait IntoJson<T> {
+    fn into_json(self, renderer: &JsonRenderer<'_>) -> T;
 }
 
-impl<T, U> IntoWithTcx<U> for T
+impl<T, U> IntoJson<U> for T
 where
-    U: FromWithTcx<T>,
+    U: FromClean<T>,
 {
-    fn into_tcx(self, tcx: TyCtxt<'_>) -> U {
-        U::from_tcx(self, tcx)
+    fn into_json(self, renderer: &JsonRenderer<'_>) -> U {
+        U::from_clean(self, renderer)
     }
 }
 
-impl<I, T, U> FromWithTcx<I> for Vec<U>
+impl<I, T, U> FromClean<I> for Vec<U>
 where
     I: IntoIterator<Item = T>,
-    U: FromWithTcx<T>,
+    U: FromClean<T>,
 {
-    fn from_tcx(f: I, tcx: TyCtxt<'_>) -> Vec<U> {
-        f.into_iter().map(|x| x.into_tcx(tcx)).collect()
+    fn from_clean(f: I, renderer: &JsonRenderer<'_>) -> Vec<U> {
+        f.into_iter().map(|x| x.into_json(renderer)).collect()
     }
 }
 
@@ -150,37 +226,38 @@ pub(crate) fn from_deprecation(deprecation: rustc_attr::Deprecation) -> Deprecat
     Deprecation { since, note: note.map(|s| s.to_string()) }
 }
 
-impl FromWithTcx<clean::GenericArgs> for GenericArgs {
-    fn from_tcx(args: clean::GenericArgs, tcx: TyCtxt<'_>) -> Self {
+impl FromClean<clean::GenericArgs> for GenericArgs {
+    fn from_clean(args: clean::GenericArgs, renderer: &JsonRenderer<'_>) -> Self {
         use clean::GenericArgs::*;
         match args {
             AngleBracketed { args, constraints } => GenericArgs::AngleBracketed {
-                args: args.into_vec().into_tcx(tcx),
-                constraints: constraints.into_tcx(tcx),
+                args: args.into_vec().into_json(renderer),
+                constraints: constraints.into_json(renderer),
             },
             Parenthesized { inputs, output } => GenericArgs::Parenthesized {
-                inputs: inputs.into_vec().into_tcx(tcx),
-                output: output.map(|a| (*a).into_tcx(tcx)),
+                inputs: inputs.into_vec().into_json(renderer),
+                output: output.map(|a| (*a).into_json(renderer)),
             },
         }
     }
 }
 
-impl FromWithTcx<clean::GenericArg> for GenericArg {
-    fn from_tcx(arg: clean::GenericArg, tcx: TyCtxt<'_>) -> Self {
+impl FromClean<clean::GenericArg> for GenericArg {
+    fn from_clean(arg: clean::GenericArg, renderer: &JsonRenderer<'_>) -> Self {
         use clean::GenericArg::*;
         match arg {
             Lifetime(l) => GenericArg::Lifetime(convert_lifetime(l)),
-            Type(t) => GenericArg::Type(t.into_tcx(tcx)),
-            Const(box c) => GenericArg::Const(c.into_tcx(tcx)),
+            Type(t) => GenericArg::Type(t.into_json(renderer)),
+            Const(box c) => GenericArg::Const(c.into_json(renderer)),
             Infer => GenericArg::Infer,
         }
     }
 }
 
-impl FromWithTcx<clean::Constant> for Constant {
+impl FromClean<clean::Constant> for Constant {
     // FIXME(generic_const_items): Add support for generic const items.
-    fn from_tcx(constant: clean::Constant, tcx: TyCtxt<'_>) -> Self {
+    fn from_clean(constant: clean::Constant, renderer: &JsonRenderer<'_>) -> Self {
+        let tcx = renderer.tcx;
         let expr = constant.expr(tcx);
         let value = constant.value(tcx);
         let is_literal = constant.is_literal(tcx);
@@ -188,9 +265,10 @@ impl FromWithTcx<clean::Constant> for Constant {
     }
 }
 
-impl FromWithTcx<clean::ConstantKind> for Constant {
+impl FromClean<clean::ConstantKind> for Constant {
     // FIXME(generic_const_items): Add support for generic const items.
-    fn from_tcx(constant: clean::ConstantKind, tcx: TyCtxt<'_>) -> Self {
+    fn from_clean(constant: clean::ConstantKind, renderer: &JsonRenderer<'_>) -> Self {
+        let tcx = renderer.tcx;
         let expr = constant.expr(tcx);
         let value = constant.value(tcx);
         let is_literal = constant.is_literal(tcx);
@@ -198,147 +276,62 @@ impl FromWithTcx<clean::ConstantKind> for Constant {
     }
 }
 
-impl FromWithTcx<clean::AssocItemConstraint> for AssocItemConstraint {
-    fn from_tcx(constraint: clean::AssocItemConstraint, tcx: TyCtxt<'_>) -> Self {
+impl FromClean<clean::AssocItemConstraint> for AssocItemConstraint {
+    fn from_clean(constraint: clean::AssocItemConstraint, renderer: &JsonRenderer<'_>) -> Self {
         AssocItemConstraint {
             name: constraint.assoc.name.to_string(),
-            args: constraint.assoc.args.into_tcx(tcx),
-            binding: constraint.kind.into_tcx(tcx),
+            args: constraint.assoc.args.into_json(renderer),
+            binding: constraint.kind.into_json(renderer),
         }
     }
 }
 
-impl FromWithTcx<clean::AssocItemConstraintKind> for AssocItemConstraintKind {
-    fn from_tcx(kind: clean::AssocItemConstraintKind, tcx: TyCtxt<'_>) -> Self {
+impl FromClean<clean::AssocItemConstraintKind> for AssocItemConstraintKind {
+    fn from_clean(kind: clean::AssocItemConstraintKind, renderer: &JsonRenderer<'_>) -> Self {
         use clean::AssocItemConstraintKind::*;
         match kind {
-            Equality { term } => AssocItemConstraintKind::Equality(term.into_tcx(tcx)),
-            Bound { bounds } => AssocItemConstraintKind::Constraint(bounds.into_tcx(tcx)),
-        }
-    }
-}
-
-#[inline]
-pub(crate) fn id_from_item_default(item_id: ItemId, tcx: TyCtxt<'_>) -> Id {
-    id_from_item_inner(item_id, tcx, None, None)
-}
-
-/// It generates an ID as follows:
-///
-/// `CRATE_ID:ITEM_ID[:NAME_ID][-EXTRA]`:
-///   * If there is no `name`, `NAME_ID` is not generated.
-///   * If there is no `extra`, `EXTRA` is not generated.
-///
-/// * `name` is the item's name if available (it's not for impl blocks for example).
-/// * `extra` is used for reexports: it contains the ID of the reexported item. It is used to allow
-///   to have items with the same name but different types to both appear in the generated JSON.
-pub(crate) fn id_from_item_inner(
-    item_id: ItemId,
-    tcx: TyCtxt<'_>,
-    name: Option<Symbol>,
-    extra: Option<&Id>,
-) -> Id {
-    struct DisplayDefId<'a, 'b>(DefId, TyCtxt<'a>, Option<&'b Id>, Option<Symbol>);
-
-    impl<'a, 'b> fmt::Display for DisplayDefId<'a, 'b> {
-        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-            let DisplayDefId(def_id, tcx, extra, name) = self;
-            // We need this workaround because primitive types' DefId actually refers to
-            // their parent module, which isn't present in the output JSON items. So
-            // instead, we directly get the primitive symbol and convert it to u32 to
-            // generate the ID.
-            let s;
-            let extra = if let Some(e) = extra {
-                s = format!("-{}", e.0);
-                &s
-            } else {
-                ""
-            };
-            let name = match name {
-                Some(name) => format!(":{}", name.as_u32()),
-                None => {
-                    // We need this workaround because primitive types' DefId actually refers to
-                    // their parent module, which isn't present in the output JSON items. So
-                    // instead, we directly get the primitive symbol and convert it to u32 to
-                    // generate the ID.
-                    if matches!(tcx.def_kind(def_id), DefKind::Mod)
-                        && let Some(prim) = tcx
-                            .get_attrs(*def_id, sym::rustc_doc_primitive)
-                            .find_map(|attr| attr.value_str())
-                    {
-                        format!(":{}", prim.as_u32())
-                    } else {
-                        tcx.opt_item_name(*def_id)
-                            .map(|n| format!(":{}", n.as_u32()))
-                            .unwrap_or_default()
-                    }
-                }
-            };
-            write!(f, "{}:{}{name}{extra}", def_id.krate.as_u32(), u32::from(def_id.index))
-        }
-    }
-
-    match item_id {
-        ItemId::DefId(did) => Id(format!("{}", DisplayDefId(did, tcx, extra, name))),
-        ItemId::Blanket { for_, impl_id } => Id(format!(
-            "b:{}-{}",
-            DisplayDefId(impl_id, tcx, None, None),
-            DisplayDefId(for_, tcx, extra, name)
-        )),
-        ItemId::Auto { for_, trait_ } => Id(format!(
-            "a:{}-{}",
-            DisplayDefId(trait_, tcx, None, None),
-            DisplayDefId(for_, tcx, extra, name)
-        )),
-    }
-}
-
-pub(crate) fn id_from_item(item: &clean::Item, tcx: TyCtxt<'_>) -> Id {
-    match item.kind {
-        clean::ItemKind::ImportItem(ref import) => {
-            let extra =
-                import.source.did.map(ItemId::from).map(|i| id_from_item_inner(i, tcx, None, None));
-            id_from_item_inner(item.item_id, tcx, item.name, extra.as_ref())
+            Equality { term } => AssocItemConstraintKind::Equality(term.into_json(renderer)),
+            Bound { bounds } => AssocItemConstraintKind::Constraint(bounds.into_json(renderer)),
         }
-        _ => id_from_item_inner(item.item_id, tcx, item.name, None),
     }
 }
 
-fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
+fn from_clean_item(item: clean::Item, renderer: &JsonRenderer<'_>) -> ItemEnum {
     use clean::ItemKind::*;
     let name = item.name;
     let is_crate = item.is_crate();
-    let header = item.fn_header(tcx);
+    let header = item.fn_header(renderer.tcx);
 
     match item.inner.kind {
         ModuleItem(m) => {
-            ItemEnum::Module(Module { is_crate, items: ids(m.items, tcx), is_stripped: false })
-        }
-        ImportItem(i) => ItemEnum::Use(i.into_tcx(tcx)),
-        StructItem(s) => ItemEnum::Struct(s.into_tcx(tcx)),
-        UnionItem(u) => ItemEnum::Union(u.into_tcx(tcx)),
-        StructFieldItem(f) => ItemEnum::StructField(f.into_tcx(tcx)),
-        EnumItem(e) => ItemEnum::Enum(e.into_tcx(tcx)),
-        VariantItem(v) => ItemEnum::Variant(v.into_tcx(tcx)),
-        FunctionItem(f) => ItemEnum::Function(from_function(f, true, header.unwrap(), tcx)),
+            ItemEnum::Module(Module { is_crate, items: renderer.ids(m.items), is_stripped: false })
+        }
+        ImportItem(i) => ItemEnum::Use(i.into_json(renderer)),
+        StructItem(s) => ItemEnum::Struct(s.into_json(renderer)),
+        UnionItem(u) => ItemEnum::Union(u.into_json(renderer)),
+        StructFieldItem(f) => ItemEnum::StructField(f.into_json(renderer)),
+        EnumItem(e) => ItemEnum::Enum(e.into_json(renderer)),
+        VariantItem(v) => ItemEnum::Variant(v.into_json(renderer)),
+        FunctionItem(f) => ItemEnum::Function(from_function(f, true, header.unwrap(), renderer)),
         ForeignFunctionItem(f, _) => {
-            ItemEnum::Function(from_function(f, false, header.unwrap(), tcx))
-        }
-        TraitItem(t) => ItemEnum::Trait((*t).into_tcx(tcx)),
-        TraitAliasItem(t) => ItemEnum::TraitAlias(t.into_tcx(tcx)),
-        MethodItem(m, _) => ItemEnum::Function(from_function(m, true, header.unwrap(), tcx)),
-        TyMethodItem(m) => ItemEnum::Function(from_function(m, false, header.unwrap(), tcx)),
-        ImplItem(i) => ItemEnum::Impl((*i).into_tcx(tcx)),
-        StaticItem(s) => ItemEnum::Static(s.into_tcx(tcx)),
-        ForeignStaticItem(s, _) => ItemEnum::Static(s.into_tcx(tcx)),
+            ItemEnum::Function(from_function(f, false, header.unwrap(), renderer))
+        }
+        TraitItem(t) => ItemEnum::Trait((*t).into_json(renderer)),
+        TraitAliasItem(t) => ItemEnum::TraitAlias(t.into_json(renderer)),
+        MethodItem(m, _) => ItemEnum::Function(from_function(m, true, header.unwrap(), renderer)),
+        TyMethodItem(m) => ItemEnum::Function(from_function(m, false, header.unwrap(), renderer)),
+        ImplItem(i) => ItemEnum::Impl((*i).into_json(renderer)),
+        StaticItem(s) => ItemEnum::Static(s.into_json(renderer)),
+        ForeignStaticItem(s, _) => ItemEnum::Static(s.into_json(renderer)),
         ForeignTypeItem => ItemEnum::ExternType,
-        TypeAliasItem(t) => ItemEnum::TypeAlias(t.into_tcx(tcx)),
+        TypeAliasItem(t) => ItemEnum::TypeAlias(t.into_json(renderer)),
         // FIXME(generic_const_items): Add support for generic free consts
-        ConstantItem(ci) => {
-            ItemEnum::Constant { type_: ci.type_.into_tcx(tcx), const_: ci.kind.into_tcx(tcx) }
-        }
+        ConstantItem(ci) => ItemEnum::Constant {
+            type_: ci.type_.into_json(renderer),
+            const_: ci.kind.into_json(renderer),
+        },
         MacroItem(m) => ItemEnum::Macro(m.source),
-        ProcMacroItem(m) => ItemEnum::ProcMacro(m.into_tcx(tcx)),
+        ProcMacroItem(m) => ItemEnum::ProcMacro(m.into_json(renderer)),
         PrimitiveItem(p) => {
             ItemEnum::Primitive(Primitive {
                 name: p.as_sym().to_string(),
@@ -347,19 +340,22 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
         }
         // FIXME(generic_const_items): Add support for generic associated consts.
         TyAssocConstItem(_generics, ty) => {
-            ItemEnum::AssocConst { type_: (*ty).into_tcx(tcx), value: None }
+            ItemEnum::AssocConst { type_: (*ty).into_json(renderer), value: None }
         }
         // FIXME(generic_const_items): Add support for generic associated consts.
-        AssocConstItem(ci) => {
-            ItemEnum::AssocConst { type_: ci.type_.into_tcx(tcx), value: Some(ci.kind.expr(tcx)) }
-        }
-        TyAssocTypeItem(g, b) => {
-            ItemEnum::AssocType { generics: g.into_tcx(tcx), bounds: b.into_tcx(tcx), type_: None }
-        }
+        AssocConstItem(ci) => ItemEnum::AssocConst {
+            type_: ci.type_.into_json(renderer),
+            value: Some(ci.kind.expr(renderer.tcx)),
+        },
+        TyAssocTypeItem(g, b) => ItemEnum::AssocType {
+            generics: g.into_json(renderer),
+            bounds: b.into_json(renderer),
+            type_: None,
+        },
         AssocTypeItem(t, b) => ItemEnum::AssocType {
-            generics: t.generics.into_tcx(tcx),
-            bounds: b.into_tcx(tcx),
-            type_: Some(t.item_type.unwrap_or(t.type_).into_tcx(tcx)),
+            generics: t.generics.into_json(renderer),
+            bounds: b.into_json(renderer),
+            type_: Some(t.item_type.unwrap_or(t.type_).into_json(renderer)),
         },
         // `convert_item` early returns `None` for stripped items and keywords.
         KeywordItem => unreachable!(),
@@ -367,7 +363,7 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
             match *inner {
                 ModuleItem(m) => ItemEnum::Module(Module {
                     is_crate,
-                    items: ids(m.items, tcx),
+                    items: renderer.ids(m.items),
                     is_stripped: true,
                 }),
                 // `convert_item` early returns `None` for stripped items we're not including
@@ -381,36 +377,36 @@ fn from_clean_item(item: clean::Item, tcx: TyCtxt<'_>) -> ItemEnum {
     }
 }
 
-impl FromWithTcx<clean::Struct> for Struct {
-    fn from_tcx(struct_: clean::Struct, tcx: TyCtxt<'_>) -> Self {
+impl FromClean<clean::Struct> for Struct {
+    fn from_clean(struct_: clean::Struct, renderer: &JsonRenderer<'_>) -> Self {
         let has_stripped_fields = struct_.has_stripped_entries();
         let clean::Struct { ctor_kind, generics, fields } = struct_;
 
         let kind = match ctor_kind {
-            Some(CtorKind::Fn) => StructKind::Tuple(ids_keeping_stripped(fields, tcx)),
+            Some(CtorKind::Fn) => StructKind::Tuple(renderer.ids_keeping_stripped(fields)),
             Some(CtorKind::Const) => {
                 assert!(fields.is_empty());
                 StructKind::Unit
             }
-            None => StructKind::Plain { fields: ids(fields, tcx), has_stripped_fields },
+            None => StructKind::Plain { fields: renderer.ids(fields), has_stripped_fields },
         };
 
         Struct {
             kind,
-            generics: generics.into_tcx(tcx),
+            generics: generics.into_json(renderer),
             impls: Vec::new(), // Added in JsonRenderer::item
         }
     }
 }
 
-impl FromWithTcx<clean::Union> for Union {
-    fn from_tcx(union_: clean::Union, tcx: TyCtxt<'_>) -> Self {
+impl FromClean<clean::Union> for Union {
+    fn from_clean(union_: clean::Union, renderer: &JsonRenderer<'_>) -> Self {
         let has_stripped_fields = union_.has_stripped_entries();
         let clean::Union { generics, fields } = union_;
         Union {
-            generics: generics.into_tcx(tcx),
+            generics: generics.into_json(renderer),
             has_stripped_fields,
-            fields: ids(fields, tcx),
+            fields: renderer.ids(fields),
             impls: Vec::new(), // Added in JsonRenderer::item
         }
     }
@@ -444,51 +440,51 @@ fn convert_lifetime(l: clean::Lifetime) -> String {
     l.0.to_string()
 }
 
-impl FromWithTcx<clean::Generics> for Generics {
-    fn from_tcx(generics: clean::Generics, tcx: TyCtxt<'_>) -> Self {
+impl FromClean<clean::Generics> for Generics {
+    fn from_clean(generics: clean::Generics, renderer: &JsonRenderer<'_>) -> Self {
         Generics {
-            params: generics.params.into_tcx(tcx),
-            where_predicates: generics.where_predicates.into_tcx(tcx),
+            params: generics.params.into_json(renderer),
+            where_predicates: generics.where_predicates.into_json(renderer),
         }
     }
 }
 
-impl FromWithTcx<clean::GenericParamDef> for GenericParamDef {
-    fn from_tcx(generic_param: clean::GenericParamDef, tcx: TyCtxt<'_>) -> Self {
+impl FromClean<clean::GenericParamDef> for GenericParamDef {
+    fn from_clean(generic_param: clean::GenericParamDef, renderer: &JsonRenderer<'_>) -> Self {
         GenericParamDef {
             name: generic_param.name.to_string(),
-            kind: generic_param.kind.into_tcx(tcx),
+            kind: generic_param.kind.into_json(renderer),
         }
     }
 }
 
-impl FromWithTcx<clean::GenericParamDefKind> for GenericParamDefKind {
-    fn from_tcx(kind: clean::GenericParamDefKind, tcx: TyCtxt<'_>) -> Self {
+impl FromClean<clean::GenericParamDefKind> for GenericParamDefKind {
+    fn from_clean(kind: clean::GenericParamDefKind, renderer: &JsonRenderer<'_>) -> Self {
         use clean::GenericParamDefKind::*;
         match kind {
             Lifetime { outlives } => GenericParamDefKind::Lifetime {
                 outlives: outlives.into_iter().map(convert_lifetime).collect(),
             },
             Type { bounds, default, synthetic } => GenericParamDefKind::Type {
-                bounds: bounds.into_tcx(tcx),
-                default: default.map(|x| (*x).into_tcx(tcx)),
+                bounds: bounds.into_json(renderer),
+                default: default.map(|x| (*x).into_json(renderer)),
                 is_synthetic: synthetic,
             },
             Const { ty, default, synthetic: _ } => GenericParamDefKind::Const {
-                type_: (*ty).into_tcx(tcx),
+                type_: (*ty).into_json(renderer),
                 default: default.map(|x| *x),
             },
         }
     }
 }
 
-impl FromWithTcx<clean::WherePredicate> for WherePredicate {
-    fn from_tcx(predicate: clean::WherePredicate, tcx: TyCtxt<'_>) -> Self {
+impl FromClean<clean::WherePredicate> for WherePredicate {
+    fn from_clean(predicate: clean::WherePredicate, renderer: &JsonRenderer<'_>) -> Self {
         use clean::WherePredicate::*;
         match predicate {
             BoundPredicate { ty, bounds, bound_params } => WherePredicate::BoundPredicate {
-                type_: ty.into_tcx(tcx),
-                bounds: bounds.into_tcx(tcx),
+                type_: ty.into_json(renderer),
+                bounds: bounds.into_json(renderer),
                 generic_params: bound_params
                     .into_iter()
                     .map(|x| {
@@ -503,15 +499,15 @@ impl FromWithTcx<clean::WherePredicate> for WherePredicate {
                                 GenericParamDefKind::Type {
                                     bounds: bounds
                                         .into_iter()
-                                        .map(|bound| bound.into_tcx(tcx))
+                                        .map(|bound| bound.into_json(renderer))
                                         .collect(),
-                                    default: default.map(|ty| (*ty).into_tcx(tcx)),
+                                    default: default.map(|ty| (*ty).into_json(renderer)),
                                     is_synthetic: synthetic,
                                 }
                             }
                             clean::GenericParamDefKind::Const { ty, default, synthetic: _ } => {
                                 GenericParamDefKind::Const {
-                                    type_: (*ty).into_tcx(tcx),
+                                    type_: (*ty).into_json(renderer),
                                     default: default.map(|d| *d),
                                 }
                             }
@@ -530,21 +526,22 @@ impl FromWithTcx<clean::WherePredicate> for WherePredicate {
                     })
                     .collect(),
             },
-            EqPredicate { lhs, rhs } => {
-                WherePredicate::EqPredicate { lhs: lhs.into_tcx(tcx), rhs: rhs.into_tcx(tcx) }
-            }
+            EqPredicate { lhs, rhs } => WherePredicate::EqPredicate {
+                lhs: lhs.into_json(renderer),
+                rhs: rhs.into_json(renderer),
+            },
         }
     }
 }
 
-impl FromWithTcx<clean::GenericBound> for GenericBound {
-    fn from_tcx(bound: clean::GenericBound, tcx: TyCtxt<'_>) -> Self {
+impl FromClean<clean::GenericBound> for GenericBound {
+    fn from_clean(bound: clean::GenericBound, renderer: &JsonRenderer<'_>) -> Self {
         use clean::GenericBound::*;
         match bound {
             TraitBound(clean::PolyTrait { trait_, generic_params }, modifier) => {
                 GenericBound::TraitBound {
-                    trait_: trait_.into_tcx(tcx),
-                    generic_params: generic_params.into_tcx(tcx),
+                    trait_: trait_.into_json(renderer),
+                    generic_params: generic_params.into_json(renderer),
                     modifier: from_trait_bound_modifier(modifier),
                 }
             }
@@ -572,73 +569,75 @@ pub(crate) fn from_trait_bound_modifier(
     }
 }
 
-impl FromWithTcx<clean::Type> for Type {
-    fn from_tcx(ty: clean::Type, tcx: TyCtxt<'_>) -> Self {
+impl FromClean<clean::Type> for Type {
+    fn from_clean(ty: clean::Type, renderer: &JsonRenderer<'_>) -> Self {
         use clean::Type::{
             Array, BareFunction, BorrowedRef, Generic, ImplTrait, Infer, Primitive, QPath,
             RawPointer, SelfTy, Slice, Tuple,
         };
 
         match ty {
-            clean::Type::Path { path } => Type::ResolvedPath(path.into_tcx(tcx)),
+            clean::Type::Path { path } => Type::ResolvedPath(path.into_json(renderer)),
             clean::Type::DynTrait(bounds, lt) => Type::DynTrait(DynTrait {
                 lifetime: lt.map(convert_lifetime),
-                traits: bounds.into_tcx(tcx),
+                traits: bounds.into_json(renderer),
             }),
             Generic(s) => Type::Generic(s.to_string()),
             // FIXME: add dedicated variant to json Type?
             SelfTy => Type::Generic("Self".to_owned()),
             Primitive(p) => Type::Primitive(p.as_sym().to_string()),
-            BareFunction(f) => Type::FunctionPointer(Box::new((*f).into_tcx(tcx))),
-            Tuple(t) => Type::Tuple(t.into_tcx(tcx)),
-            Slice(t) => Type::Slice(Box::new((*t).into_tcx(tcx))),
-            Array(t, s) => Type::Array { type_: Box::new((*t).into_tcx(tcx)), len: s.to_string() },
+            BareFunction(f) => Type::FunctionPointer(Box::new((*f).into_json(renderer))),
+            Tuple(t) => Type::Tuple(t.into_json(renderer)),
+            Slice(t) => Type::Slice(Box::new((*t).into_json(renderer))),
+            Array(t, s) => {
+                Type::Array { type_: Box::new((*t).into_json(renderer)), len: s.to_string() }
+            }
             clean::Type::Pat(t, p) => Type::Pat {
-                type_: Box::new((*t).into_tcx(tcx)),
+                type_: Box::new((*t).into_json(renderer)),
                 __pat_unstable_do_not_use: p.to_string(),
             },
-            ImplTrait(g) => Type::ImplTrait(g.into_tcx(tcx)),
+            ImplTrait(g) => Type::ImplTrait(g.into_json(renderer)),
             Infer => Type::Infer,
             RawPointer(mutability, type_) => Type::RawPointer {
                 is_mutable: mutability == ast::Mutability::Mut,
-                type_: Box::new((*type_).into_tcx(tcx)),
+                type_: Box::new((*type_).into_json(renderer)),
             },
             BorrowedRef { lifetime, mutability, type_ } => Type::BorrowedRef {
                 lifetime: lifetime.map(convert_lifetime),
                 is_mutable: mutability == ast::Mutability::Mut,
-                type_: Box::new((*type_).into_tcx(tcx)),
+                type_: Box::new((*type_).into_json(renderer)),
             },
             QPath(box clean::QPathData { assoc, self_type, trait_, .. }) => Type::QualifiedPath {
                 name: assoc.name.to_string(),
-                args: Box::new(assoc.args.into_tcx(tcx)),
-                self_type: Box::new(self_type.into_tcx(tcx)),
-                trait_: trait_.map(|trait_| trait_.into_tcx(tcx)),
+                args: Box::new(assoc.args.into_json(renderer)),
+                self_type: Box::new(self_type.into_json(renderer)),
+                trait_: trait_.map(|trait_| trait_.into_json(renderer)),
             },
         }
     }
 }
 
-impl FromWithTcx<clean::Path> for Path {
-    fn from_tcx(path: clean::Path, tcx: TyCtxt<'_>) -> Path {
+impl FromClean<clean::Path> for Path {
+    fn from_clean(path: clean::Path, renderer: &JsonRenderer<'_>) -> Path {
         Path {
             name: path.whole_name(),
-            id: id_from_item_default(path.def_id().into(), tcx),
-            args: path.segments.last().map(|args| Box::new(args.clone().args.into_tcx(tcx))),
+            id: renderer.id_from_item_default(path.def_id().into()),
+            args: path.segments.last().map(|args| Box::new(args.clone().args.into_json(renderer))),
         }
     }
 }
 
-impl FromWithTcx<clean::Term> for Term {
-    fn from_tcx(term: clean::Term, tcx: TyCtxt<'_>) -> Term {
+impl FromClean<clean::Term> for Term {
+    fn from_clean(term: clean::Term, renderer: &JsonRenderer<'_>) -> Term {
         match term {
-            clean::Term::Type(ty) => Term::Type(FromWithTcx::from_tcx(ty, tcx)),
-            clean::Term::Constant(c) => Term::Constant(FromWithTcx::from_tcx(c, tcx)),
+            clean::Term::Type(ty) => Term::Type(ty.into_json(renderer)),
+            clean::Term::Constant(c) => Term::Constant(c.into_json(renderer)),
         }
     }
 }
 
-impl FromWithTcx<clean::BareFunctionDecl> for FunctionPointer {
-    fn from_tcx(bare_decl: clean::BareFunctionDecl, tcx: TyCtxt<'_>) -> Self {
+impl FromClean<clean::BareFunctionDecl> for FunctionPointer {
+    fn from_clean(bare_decl: clean::BareFunctionDecl, renderer: &JsonRenderer<'_>) -> Self {
         let clean::BareFunctionDecl { safety, generic_params, decl, abi } = bare_decl;
         FunctionPointer {
             header: FunctionHeader {
@@ -647,29 +646,30 @@ impl FromWithTcx<clean::BareFunctionDecl> for FunctionPointer {
                 is_async: false,
                 abi: convert_abi(abi),
             },
-            generic_params: generic_params.into_tcx(tcx),
-            sig: decl.into_tcx(tcx),
+            generic_params: generic_params.into_json(renderer),
+            sig: decl.into_json(renderer),
         }
     }
 }
 
-impl FromWithTcx<clean::FnDecl> for FunctionSignature {
-    fn from_tcx(decl: clean::FnDecl, tcx: TyCtxt<'_>) -> Self {
+impl FromClean<clean::FnDecl> for FunctionSignature {
+    fn from_clean(decl: clean::FnDecl, renderer: &JsonRenderer<'_>) -> Self {
         let clean::FnDecl { inputs, output, c_variadic } = decl;
         FunctionSignature {
             inputs: inputs
                 .values
                 .into_iter()
-                .map(|arg| (arg.name.to_string(), arg.type_.into_tcx(tcx)))
+                .map(|arg| (arg.name.to_string(), arg.type_.into_json(renderer)))
                 .collect(),
-            output: if output.is_unit() { None } else { Some(output.into_tcx(tcx)) },
+            output: if output.is_unit() { None } else { Some(output.into_json(renderer)) },
             is_c_variadic: c_variadic,
         }
     }
 }
 
-impl FromWithTcx<clean::Trait> for Trait {
-    fn from_tcx(trait_: clean::Trait, tcx: TyCtxt<'_>) -> Self {
+impl FromClean<clean::Trait> for Trait {
+    fn from_clean(trait_: clean::Trait, renderer: &JsonRenderer<'_>) -> Self {
+        let tcx = renderer.tcx;
         let is_auto = trait_.is_auto(tcx);
         let is_unsafe = trait_.safety(tcx) == rustc_hir::Safety::Unsafe;
         let is_object_safe = trait_.is_object_safe(tcx);
@@ -678,26 +678,29 @@ impl FromWithTcx<clean::Trait> for Trait {
             is_auto,
             is_unsafe,
             is_object_safe,
-            items: ids(items, tcx),
-            generics: generics.into_tcx(tcx),
-            bounds: bounds.into_tcx(tcx),
+            items: renderer.ids(items),
+            generics: generics.into_json(renderer),
+            bounds: bounds.into_json(renderer),
             implementations: Vec::new(), // Added in JsonRenderer::item
         }
     }
 }
 
-impl FromWithTcx<clean::PolyTrait> for PolyTrait {
-    fn from_tcx(
+impl FromClean<clean::PolyTrait> for PolyTrait {
+    fn from_clean(
         clean::PolyTrait { trait_, generic_params }: clean::PolyTrait,
-        tcx: TyCtxt<'_>,
+        renderer: &JsonRenderer<'_>,
     ) -> Self {
-        PolyTrait { trait_: trait_.into_tcx(tcx), generic_params: generic_params.into_tcx(tcx) }
+        PolyTrait {
+            trait_: trait_.into_json(renderer),
+            generic_params: generic_params.into_json(renderer),
+        }
     }
 }
 
-impl FromWithTcx<clean::Impl> for Impl {
-    fn from_tcx(impl_: clean::Impl, tcx: TyCtxt<'_>) -> Self {
-        let provided_trait_methods = impl_.provided_trait_methods(tcx);
+impl FromClean<clean::Impl> for Impl {
+    fn from_clean(impl_: clean::Impl, renderer: &JsonRenderer<'_>) -> Self {
+        let provided_trait_methods = impl_.provided_trait_methods(renderer.tcx);
         let clean::Impl { safety, generics, trait_, for_, items, polarity, kind } = impl_;
         // FIXME: use something like ImplKind in JSON?
         let (is_synthetic, blanket_impl) = match kind {
@@ -711,17 +714,17 @@ impl FromWithTcx<clean::Impl> for Impl {
         };
         Impl {
             is_unsafe: safety == rustc_hir::Safety::Unsafe,
-            generics: generics.into_tcx(tcx),
+            generics: generics.into_json(renderer),
             provided_trait_methods: provided_trait_methods
                 .into_iter()
                 .map(|x| x.to_string())
                 .collect(),
-            trait_: trait_.map(|path| path.into_tcx(tcx)),
-            for_: for_.into_tcx(tcx),
-            items: ids(items, tcx),
+            trait_: trait_.map(|path| path.into_json(renderer)),
+            for_: for_.into_json(renderer),
+            items: renderer.ids(items),
             is_negative,
             is_synthetic,
-            blanket_impl: blanket_impl.map(|x| x.into_tcx(tcx)),
+            blanket_impl: blanket_impl.map(|x| x.into_json(renderer)),
         }
     }
 }
@@ -730,42 +733,42 @@ pub(crate) fn from_function(
     function: Box<clean::Function>,
     has_body: bool,
     header: rustc_hir::FnHeader,
-    tcx: TyCtxt<'_>,
+    renderer: &JsonRenderer<'_>,
 ) -> Function {
     let clean::Function { decl, generics } = *function;
     Function {
-        sig: decl.into_tcx(tcx),
-        generics: generics.into_tcx(tcx),
+        sig: decl.into_json(renderer),
+        generics: generics.into_json(renderer),
         header: from_fn_header(&header),
         has_body,
     }
 }
 
-impl FromWithTcx<clean::Enum> for Enum {
-    fn from_tcx(enum_: clean::Enum, tcx: TyCtxt<'_>) -> Self {
+impl FromClean<clean::Enum> for Enum {
+    fn from_clean(enum_: clean::Enum, renderer: &JsonRenderer<'_>) -> Self {
         let has_stripped_variants = enum_.has_stripped_entries();
         let clean::Enum { variants, generics } = enum_;
         Enum {
-            generics: generics.into_tcx(tcx),
+            generics: generics.into_json(renderer),
             has_stripped_variants,
-            variants: ids(variants, tcx),
+            variants: renderer.ids(variants),
             impls: Vec::new(), // Added in JsonRenderer::item
         }
     }
 }
 
-impl FromWithTcx<clean::Variant> for Variant {
-    fn from_tcx(variant: clean::Variant, tcx: TyCtxt<'_>) -> Self {
+impl FromClean<clean::Variant> for Variant {
+    fn from_clean(variant: clean::Variant, renderer: &JsonRenderer<'_>) -> Self {
         use clean::VariantKind::*;
 
-        let discriminant = variant.discriminant.map(|d| d.into_tcx(tcx));
+        let discriminant = variant.discriminant.map(|d| d.into_json(renderer));
 
         let kind = match variant.kind {
             CLike => VariantKind::Plain,
-            Tuple(fields) => VariantKind::Tuple(ids_keeping_stripped(fields, tcx)),
+            Tuple(fields) => VariantKind::Tuple(renderer.ids_keeping_stripped(fields)),
             Struct(s) => VariantKind::Struct {
                 has_stripped_fields: s.has_stripped_entries(),
-                fields: ids(s.fields, tcx),
+                fields: renderer.ids(s.fields),
             },
         };
 
@@ -773,8 +776,9 @@ impl FromWithTcx<clean::Variant> for Variant {
     }
 }
 
-impl FromWithTcx<clean::Discriminant> for Discriminant {
-    fn from_tcx(disr: clean::Discriminant, tcx: TyCtxt<'_>) -> Self {
+impl FromClean<clean::Discriminant> for Discriminant {
+    fn from_clean(disr: clean::Discriminant, renderer: &JsonRenderer<'_>) -> Self {
+        let tcx = renderer.tcx;
         Discriminant {
             // expr is only none if going through the inlining path, which gets
             // `rustc_middle` types, not `rustc_hir`, but because JSON never inlines
@@ -785,8 +789,8 @@ impl FromWithTcx<clean::Discriminant> for Discriminant {
     }
 }
 
-impl FromWithTcx<clean::Import> for Use {
-    fn from_tcx(import: clean::Import, tcx: TyCtxt<'_>) -> Self {
+impl FromClean<clean::Import> for Use {
+    fn from_clean(import: clean::Import, renderer: &JsonRenderer<'_>) -> Self {
         use clean::ImportKind::*;
         let (name, is_glob) = match import.kind {
             Simple(s) => (s.to_string(), false),
@@ -798,14 +802,14 @@ impl FromWithTcx<clean::Import> for Use {
         Use {
             source: import.source.path.whole_name(),
             name,
-            id: import.source.did.map(ItemId::from).map(|i| id_from_item_default(i, tcx)),
+            id: import.source.did.map(ItemId::from).map(|i| renderer.id_from_item_default(i)),
             is_glob,
         }
     }
 }
 
-impl FromWithTcx<clean::ProcMacro> for ProcMacro {
-    fn from_tcx(mac: clean::ProcMacro, _tcx: TyCtxt<'_>) -> Self {
+impl FromClean<clean::ProcMacro> for ProcMacro {
+    fn from_clean(mac: clean::ProcMacro, _renderer: &JsonRenderer<'_>) -> Self {
         ProcMacro {
             kind: from_macro_kind(mac.kind),
             helpers: mac.helpers.iter().map(|x| x.to_string()).collect(),
@@ -822,17 +826,18 @@ pub(crate) fn from_macro_kind(kind: rustc_span::hygiene::MacroKind) -> MacroKind
     }
 }
 
-impl FromWithTcx<Box<clean::TypeAlias>> for TypeAlias {
-    fn from_tcx(type_alias: Box<clean::TypeAlias>, tcx: TyCtxt<'_>) -> Self {
+impl FromClean<Box<clean::TypeAlias>> for TypeAlias {
+    fn from_clean(type_alias: Box<clean::TypeAlias>, renderer: &JsonRenderer<'_>) -> Self {
         let clean::TypeAlias { type_, generics, item_type: _, inner_type: _ } = *type_alias;
-        TypeAlias { type_: type_.into_tcx(tcx), generics: generics.into_tcx(tcx) }
+        TypeAlias { type_: type_.into_json(renderer), generics: generics.into_json(renderer) }
     }
 }
 
-impl FromWithTcx<clean::Static> for Static {
-    fn from_tcx(stat: clean::Static, tcx: TyCtxt<'_>) -> Self {
+impl FromClean<clean::Static> for Static {
+    fn from_clean(stat: clean::Static, renderer: &JsonRenderer<'_>) -> Self {
+        let tcx = renderer.tcx;
         Static {
-            type_: (*stat.type_).into_tcx(tcx),
+            type_: (*stat.type_).into_json(renderer),
             is_mutable: stat.mutability == ast::Mutability::Mut,
             expr: stat
                 .expr
@@ -842,14 +847,17 @@ impl FromWithTcx<clean::Static> for Static {
     }
 }
 
-impl FromWithTcx<clean::TraitAlias> for TraitAlias {
-    fn from_tcx(alias: clean::TraitAlias, tcx: TyCtxt<'_>) -> Self {
-        TraitAlias { generics: alias.generics.into_tcx(tcx), params: alias.bounds.into_tcx(tcx) }
+impl FromClean<clean::TraitAlias> for TraitAlias {
+    fn from_clean(alias: clean::TraitAlias, renderer: &JsonRenderer<'_>) -> Self {
+        TraitAlias {
+            generics: alias.generics.into_json(renderer),
+            params: alias.bounds.into_json(renderer),
+        }
     }
 }
 
-impl FromWithTcx<ItemType> for ItemKind {
-    fn from_tcx(kind: ItemType, _tcx: TyCtxt<'_>) -> Self {
+impl FromClean<ItemType> for ItemKind {
+    fn from_clean(kind: ItemType, _renderer: &JsonRenderer<'_>) -> Self {
         use ItemType::*;
         match kind {
             Module => ItemKind::Module,
@@ -878,25 +886,3 @@ impl FromWithTcx<ItemType> for ItemKind {
         }
     }
 }
-
-fn ids(items: impl IntoIterator<Item = clean::Item>, tcx: TyCtxt<'_>) -> Vec<Id> {
-    items
-        .into_iter()
-        .filter(|x| !x.is_stripped() && !x.is_keyword())
-        .map(|i| id_from_item(&i, tcx))
-        .collect()
-}
-
-fn ids_keeping_stripped(
-    items: impl IntoIterator<Item = clean::Item>,
-    tcx: TyCtxt<'_>,
-) -> Vec<Option<Id>> {
-    items
-        .into_iter()
-        .map(
-            |i| {
-                if !i.is_stripped() && !i.is_keyword() { Some(id_from_item(&i, tcx)) } else { None }
-            },
-        )
-        .collect()
-}
diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs
index b7a683eed1c..df97c5ea263 100644
--- a/src/librustdoc/json/mod.rs
+++ b/src/librustdoc/json/mod.rs
@@ -16,6 +16,7 @@ use std::rc::Rc;
 use rustc_hir::def_id::{DefId, DefIdSet};
 use rustc_middle::ty::TyCtxt;
 use rustc_session::Session;
+use rustc_span::Symbol;
 use rustc_span::def_id::LOCAL_CRATE;
 use rustdoc_json_types as types;
 // It's important to use the FxHashMap from rustdoc_json_types here, instead of
@@ -31,9 +32,17 @@ use crate::docfs::PathError;
 use crate::error::Error;
 use crate::formats::FormatRenderer;
 use crate::formats::cache::Cache;
-use crate::json::conversions::{IntoWithTcx, id_from_item, id_from_item_default};
+use crate::json::conversions::IntoJson;
 use crate::{clean, try_err};
 
+#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
+struct FullItemId {
+    def_id: DefId,
+    name: Option<Symbol>,
+    /// Used to distinguish imports of different items with the same name
+    extra: Option<types::Id>,
+}
+
 #[derive(Clone)]
 pub(crate) struct JsonRenderer<'tcx> {
     tcx: TyCtxt<'tcx>,
@@ -46,6 +55,7 @@ pub(crate) struct JsonRenderer<'tcx> {
     out_dir: Option<PathBuf>,
     cache: Rc<Cache>,
     imported_items: DefIdSet,
+    id_interner: Rc<RefCell<FxHashMap<(FullItemId, Option<FullItemId>), types::Id>>>,
 }
 
 impl<'tcx> JsonRenderer<'tcx> {
@@ -63,7 +73,7 @@ impl<'tcx> JsonRenderer<'tcx> {
                     .map(|i| {
                         let item = &i.impl_item;
                         self.item(item.clone()).unwrap();
-                        id_from_item(&item, self.tcx)
+                        self.id_from_item(&item)
                     })
                     .collect()
             })
@@ -94,7 +104,7 @@ impl<'tcx> JsonRenderer<'tcx> {
 
                         if item.item_id.is_local() || is_primitive_impl {
                             self.item(item.clone()).unwrap();
-                            Some(id_from_item(&item, self.tcx))
+                            Some(self.id_from_item(&item))
                         } else {
                             None
                         }
@@ -145,6 +155,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
                 out_dir: if options.output_to_stdout { None } else { Some(options.output) },
                 cache: Rc::new(cache),
                 imported_items,
+                id_interner: Default::default(),
             },
             krate,
         ))
@@ -243,7 +254,7 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
 
         debug!("Constructing Output");
         let output_crate = types::Crate {
-            root: types::Id(format!("0:0:{}", e.name(self.tcx).as_u32())),
+            root: self.id_from_item_default(e.def_id().into()),
             crate_version: self.cache.crate_version.clone(),
             includes_private: self.cache.document_private,
             index,
@@ -253,10 +264,10 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
                 .iter()
                 .chain(&self.cache.external_paths)
                 .map(|(&k, &(ref path, kind))| {
-                    (id_from_item_default(k.into(), self.tcx), types::ItemSummary {
+                    (self.id_from_item_default(k.into()), types::ItemSummary {
                         crate_id: k.krate.as_u32(),
                         path: path.iter().map(|s| s.to_string()).collect(),
-                        kind: kind.into_tcx(self.tcx),
+                        kind: kind.into_json(self),
                     })
                 })
                 .collect(),
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index 2be415e2e0e..10fc0ea9990 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -20,7 +20,6 @@
 #![warn(rustc::internal)]
 #![allow(clippy::collapsible_if, clippy::collapsible_else_if)]
 #![allow(rustc::diagnostic_outside_of_impl)]
-#![allow(rustc::potential_query_instability)]
 #![allow(rustc::untranslatable_diagnostic)]
 
 extern crate thin_vec;
@@ -99,7 +98,7 @@ use crate::clean::utils::DOC_RUST_LANG_ORG_CHANNEL;
 /// Commas between elements are required (even if the expression is a block).
 macro_rules! map {
     ($( $key: expr => $val: expr ),* $(,)*) => {{
-        let mut map = ::rustc_data_structures::fx::FxHashMap::default();
+        let mut map = ::rustc_data_structures::fx::FxIndexMap::default();
         $( map.insert($key, $val); )*
         map
     }}
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index 0dba16cbaf3..db235786cf4 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -9,7 +9,7 @@ use std::ops::Range;
 
 use pulldown_cmark::LinkType;
 use rustc_ast::util::comments::may_have_doc_links;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
 use rustc_data_structures::intern::Interned;
 use rustc_errors::{Applicability, Diag, DiagMessage};
 use rustc_hir::def::Namespace::*;
@@ -778,9 +778,9 @@ fn trait_impls_for<'a>(
     cx: &mut DocContext<'a>,
     ty: Ty<'a>,
     module: DefId,
-) -> FxHashSet<(DefId, DefId)> {
+) -> FxIndexSet<(DefId, DefId)> {
     let tcx = cx.tcx;
-    let mut impls = FxHashSet::default();
+    let mut impls = FxIndexSet::default();
 
     for &trait_ in tcx.doc_link_traits_in_scope(module) {
         tcx.for_each_relevant_impl(trait_, ty, |impl_| {
@@ -1040,21 +1040,6 @@ impl LinkCollector<'_, '_> {
             false,
         )?;
 
-        if ori_link.display_text.is_some() {
-            self.resolve_display_text(
-                path_str,
-                ResolutionInfo {
-                    item_id,
-                    module_id,
-                    dis: disambiguator,
-                    path_str: ori_link.display_text.clone()?.into_boxed_str(),
-                    extra_fragment: extra_fragment.clone(),
-                },
-                &ori_link,
-                &diag_info,
-            );
-        }
-
         // Check for a primitive which might conflict with a module
         // Report the ambiguity and require that the user specify which one they meant.
         // FIXME: could there ever be a primitive not in the type namespace?
@@ -1088,7 +1073,7 @@ impl LinkCollector<'_, '_> {
                     // valid omission. See https://github.com/rust-lang/rust/pull/80660#discussion_r551585677
                     // for discussion on the matter.
                     let kind = self.cx.tcx.def_kind(id);
-                    self.verify_disambiguator(path_str, kind, id, disambiguator, item, &diag_info)?;
+                    self.verify_disambiguator(path_str, kind, id, disambiguator, &diag_info)?;
                 } else {
                     match disambiguator {
                         Some(Disambiguator::Primitive | Disambiguator::Namespace(_)) | None => {}
@@ -1117,7 +1102,6 @@ impl LinkCollector<'_, '_> {
                     kind_for_dis,
                     id_for_dis,
                     disambiguator,
-                    item,
                     &diag_info,
                 )?;
 
@@ -1138,7 +1122,6 @@ impl LinkCollector<'_, '_> {
         kind: DefKind,
         id: DefId,
         disambiguator: Option<Disambiguator>,
-        item: &Item,
         diag_info: &DiagnosticInfo<'_>,
     ) -> Option<()> {
         debug!("intra-doc link to {path_str} resolved to {:?}", (kind, id));
@@ -1165,7 +1148,7 @@ impl LinkCollector<'_, '_> {
 
         // item can be non-local e.g. when using `#[rustc_doc_primitive = "pointer"]`
         if let Some((src_id, dst_id)) = id.as_local().and_then(|dst_id| {
-            item.item_id.expect_def_id().as_local().map(|src_id| (src_id, dst_id))
+            diag_info.item.item_id.expect_def_id().as_local().map(|src_id| (src_id, dst_id))
         }) {
             if self.cx.tcx.effective_visibilities(()).is_exported(src_id)
                 && !self.cx.tcx.effective_visibilities(()).is_exported(dst_id)
@@ -1398,58 +1381,6 @@ impl LinkCollector<'_, '_> {
             }
         }
     }
-
-    /// Resolve display text if the provided link has separated parts of links.
-    ///
-    /// For example:
-    /// Inline link `[display_text](dest_link)` and reference link `[display_text][reference_link]` has
-    /// separated parts of links.
-    fn resolve_display_text(
-        &mut self,
-        explicit_link: &Box<str>,
-        display_res_info: ResolutionInfo,
-        ori_link: &MarkdownLink,
-        diag_info: &DiagnosticInfo<'_>,
-    ) {
-        // Check if explicit resolution's path is same as resolution of original link's display text path, see
-        // tests/rustdoc-ui/lint/redundant_explicit_links.rs for more cases.
-        //
-        // To avoid disambiguator from panicking, we check if display text path is possible to be disambiguated
-        // into explicit path.
-        if !matches!(
-            ori_link.kind,
-            LinkType::Inline | LinkType::Reference | LinkType::ReferenceUnknown
-        ) {
-            return;
-        }
-
-        // Algorithm to check if display text could possibly be the explicit link:
-        //
-        // Consider 2 links which are display text and explicit link, pick the shorter
-        // one as symbol and longer one as full qualified path, and tries to match symbol
-        // to the full qualified path's last symbol.
-        //
-        // Otherwise, check if 2 links are same, if so, skip the resolve process.
-        //
-        // Notice that this algorithm is passive, might possibly miss actual redundant cases.
-        let explicit_link = explicit_link.to_string();
-        let display_text = ori_link.display_text.as_ref().unwrap();
-
-        if display_text.len() == explicit_link.len() {
-            // Whether they are same or not, skip the resolve process.
-            return;
-        }
-
-        if explicit_link.ends_with(&display_text[..]) || display_text.ends_with(&explicit_link[..])
-        {
-            self.resolve_with_disambiguator_cached(
-                display_res_info,
-                diag_info.clone(), // this struct should really be Copy, but Range is not :(
-                false,
-                true,
-            );
-        }
-    }
 }
 
 /// Get the section of a link between the backticks,
@@ -1996,11 +1927,22 @@ fn resolution_failure(
                             &diag_info,
                         );
 
-                        format!(
-                            "this link resolves to {}, which is not in the {} namespace",
-                            item(res),
-                            expected_ns.descr()
-                        )
+                        if let Some(disambiguator) = disambiguator
+                            && !matches!(disambiguator, Disambiguator::Namespace(..))
+                        {
+                            format!(
+                                "this link resolves to {}, which is not {} {}",
+                                item(res),
+                                disambiguator.article(),
+                                disambiguator.descr()
+                            )
+                        } else {
+                            format!(
+                                "this link resolves to {}, which is not in the {} namespace",
+                                item(res),
+                                expected_ns.descr()
+                            )
+                        }
                     }
                 };
                 if let Some(span) = sp {
diff --git a/src/librustdoc/passes/collect_trait_impls.rs b/src/librustdoc/passes/collect_trait_impls.rs
index b307e84e42e..d1a1f0df3e7 100644
--- a/src/librustdoc/passes/collect_trait_impls.rs
+++ b/src/librustdoc/passes/collect_trait_impls.rs
@@ -219,7 +219,7 @@ pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) ->
         panic!("collect-trait-impls can't run");
     };
 
-    krate.external_traits.extend(cx.external_traits.drain());
+    krate.external_traits.extend(cx.external_traits.drain(..));
 
     krate
 }
diff --git a/src/librustdoc/passes/lint/check_code_block_syntax.rs b/src/librustdoc/passes/lint/check_code_block_syntax.rs
index 848e70a7bdb..e0dc5b4c513 100644
--- a/src/librustdoc/passes/lint/check_code_block_syntax.rs
+++ b/src/librustdoc/passes/lint/check_code_block_syntax.rs
@@ -145,7 +145,7 @@ struct BufferEmitter {
 }
 
 impl Translate for BufferEmitter {
-    fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> {
+    fn fluent_bundle(&self) -> Option<&rustc_errors::FluentBundle> {
         None
     }
 
@@ -169,7 +169,7 @@ impl Emitter for BufferEmitter {
         }
     }
 
-    fn source_map(&self) -> Option<&Lrc<SourceMap>> {
+    fn source_map(&self) -> Option<&SourceMap> {
         None
     }
 }
diff --git a/src/librustdoc/scrape_examples.rs b/src/librustdoc/scrape_examples.rs
index 9d4d203562c..a59c43bfbf9 100644
--- a/src/librustdoc/scrape_examples.rs
+++ b/src/librustdoc/scrape_examples.rs
@@ -3,7 +3,7 @@
 use std::fs;
 use std::path::PathBuf;
 
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::FxIndexMap;
 use rustc_errors::DiagCtxtHandle;
 use rustc_hir::intravisit::{self, Visitor};
 use rustc_hir::{self as hir};
@@ -102,8 +102,8 @@ pub(crate) struct CallData {
     pub(crate) is_bin: bool,
 }
 
-pub(crate) type FnCallLocations = FxHashMap<PathBuf, CallData>;
-pub(crate) type AllCallLocations = FxHashMap<DefPathHash, FnCallLocations>;
+pub(crate) type FnCallLocations = FxIndexMap<PathBuf, CallData>;
+pub(crate) type AllCallLocations = FxIndexMap<DefPathHash, FnCallLocations>;
 
 /// Visitor for traversing a crate and finding instances of function calls.
 struct FindCalls<'a, 'tcx> {
@@ -293,7 +293,7 @@ pub(crate) fn run(
         debug!("Scrape examples target_crates: {target_crates:?}");
 
         // Run call-finder on all items
-        let mut calls = FxHashMap::default();
+        let mut calls = FxIndexMap::default();
         let mut finder =
             FindCalls { calls: &mut calls, tcx, map: tcx.hir(), cx, target_crates, bin_crate };
         tcx.hir().visit_all_item_likes_in_crate(&mut finder);
@@ -332,7 +332,7 @@ pub(crate) fn load_call_locations(
     with_examples: Vec<String>,
     dcx: DiagCtxtHandle<'_>,
 ) -> AllCallLocations {
-    let mut all_calls: AllCallLocations = FxHashMap::default();
+    let mut all_calls: AllCallLocations = FxIndexMap::default();
     for path in with_examples {
         let bytes = match fs::read(&path) {
             Ok(bytes) => bytes,
diff --git a/src/librustdoc/theme.rs b/src/librustdoc/theme.rs
index 409728a8e86..2c00bb7e132 100644
--- a/src/librustdoc/theme.rs
+++ b/src/librustdoc/theme.rs
@@ -1,10 +1,9 @@
-use std::collections::hash_map::Entry;
 use std::fs;
 use std::iter::Peekable;
 use std::path::Path;
 use std::str::Chars;
 
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::{FxIndexMap, IndexEntry};
 use rustc_errors::DiagCtxtHandle;
 
 #[cfg(test)]
@@ -12,8 +11,8 @@ mod tests;
 
 #[derive(Debug)]
 pub(crate) struct CssPath {
-    pub(crate) rules: FxHashMap<String, String>,
-    pub(crate) children: FxHashMap<String, CssPath>,
+    pub(crate) rules: FxIndexMap<String, String>,
+    pub(crate) children: FxIndexMap<String, CssPath>,
 }
 
 /// When encountering a `"` or a `'`, returns the whole string, including the quote characters.
@@ -120,10 +119,10 @@ fn parse_rules(
     content: &str,
     selector: String,
     iter: &mut Peekable<Chars<'_>>,
-    paths: &mut FxHashMap<String, CssPath>,
+    paths: &mut FxIndexMap<String, CssPath>,
 ) -> Result<(), String> {
-    let mut rules = FxHashMap::default();
-    let mut children = FxHashMap::default();
+    let mut rules = FxIndexMap::default();
+    let mut children = FxIndexMap::default();
 
     loop {
         // If the parent isn't a "normal" CSS selector, we only expect sub-selectors and not CSS
@@ -146,10 +145,10 @@ fn parse_rules(
             return Err(format!("Found empty value for rule `{rule}` in selector `{selector}`"));
         }
         match rules.entry(rule) {
-            Entry::Occupied(mut o) => {
+            IndexEntry::Occupied(mut o) => {
                 *o.get_mut() = value;
             }
-            Entry::Vacant(v) => {
+            IndexEntry::Vacant(v) => {
                 v.insert(value);
             }
         }
@@ -159,7 +158,7 @@ fn parse_rules(
     }
 
     match paths.entry(selector) {
-        Entry::Occupied(mut o) => {
+        IndexEntry::Occupied(mut o) => {
             let v = o.get_mut();
             for (key, value) in rules.into_iter() {
                 v.rules.insert(key, value);
@@ -168,7 +167,7 @@ fn parse_rules(
                 v.children.insert(sel, child);
             }
         }
-        Entry::Vacant(v) => {
+        IndexEntry::Vacant(v) => {
             v.insert(CssPath { rules, children });
         }
     }
@@ -178,7 +177,7 @@ fn parse_rules(
 pub(crate) fn parse_selectors(
     content: &str,
     iter: &mut Peekable<Chars<'_>>,
-    paths: &mut FxHashMap<String, CssPath>,
+    paths: &mut FxIndexMap<String, CssPath>,
 ) -> Result<(), String> {
     let mut selector = String::new();
 
@@ -202,17 +201,17 @@ pub(crate) fn parse_selectors(
 
 /// The entry point to parse the CSS rules. Every time we encounter a `{`, we then parse the rules
 /// inside it.
-pub(crate) fn load_css_paths(content: &str) -> Result<FxHashMap<String, CssPath>, String> {
+pub(crate) fn load_css_paths(content: &str) -> Result<FxIndexMap<String, CssPath>, String> {
     let mut iter = content.chars().peekable();
-    let mut paths = FxHashMap::default();
+    let mut paths = FxIndexMap::default();
 
     parse_selectors(content, &mut iter, &mut paths)?;
     Ok(paths)
 }
 
 pub(crate) fn get_differences(
-    origin: &FxHashMap<String, CssPath>,
-    against: &FxHashMap<String, CssPath>,
+    origin: &FxIndexMap<String, CssPath>,
+    against: &FxIndexMap<String, CssPath>,
     v: &mut Vec<String>,
 ) {
     for (selector, entry) in origin.iter() {
@@ -235,7 +234,7 @@ pub(crate) fn get_differences(
 
 pub(crate) fn test_theme_against<P: AsRef<Path>>(
     f: &P,
-    origin: &FxHashMap<String, CssPath>,
+    origin: &FxIndexMap<String, CssPath>,
     dcx: DiagCtxtHandle<'_>,
 ) -> (bool, Vec<String>) {
     let against = match fs::read_to_string(f)
diff --git a/src/librustdoc/visit_ast.rs b/src/librustdoc/visit_ast.rs
index c44e5ecaba8..f789aca7378 100644
--- a/src/librustdoc/visit_ast.rs
+++ b/src/librustdoc/visit_ast.rs
@@ -164,7 +164,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
                     .unwrap_or(&[])
                     .iter()
                     .filter_map(|attr| {
-                        Cfg::parse(attr.meta_item()?)
+                        Cfg::parse(attr)
                             .map_err(|e| self.cx.sess().dcx().span_err(e.span, e.msg))
                             .ok()
                     })
@@ -505,21 +505,11 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
             | hir::ItemKind::Struct(..)
             | hir::ItemKind::Union(..)
             | hir::ItemKind::TyAlias(..)
-            | hir::ItemKind::OpaqueTy(hir::OpaqueTy {
-                origin: hir::OpaqueTyOrigin::TyAlias { .. },
-                ..
-            })
             | hir::ItemKind::Static(..)
             | hir::ItemKind::Trait(..)
             | hir::ItemKind::TraitAlias(..) => {
                 self.add_to_current_mod(item, renamed, import_id);
             }
-            hir::ItemKind::OpaqueTy(hir::OpaqueTy {
-                origin: hir::OpaqueTyOrigin::AsyncFn { .. } | hir::OpaqueTyOrigin::FnReturn { .. },
-                ..
-            }) => {
-                // return-position impl traits are never nameable, and should never be documented.
-            }
             hir::ItemKind::Const(..) => {
                 // Underscore constants do not correspond to a nameable item and
                 // so are never useful in documentation.
diff --git a/src/llvm-project b/src/llvm-project
-Subproject 56997739365e8132cc817e00899480746c09d7d
+Subproject 3a17f74904a74565c54cfac0d67026362d03869
diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs
index b3707cf6157..fc64bc98bb9 100644
--- a/src/rustdoc-json-types/lib.rs
+++ b/src/rustdoc-json-types/lib.rs
@@ -13,7 +13,7 @@ use serde::{Deserialize, Serialize};
 /// This integer is incremented with every breaking change to the API,
 /// and is returned along with the JSON blob as [`Crate::format_version`].
 /// Consuming code should assert that this value matches the format version(s) that it supports.
-pub const FORMAT_VERSION: u32 = 34;
+pub const FORMAT_VERSION: u32 = 35;
 
 /// The root of the emitted JSON blob.
 ///
@@ -296,9 +296,9 @@ pub enum AssocItemConstraintKind {
 /// Rustdoc makes no guarantees about the inner value of Id's. Applications
 /// should treat them as opaque keys to lookup items, and avoid attempting
 /// to parse them, or otherwise depend on any implementation details.
-#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 // FIXME(aDotInTheVoid): Consider making this non-public in rustdoc-types.
-pub struct Id(pub String);
+pub struct Id(pub u32);
 
 /// The fundamental kind of an item. Unlike [`ItemEnum`], this does not carry any aditional info.
 ///
@@ -677,7 +677,7 @@ pub struct FunctionHeader {
 /// on unwinding for more info.
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub enum Abi {
-    // We only have a concrete listing here for stable ABI's because their are so many
+    // We only have a concrete listing here for stable ABI's because there are so many
     // See rustc_ast_passes::feature_gate::PostExpansionVisitor::check_abi for the list
     /// The default ABI, but that can also be written explicitly with `extern "Rust"`.
     Rust,
diff --git a/src/tools/build_helper/src/git.rs b/src/tools/build_helper/src/git.rs
index 10c5476cd8f..2aad5650fa8 100644
--- a/src/tools/build_helper/src/git.rs
+++ b/src/tools/build_helper/src/git.rs
@@ -1,6 +1,8 @@
 use std::path::{Path, PathBuf};
 use std::process::{Command, Stdio};
 
+use crate::ci::CiEnv;
+
 pub struct GitConfig<'a> {
     pub git_repository: &'a str,
     pub nightly_branch: &'a str,
@@ -114,8 +116,8 @@ fn git_upstream_merge_base(
 
 /// Searches for the nearest merge commit in the repository that also exists upstream.
 ///
-/// If it fails to find the upstream remote, it then looks for the most recent commit made
-/// by the merge bot by matching the author's email address with the merge bot's email.
+/// It looks for the most recent commit made by the merge bot by matching the author's email
+/// address with the merge bot's email.
 pub fn get_closest_merge_commit(
     git_dir: Option<&Path>,
     config: &GitConfig<'_>,
@@ -127,7 +129,15 @@ pub fn get_closest_merge_commit(
         git.current_dir(git_dir);
     }
 
-    let merge_base = git_upstream_merge_base(config, git_dir).unwrap_or_else(|_| "HEAD".into());
+    let merge_base = {
+        if CiEnv::is_ci() {
+            git_upstream_merge_base(config, git_dir).unwrap()
+        } else {
+            // For non-CI environments, ignore rust-lang/rust upstream as it usually gets
+            // outdated very quickly.
+            "HEAD".to_string()
+        }
+    };
 
     git.args([
         "rev-list",
@@ -196,67 +206,3 @@ pub fn get_git_untracked_files(
         .collect();
     Ok(Some(files))
 }
-
-/// Print a warning if the branch returned from `updated_master_branch` is old
-///
-/// For certain configurations of git repository, this remote will not be
-/// updated when running `git pull`.
-///
-/// This can result in formatting thousands of files instead of a dozen,
-/// so we should warn the user something is wrong.
-pub fn warn_old_master_branch(config: &GitConfig<'_>, git_dir: &Path) {
-    if crate::ci::CiEnv::is_ci() {
-        // this warning is useless in CI,
-        // and CI probably won't have the right branches anyway.
-        return;
-    }
-    // this will be overwritten by the actual name, if possible
-    let mut updated_master = "the upstream master branch".to_string();
-    match warn_old_master_branch_(config, git_dir, &mut updated_master) {
-        Ok(branch_is_old) => {
-            if !branch_is_old {
-                return;
-            }
-            // otherwise fall through and print the rest of the warning
-        }
-        Err(err) => {
-            eprintln!("warning: unable to check if {updated_master} is old due to error: {err}")
-        }
-    }
-    eprintln!(
-        "warning: {updated_master} is used to determine if files have been modified\n\
-         warning: if it is not updated, this may cause files to be needlessly reformatted"
-    );
-}
-
-pub fn warn_old_master_branch_(
-    config: &GitConfig<'_>,
-    git_dir: &Path,
-    updated_master: &mut String,
-) -> Result<bool, Box<dyn std::error::Error>> {
-    use std::time::Duration;
-    *updated_master = updated_master_branch(config, Some(git_dir))?;
-    let branch_path = git_dir.join(".git/refs/remotes").join(&updated_master);
-    const WARN_AFTER: Duration = Duration::from_secs(60 * 60 * 24 * 10);
-    let meta = match std::fs::metadata(&branch_path) {
-        Ok(meta) => meta,
-        Err(err) => {
-            let gcd = git_common_dir(&git_dir)?;
-            if branch_path.starts_with(&gcd) {
-                return Err(Box::new(err));
-            }
-            std::fs::metadata(Path::new(&gcd).join("refs/remotes").join(&updated_master))?
-        }
-    };
-    if meta.modified()?.elapsed()? > WARN_AFTER {
-        eprintln!("warning: {updated_master} has not been updated in 10 days");
-        Ok(true)
-    } else {
-        Ok(false)
-    }
-}
-
-fn git_common_dir(dir: &Path) -> Result<String, String> {
-    output_result(Command::new("git").arg("-C").arg(dir).arg("rev-parse").arg("--git-common-dir"))
-        .map(|x| x.trim().to_string())
-}
diff --git a/src/tools/cargo b/src/tools/cargo
-Subproject 80d82ca22abbee5fb7b51fa1abeb1ae34e99e88
+Subproject 15fbd2f607d4defc87053b8b76bf5038f2483cf
diff --git a/src/tools/clippy/clippy_lints/src/attrs/allow_attributes_without_reason.rs b/src/tools/clippy/clippy_lints/src/attrs/allow_attributes_without_reason.rs
index 40959eccd3a..5d4e864b9b0 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/allow_attributes_without_reason.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/allow_attributes_without_reason.rs
@@ -1,15 +1,15 @@
 use super::{ALLOW_ATTRIBUTES_WITHOUT_REASON, Attribute};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_from_proc_macro;
-use rustc_ast::{MetaItemKind, NestedMetaItem};
+use rustc_ast::{MetaItemInner, MetaItemKind};
 use rustc_lint::{LateContext, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_span::sym;
 use rustc_span::symbol::Symbol;
 
-pub(super) fn check<'cx>(cx: &LateContext<'cx>, name: Symbol, items: &[NestedMetaItem], attr: &'cx Attribute) {
+pub(super) fn check<'cx>(cx: &LateContext<'cx>, name: Symbol, items: &[MetaItemInner], attr: &'cx Attribute) {
     // Check if the reason is present
-    if let Some(item) = items.last().and_then(NestedMetaItem::meta_item)
+    if let Some(item) = items.last().and_then(MetaItemInner::meta_item)
         && let MetaItemKind::NameValue(_) = &item.kind
         && item.path == sym::reason
     {
diff --git a/src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs b/src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs
index 508963a20ea..0baf889faa0 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs
@@ -1,12 +1,12 @@
 use super::BLANKET_CLIPPY_RESTRICTION_LINTS;
 use super::utils::extract_clippy_lint;
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
-use rustc_ast::NestedMetaItem;
+use rustc_ast::MetaItemInner;
 use rustc_lint::{LateContext, Level, LintContext};
 use rustc_span::symbol::Symbol;
 use rustc_span::{DUMMY_SP, sym};
 
-pub(super) fn check(cx: &LateContext<'_>, name: Symbol, items: &[NestedMetaItem]) {
+pub(super) fn check(cx: &LateContext<'_>, name: Symbol, items: &[MetaItemInner]) {
     for lint in items {
         if let Some(lint_name) = extract_clippy_lint(lint) {
             if lint_name.as_str() == "restriction" && name != sym::allow {
diff --git a/src/tools/clippy/clippy_lints/src/attrs/mod.rs b/src/tools/clippy/clippy_lints/src/attrs/mod.rs
index 888f28fa225..1a34ca99fc2 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/mod.rs
@@ -14,7 +14,7 @@ mod utils;
 
 use clippy_config::Conf;
 use clippy_config::msrvs::{self, Msrv};
-use rustc_ast::{Attribute, MetaItemKind, NestedMetaItem};
+use rustc_ast::{Attribute, MetaItemInner, MetaItemKind};
 use rustc_hir::{ImplItem, Item, ItemKind, TraitItem};
 use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
@@ -456,7 +456,7 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
                     return;
                 }
                 for item in items {
-                    if let NestedMetaItem::MetaItem(mi) = &item
+                    if let MetaItemInner::MetaItem(mi) = &item
                         && let MetaItemKind::NameValue(lit) = &mi.kind
                         && mi.has_name(sym::since)
                     {
diff --git a/src/tools/clippy/clippy_lints/src/attrs/non_minimal_cfg.rs b/src/tools/clippy/clippy_lints/src/attrs/non_minimal_cfg.rs
index 877025cce4c..7eff5eccfa1 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/non_minimal_cfg.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/non_minimal_cfg.rs
@@ -1,7 +1,7 @@
 use super::{Attribute, NON_MINIMAL_CFG};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::SpanRangeExt;
-use rustc_ast::{MetaItemKind, NestedMetaItem};
+use rustc_ast::{MetaItemInner, MetaItemKind};
 use rustc_errors::Applicability;
 use rustc_lint::EarlyContext;
 use rustc_span::sym;
@@ -14,9 +14,9 @@ pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute) {
     }
 }
 
-fn check_nested_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) {
+fn check_nested_cfg(cx: &EarlyContext<'_>, items: &[MetaItemInner]) {
     for item in items {
-        if let NestedMetaItem::MetaItem(meta) = item {
+        if let MetaItemInner::MetaItem(meta) = item {
             if !meta.has_name(sym::any) && !meta.has_name(sym::all) {
                 continue;
             }
diff --git a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs
index 12668c616c1..72e6ce59d59 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs
@@ -2,7 +2,7 @@ use super::utils::{extract_clippy_lint, is_lint_level, is_word};
 use super::{Attribute, USELESS_ATTRIBUTE};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::{SpanRangeExt, first_line_of_span};
-use rustc_ast::NestedMetaItem;
+use rustc_ast::MetaItemInner;
 use rustc_errors::Applicability;
 use rustc_hir::{Item, ItemKind};
 use rustc_lint::{LateContext, LintContext};
@@ -21,7 +21,7 @@ pub(super) fn check(cx: &LateContext<'_>, item: &Item<'_>, attrs: &[Attribute])
                 for lint in lint_list {
                     match item.kind {
                         ItemKind::Use(..) => {
-                            if let NestedMetaItem::MetaItem(meta_item) = lint
+                            if let MetaItemInner::MetaItem(meta_item) = lint
                                 && meta_item.is_word()
                                 && let Some(ident) = meta_item.ident()
                                 && matches!(
diff --git a/src/tools/clippy/clippy_lints/src/attrs/utils.rs b/src/tools/clippy/clippy_lints/src/attrs/utils.rs
index 91ae19acbf7..9b10ae83651 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/utils.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/utils.rs
@@ -1,5 +1,5 @@
 use clippy_utils::macros::{is_panic, macro_backtrace};
-use rustc_ast::{AttrId, NestedMetaItem};
+use rustc_ast::{AttrId, MetaItemInner};
 use rustc_hir::{
     Block, Expr, ExprKind, ImplItem, ImplItemKind, Item, ItemKind, StmtKind, TraitFn, TraitItem, TraitItemKind,
 };
@@ -8,8 +8,8 @@ use rustc_middle::ty;
 use rustc_span::sym;
 use rustc_span::symbol::Symbol;
 
-pub(super) fn is_word(nmi: &NestedMetaItem, expected: Symbol) -> bool {
-    if let NestedMetaItem::MetaItem(mi) = &nmi {
+pub(super) fn is_word(nmi: &MetaItemInner, expected: Symbol) -> bool {
+    if let MetaItemInner::MetaItem(mi) = &nmi {
         mi.is_word() && mi.has_name(expected)
     } else {
         false
@@ -74,7 +74,7 @@ fn is_relevant_expr(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>
 }
 
 /// Returns the lint name if it is clippy lint.
-pub(super) fn extract_clippy_lint(lint: &NestedMetaItem) -> Option<Symbol> {
+pub(super) fn extract_clippy_lint(lint: &MetaItemInner) -> Option<Symbol> {
     if let Some(meta_item) = lint.meta_item()
         && meta_item.path.segments.len() > 1
         && let tool_name = meta_item.path.segments[0].ident
diff --git a/src/tools/clippy/clippy_lints/src/cfg_not_test.rs b/src/tools/clippy/clippy_lints/src/cfg_not_test.rs
index 884d15cabff..84136a2e6c2 100644
--- a/src/tools/clippy/clippy_lints/src/cfg_not_test.rs
+++ b/src/tools/clippy/clippy_lints/src/cfg_not_test.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use rustc_ast::NestedMetaItem;
+use rustc_ast::MetaItemInner;
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::declare_lint_pass;
 
@@ -47,7 +47,7 @@ impl EarlyLintPass for CfgNotTest {
     }
 }
 
-fn contains_not_test(list: Option<&[NestedMetaItem]>, not: bool) -> bool {
+fn contains_not_test(list: Option<&[MetaItemInner]>, not: bool) -> bool {
     list.is_some_and(|list| {
         list.iter().any(|item| {
             item.ident().is_some_and(|ident| match ident.name {
diff --git a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs
index 796af851bac..6ad879b9fe7 100644
--- a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs
+++ b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs
@@ -6,7 +6,7 @@ use rustc_errors::Applicability;
 use rustc_hir::intravisit::{Visitor, walk_impl_item, walk_item, walk_param_bound, walk_ty};
 use rustc_hir::{
     BodyId, ExprKind, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, ImplItemKind, Item, ItemKind,
-    PredicateOrigin, Ty, TyKind, WherePredicate,
+    PredicateOrigin, Ty, WherePredicate,
 };
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::hir::nested_filter;
@@ -199,12 +199,6 @@ impl<'tcx> Visitor<'tcx> for TypeWalker<'_, 'tcx> {
     fn visit_ty(&mut self, t: &'tcx Ty<'tcx>) {
         if let Some((def_id, _)) = t.peel_refs().as_generic_param() {
             self.ty_params.remove(&def_id);
-        } else if let TyKind::OpaqueDef(id, _) = t.kind {
-            // Explicitly walk OpaqueDef. Normally `walk_ty` would do the job, but it calls
-            // `visit_nested_item`, which checks that `Self::NestedFilter::INTER` is set. We're
-            // using `OnlyBodies`, so the check ends up failing and the type isn't fully walked.
-            let item = self.nested_visit_map().item(id);
-            walk_item(self, item);
         } else {
             walk_ty(self, t);
         }
diff --git a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs
index 6794c6cabfe..5f349d78053 100644
--- a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs
+++ b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs
@@ -3,7 +3,7 @@ use clippy_utils::source::snippet;
 use rustc_errors::{Applicability, SuggestionStyle};
 use rustc_hir::def_id::DefId;
 use rustc_hir::{
-    AssocItemConstraint, GenericArg, GenericBound, GenericBounds, ItemKind, PredicateOrigin, TraitBoundModifier,
+    AssocItemConstraint, GenericArg, GenericBound, GenericBounds, PredicateOrigin, TraitBoundModifier,
     TyKind, WherePredicate,
 };
 use rustc_hir_analysis::lower_ty;
@@ -342,11 +342,8 @@ impl<'tcx> LateLintPass<'tcx> for ImpliedBoundsInImpls {
         }
     }
 
-    fn check_ty(&mut self, cx: &LateContext<'_>, ty: &rustc_hir::Ty<'_>) {
-        if let TyKind::OpaqueDef(item_id, ..) = ty.kind
-            && let item = cx.tcx.hir().item(item_id)
-            && let ItemKind::OpaqueTy(opaque_ty) = item.kind
-        {
+    fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &rustc_hir::Ty<'tcx>) {
+        if let TyKind::OpaqueDef(opaque_ty, ..) = ty.kind {
             check(cx, opaque_ty.bounds);
         }
     }
diff --git a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs
index 73ebe6aec15..96550c4d1cb 100644
--- a/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs
+++ b/src/tools/clippy/clippy_lints/src/index_refutable_slice.rs
@@ -5,7 +5,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::higher::IfLet;
 use clippy_utils::ty::is_copy;
 use clippy_utils::{is_expn_of, is_lint_allowed, path_to_local};
-use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
+use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_hir::HirId;
@@ -133,7 +133,7 @@ fn lint_slice(cx: &LateContext<'_>, slice: &SliceLintInformation) {
         .index_use
         .iter()
         .map(|(index, _)| *index)
-        .collect::<FxHashSet<_>>();
+        .collect::<FxIndexSet<_>>();
 
     let value_name = |index| format!("{}_{index}", slice.ident.name);
 
diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs
index 45346cd18a6..311bbce14bd 100644
--- a/src/tools/clippy/clippy_lints/src/len_zero.rs
+++ b/src/tools/clippy/clippy_lints/src/len_zero.rs
@@ -308,11 +308,7 @@ enum LenOutput {
 
 fn extract_future_output<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<&'tcx PathSegment<'tcx>> {
     if let ty::Alias(_, alias_ty) = ty.kind()
-        && let Some(Node::Item(item)) = cx.tcx.hir().get_if_local(alias_ty.def_id)
-        && let Item {
-            kind: ItemKind::OpaqueTy(opaque),
-            ..
-        } = item
+        && let Some(Node::OpaqueTy(opaque)) = cx.tcx.hir().get_if_local(alias_ty.def_id)
         && let OpaqueTyOrigin::AsyncFn { .. } = opaque.origin
         && let [GenericBound::Trait(trait_ref, _)] = &opaque.bounds
         && let Some(segment) = trait_ref.trait_ref.path.segments.last()
diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs
index 2eb6d99b761..6ee064a6124 100644
--- a/src/tools/clippy/clippy_lints/src/lib.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.rs
@@ -1,7 +1,6 @@
 #![feature(array_windows)]
 #![feature(binary_heap_into_iter_sorted)]
 #![feature(box_patterns)]
-#![feature(control_flow_enum)]
 #![feature(f128)]
 #![feature(f16)]
 #![feature(if_let_guard)]
@@ -27,8 +26,6 @@
     unused_qualifications,
     rustc::internal
 )]
-// Disable this rustc lint for now, as it was also done in rustc
-#![allow(rustc::potential_query_instability)]
 
 // FIXME: switch to something more ergonomic here, once available.
 // (Currently there is no way to opt into sysroot crates without `extern crate`.)
diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs
index 5c37e735445..ec28671a061 100644
--- a/src/tools/clippy/clippy_lints/src/lifetimes.rs
+++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs
@@ -1,12 +1,12 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
 use clippy_utils::trait_ref_of_method;
 use itertools::Itertools;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_errors::Applicability;
 use rustc_hir::FnRetTy::Return;
 use rustc_hir::intravisit::nested_filter::{self as hir_nested_filter, NestedFilter};
 use rustc_hir::intravisit::{
-    Visitor, walk_fn_decl, walk_generic_args, walk_generics, walk_impl_item_ref, walk_item, walk_param_bound,
+    Visitor, walk_fn_decl, walk_generic_args, walk_generics, walk_impl_item_ref, walk_param_bound,
     walk_poly_trait_ref, walk_trait_ref, walk_ty, walk_where_predicate,
 };
 use rustc_hir::{
@@ -311,7 +311,7 @@ fn could_use_elision<'tcx>(
     Some((elidable_lts, usages))
 }
 
-fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxHashSet<LocalDefId> {
+fn allowed_lts_from(named_generics: &[GenericParam<'_>]) -> FxIndexSet<LocalDefId> {
     named_generics
         .iter()
         .filter_map(|par| {
@@ -420,11 +420,9 @@ impl<'tcx> Visitor<'tcx> for RefVisitor<'_, 'tcx> {
 
     fn visit_ty(&mut self, ty: &'tcx Ty<'_>) {
         match ty.kind {
-            TyKind::OpaqueDef(item, bounds) => {
-                let map = self.cx.tcx.hir();
-                let item = map.item(item);
+            TyKind::OpaqueDef(opaque, bounds) => {
                 let len = self.lts.len();
-                walk_item(self, item);
+                self.visit_opaque_ty(opaque);
                 self.lts.truncate(len);
                 self.lts.extend(bounds.iter().filter_map(|bound| match bound {
                     GenericArg::Lifetime(&l) => Some(l),
@@ -499,7 +497,7 @@ struct Usage {
 
 struct LifetimeChecker<'cx, 'tcx, F> {
     cx: &'cx LateContext<'tcx>,
-    map: FxHashMap<LocalDefId, Vec<Usage>>,
+    map: FxIndexMap<LocalDefId, Vec<Usage>>,
     where_predicate_depth: usize,
     generic_args_depth: usize,
     phantom: std::marker::PhantomData<F>,
@@ -621,7 +619,7 @@ fn report_extra_impl_lifetimes<'tcx>(cx: &LateContext<'tcx>, impl_: &'tcx Impl<'
 fn report_elidable_impl_lifetimes<'tcx>(
     cx: &LateContext<'tcx>,
     impl_: &'tcx Impl<'_>,
-    map: &FxHashMap<LocalDefId, Vec<Usage>>,
+    map: &FxIndexMap<LocalDefId, Vec<Usage>>,
 ) {
     let single_usages = map
         .iter()
diff --git a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs
index 745f070a577..214b8b0f379 100644
--- a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs
@@ -5,7 +5,7 @@ use clippy_utils::ty::has_iter_method;
 use clippy_utils::visitors::is_local_used;
 use clippy_utils::{SpanlessEq, contains_name, higher, is_integer_const, sugg};
 use rustc_ast::ast;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::intravisit::{Visitor, walk_expr};
@@ -39,7 +39,7 @@ pub(super) fn check<'tcx>(
                 var: canonical_id,
                 indexed_mut: FxHashSet::default(),
                 indexed_indirectly: FxHashMap::default(),
-                indexed_directly: FxHashMap::default(),
+                indexed_directly: FxIndexMap::default(),
                 referenced: FxHashSet::default(),
                 nonindex: false,
                 prefer_mutable: false,
@@ -229,7 +229,7 @@ struct VarVisitor<'a, 'tcx> {
     indexed_indirectly: FxHashMap<Symbol, Option<region::Scope>>,
     /// subset of `indexed` of vars that are indexed directly: `v[i]`
     /// this will not contain cases like `v[calc_index(i)]` or `v[(i + 4) % N]`
-    indexed_directly: FxHashMap<Symbol, (Option<region::Scope>, Ty<'tcx>)>,
+    indexed_directly: FxIndexMap<Symbol, (Option<region::Scope>, Ty<'tcx>)>,
     /// Any names that are used outside an index operation.
     /// Used to detect things like `&mut vec` used together with `vec[i]`
     referenced: FxHashSet<Symbol>,
diff --git a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs
index 7097c85156c..81115cffdca 100644
--- a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs
@@ -4,7 +4,7 @@ use rustc_errors::Applicability;
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{
     Block, Body, Closure, ClosureKind, CoroutineDesugaring, CoroutineKind, CoroutineSource, Expr, ExprKind, FnDecl,
-    FnRetTy, GenericArg, GenericBound, ImplItem, Item, ItemKind, LifetimeName, Node, TraitRef, Ty, TyKind,
+    FnRetTy, GenericArg, GenericBound, ImplItem, Item, LifetimeName, Node, TraitRef, Ty, TyKind,
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
@@ -105,9 +105,7 @@ fn future_trait_ref<'tcx>(
     cx: &LateContext<'tcx>,
     ty: &'tcx Ty<'tcx>,
 ) -> Option<(&'tcx TraitRef<'tcx>, Vec<LifetimeName>)> {
-    if let TyKind::OpaqueDef(item_id, bounds) = ty.kind
-        && let item = cx.tcx.hir().item(item_id)
-        && let ItemKind::OpaqueTy(opaque) = &item.kind
+    if let TyKind::OpaqueDef(opaque, bounds) = ty.kind
         && let Some(trait_ref) = opaque.bounds.iter().find_map(|bound| {
             if let GenericBound::Trait(poly, _) = bound {
                 Some(&poly.trait_ref)
diff --git a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs
index 7372f52e1e5..2ce6a8a85a5 100644
--- a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs
@@ -7,6 +7,7 @@ use clippy_utils::ty::{for_each_top_level_late_bound_region, is_copy};
 use clippy_utils::{get_attr, is_lint_allowed};
 use itertools::Itertools;
 use rustc_ast::Mutability;
+use rustc_data_structures::fx::FxIndexSet;
 use rustc_errors::{Applicability, Diag};
 use rustc_hir::intravisit::{Visitor, walk_expr};
 use rustc_hir::{Arm, Expr, ExprKind, MatchSource};
@@ -475,19 +476,19 @@ impl<'tcx> Visitor<'tcx> for SigDropHelper<'_, 'tcx> {
 
 struct ArmSigDropHelper<'a, 'tcx> {
     sig_drop_checker: SigDropChecker<'a, 'tcx>,
-    found_sig_drop_spans: FxHashSet<Span>,
+    found_sig_drop_spans: FxIndexSet<Span>,
 }
 
 impl<'a, 'tcx> ArmSigDropHelper<'a, 'tcx> {
     fn new(cx: &'a LateContext<'tcx>) -> ArmSigDropHelper<'a, 'tcx> {
         ArmSigDropHelper {
             sig_drop_checker: SigDropChecker::new(cx),
-            found_sig_drop_spans: FxHashSet::<Span>::default(),
+            found_sig_drop_spans: FxIndexSet::<Span>::default(),
         }
     }
 }
 
-fn has_significant_drop_in_arms<'tcx>(cx: &LateContext<'tcx>, arms: &[&'tcx Expr<'_>]) -> FxHashSet<Span> {
+fn has_significant_drop_in_arms<'tcx>(cx: &LateContext<'tcx>, arms: &[&'tcx Expr<'_>]) -> FxIndexSet<Span> {
     let mut helper = ArmSigDropHelper::new(cx);
     for arm in arms {
         helper.visit_expr(arm);
diff --git a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs
index b40d7eba15e..c56a4014b34 100644
--- a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs
@@ -8,7 +8,7 @@ use clippy_utils::visitors::for_each_expr_without_closures;
 use clippy_utils::{eq_expr_value, hash_expr, higher};
 use rustc_ast::{LitKind, RangeLimits};
 use rustc_data_structures::packed::Pu128;
-use rustc_data_structures::unhash::UnhashMap;
+use rustc_data_structures::unhash::UnindexMap;
 use rustc_errors::{Applicability, Diag};
 use rustc_hir::{BinOp, Block, Body, Expr, ExprKind, UnOp};
 use rustc_lint::{LateContext, LateLintPass};
@@ -226,7 +226,7 @@ fn upper_index_expr(expr: &Expr<'_>) -> Option<usize> {
 }
 
 /// Checks if the expression is an index into a slice and adds it to `indexes`
-fn check_index<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut UnhashMap<u64, Vec<IndexEntry<'hir>>>) {
+fn check_index<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut UnindexMap<u64, Vec<IndexEntry<'hir>>>) {
     if let ExprKind::Index(slice, index_lit, _) = expr.kind
         && cx.typeck_results().expr_ty_adjusted(slice).peel_refs().is_slice()
         && let Some(index) = upper_index_expr(index_lit)
@@ -274,7 +274,7 @@ fn check_index<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Unh
 }
 
 /// Checks if the expression is an `assert!` expression and adds it to `asserts`
-fn check_assert<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut UnhashMap<u64, Vec<IndexEntry<'hir>>>) {
+fn check_assert<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut UnindexMap<u64, Vec<IndexEntry<'hir>>>) {
     if let Some((comparison, asserted_len, slice)) = assert_len_expr(cx, expr) {
         let hash = hash_expr(cx, slice);
         let indexes = map.entry(hash).or_default();
@@ -311,7 +311,7 @@ fn check_assert<'hir>(cx: &LateContext<'_>, expr: &'hir Expr<'hir>, map: &mut Un
 /// Inspects indexes and reports lints.
 ///
 /// Called at the end of this lint after all indexing and `assert!` expressions have been collected.
-fn report_indexes(cx: &LateContext<'_>, map: &UnhashMap<u64, Vec<IndexEntry<'_>>>) {
+fn report_indexes(cx: &LateContext<'_>, map: &UnindexMap<u64, Vec<IndexEntry<'_>>>) {
     for bucket in map.values() {
         for entry in bucket {
             let Some(full_span) = entry
@@ -403,7 +403,7 @@ fn report_indexes(cx: &LateContext<'_>, map: &UnhashMap<u64, Vec<IndexEntry<'_>>
 
 impl LateLintPass<'_> for MissingAssertsForIndexing {
     fn check_body(&mut self, cx: &LateContext<'_>, body: &Body<'_>) {
-        let mut map = UnhashMap::default();
+        let mut map = UnindexMap::default();
 
         for_each_expr_without_closures(body.value, |expr| {
             check_index(cx, expr, &mut map);
diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs
index 64fc1a8a1a5..007bcebdff6 100644
--- a/src/tools/clippy/clippy_lints/src/missing_doc.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs
@@ -193,8 +193,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
             | hir::ItemKind::Trait(..)
             | hir::ItemKind::TraitAlias(..)
             | hir::ItemKind::TyAlias(..)
-            | hir::ItemKind::Union(..)
-            | hir::ItemKind::OpaqueTy(..) => {},
+            | hir::ItemKind::Union(..) => {}
             hir::ItemKind::ExternCrate(..)
             | hir::ItemKind::ForeignMod { .. }
             | hir::ItemKind::GlobalAsm(..)
diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs
index d342be4545c..f95a0f63fab 100644
--- a/src/tools/clippy/clippy_lints/src/missing_inline.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs
@@ -130,7 +130,6 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline {
             | hir::ItemKind::GlobalAsm(..)
             | hir::ItemKind::TyAlias(..)
             | hir::ItemKind::Union(..)
-            | hir::ItemKind::OpaqueTy(..)
             | hir::ItemKind::ExternCrate(..)
             | hir::ItemKind::ForeignMod { .. }
             | hir::ItemKind::Impl { .. }
diff --git a/src/tools/clippy/clippy_lints/src/module_style.rs b/src/tools/clippy/clippy_lints/src/module_style.rs
index e9c5f64a255..676d608eb31 100644
--- a/src/tools/clippy/clippy_lints/src/module_style.rs
+++ b/src/tools/clippy/clippy_lints/src/module_style.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use rustc_ast::ast;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet};
 use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext};
 use rustc_session::impl_lint_pass;
 use rustc_span::def_id::LOCAL_CRATE;
@@ -87,7 +87,7 @@ impl EarlyLintPass for ModStyle {
 
         // `folder_segments` is all unique folder path segments `path/to/foo.rs` gives
         // `[path, to]` but not foo
-        let mut folder_segments = FxHashSet::default();
+        let mut folder_segments = FxIndexSet::default();
         // `mod_folders` is all the unique folder names that contain a mod.rs file
         let mut mod_folders = FxHashSet::default();
         // `file_map` maps file names to the full path including the file name
@@ -144,7 +144,7 @@ impl EarlyLintPass for ModStyle {
 /// is `mod.rs` we add it's parent folder to `mod_folders`.
 fn process_paths_for_mod_files<'a>(
     path: &'a Path,
-    folder_segments: &mut FxHashSet<&'a OsStr>,
+    folder_segments: &mut FxIndexSet<&'a OsStr>,
     mod_folders: &mut FxHashSet<&'a OsStr>,
 ) {
     let mut comp = path.components().rev().peekable();
diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs
index c2facb2fcf6..5c631a176c4 100644
--- a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs
@@ -5,7 +5,7 @@ use clippy_utils::source::snippet;
 use clippy_utils::visitors::for_each_expr;
 use clippy_utils::{inherits_cfg, is_from_proc_macro, is_self};
 use core::ops::ControlFlow;
-use rustc_data_structures::fx::{FxHashSet, FxIndexMap};
+use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{
@@ -101,7 +101,7 @@ fn check_closures<'tcx>(
     ctx: &mut MutablyUsedVariablesCtxt<'tcx>,
     cx: &LateContext<'tcx>,
     checked_closures: &mut FxHashSet<LocalDefId>,
-    closures: FxHashSet<LocalDefId>,
+    closures: FxIndexSet<LocalDefId>,
 ) {
     let hir = cx.tcx.hir();
     for closure in closures {
@@ -196,7 +196,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
                 prev_bind: None,
                 prev_move_to_closure: HirIdSet::default(),
                 aliases: HirIdMap::default(),
-                async_closures: FxHashSet::default(),
+                async_closures: FxIndexSet::default(),
                 tcx: cx.tcx,
             };
             euv::ExprUseVisitor::for_clippy(cx, fn_def_id, &mut ctx)
@@ -207,7 +207,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
 
             // We retrieve all the closures declared in the function because they will not be found
             // by `euv::Delegate`.
-            let mut closures: FxHashSet<LocalDefId> = FxHashSet::default();
+            let mut closures: FxIndexSet<LocalDefId> = FxIndexSet::default();
             for_each_expr(cx, body, |expr| {
                 if let ExprKind::Closure(closure) = expr.kind {
                     closures.insert(closure.def_id);
@@ -307,7 +307,7 @@ struct MutablyUsedVariablesCtxt<'tcx> {
     /// use of a variable.
     prev_move_to_closure: HirIdSet,
     aliases: HirIdMap<HirId>,
-    async_closures: FxHashSet<LocalDefId>,
+    async_closures: FxIndexSet<LocalDefId>,
     tcx: TyCtxt<'tcx>,
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs
index 1f223048ce5..662745e4b5d 100644
--- a/src/tools/clippy/clippy_lints/src/returns.rs
+++ b/src/tools/clippy/clippy_lints/src/returns.rs
@@ -7,7 +7,7 @@ use clippy_utils::{
     path_to_local_id, span_contains_cfg, span_find_starting_semi,
 };
 use core::ops::ControlFlow;
-use rustc_ast::NestedMetaItem;
+use rustc_ast::MetaItemInner;
 use rustc_errors::Applicability;
 use rustc_hir::LangItem::ResultErr;
 use rustc_hir::intravisit::FnKind;
@@ -407,7 +407,7 @@ fn check_final_expr<'tcx>(
                 }
             }
 
-            if ret_span.from_expansion() {
+            if ret_span.from_expansion() || is_from_proc_macro(cx, expr) {
                 return;
             }
 
@@ -421,7 +421,7 @@ fn check_final_expr<'tcx>(
                     if matches!(Level::from_attr(attr), Some(Level::Expect(_)))
                         && let metas = attr.meta_item_list()
                         && let Some(lst) = metas
-                        && let [NestedMetaItem::MetaItem(meta_item), ..] = lst.as_slice()
+                        && let [MetaItemInner::MetaItem(meta_item), ..] = lst.as_slice()
                         && let [tool, lint_name] = meta_item.path.segments.as_slice()
                         && tool.ident.name == sym::clippy
                         && matches!(
diff --git a/src/tools/clippy/clippy_lints/src/swap.rs b/src/tools/clippy/clippy_lints/src/swap.rs
index a3145c4647c..6cba560393d 100644
--- a/src/tools/clippy/clippy_lints/src/swap.rs
+++ b/src/tools/clippy/clippy_lints/src/swap.rs
@@ -6,9 +6,9 @@ use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::{can_mut_borrow_both, eq_expr_value, is_in_const_context, std_or_core};
 use itertools::Itertools;
 
+use rustc_data_structures::fx::FxIndexSet;
 use rustc_hir::intravisit::{Visitor, walk_expr};
 
-use crate::FxHashSet;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Block, Expr, ExprKind, LetStmt, PatKind, QPath, Stmt, StmtKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -334,7 +334,7 @@ struct IndexBinding<'a, 'tcx> {
 
 impl<'tcx> IndexBinding<'_, 'tcx> {
     fn snippet_index_bindings(&mut self, exprs: &[&'tcx Expr<'tcx>]) -> String {
-        let mut bindings = FxHashSet::default();
+        let mut bindings = FxIndexSet::default();
         for expr in exprs {
             bindings.insert(self.snippet_index_binding(expr));
         }
diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs
index 00277593622..3c3973857e7 100644
--- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs
+++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs
@@ -5,7 +5,7 @@ use clippy_utils::source::{SpanRangeExt, snippet, snippet_with_applicability};
 use clippy_utils::{SpanlessEq, SpanlessHash, is_from_proc_macro};
 use core::hash::{Hash, Hasher};
 use itertools::Itertools;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap, IndexEntry};
 use rustc_data_structures::unhash::UnhashMap;
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
@@ -16,7 +16,6 @@ use rustc_hir::{
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
 use rustc_span::{BytePos, Span};
-use std::collections::hash_map::Entry;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -427,7 +426,7 @@ fn rollup_traits(
     bounds: &[GenericBound<'_>],
     msg: &'static str,
 ) -> Vec<(ComparableTraitRef, Span)> {
-    let mut map = FxHashMap::default();
+    let mut map = FxIndexMap::default();
     let mut repeated_res = false;
 
     let only_comparable_trait_refs = |bound: &GenericBound<'_>| {
@@ -442,8 +441,8 @@ fn rollup_traits(
     for bound in bounds.iter().filter_map(only_comparable_trait_refs) {
         let (comparable_bound, span_direct) = bound;
         match map.entry(comparable_bound) {
-            Entry::Occupied(_) => repeated_res = true,
-            Entry::Vacant(e) => {
+            IndexEntry::Occupied(_) => repeated_res = true,
+            IndexEntry::Vacant(e) => {
                 e.insert((span_direct, i));
                 i += 1;
             },
diff --git a/src/tools/clippy/clippy_lints/src/use_self.rs b/src/tools/clippy/clippy_lints/src/use_self.rs
index 08449de79b3..f5cf4a586fd 100644
--- a/src/tools/clippy/clippy_lints/src/use_self.rs
+++ b/src/tools/clippy/clippy_lints/src/use_self.rs
@@ -85,10 +85,6 @@ const SEGMENTS_MSG: &str = "segments should be composed of at least 1 element";
 
 impl<'tcx> LateLintPass<'tcx> for UseSelf {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &Item<'tcx>) {
-        if matches!(item.kind, ItemKind::OpaqueTy(_)) {
-            // skip over `ItemKind::OpaqueTy` in order to lint `foo() -> impl <..>`
-            return;
-        }
         // We push the self types of `impl`s on a stack here. Only the top type on the stack is
         // relevant for linting, since this is the self type of the `impl` we're currently in. To
         // avoid linting on nested items, we push `StackItem::NoCheck` on the stack to signal, that
@@ -130,10 +126,8 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
         self.stack.push(stack_item);
     }
 
-    fn check_item_post(&mut self, _: &LateContext<'_>, item: &Item<'_>) {
-        if !matches!(item.kind, ItemKind::OpaqueTy(_)) {
-            self.stack.pop();
-        }
+    fn check_item_post(&mut self, _: &LateContext<'_>, _: &Item<'_>) {
+        self.stack.pop();
     }
 
     fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) {
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 9143d292f67..bfb3a76ad25 100644
--- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs
+++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs
@@ -141,62 +141,89 @@ fn path_search_pat(path: &Path<'_>) -> (Pat, Pat) {
 
 /// Get the search patterns to use for the given expression
 fn expr_search_pat(tcx: TyCtxt<'_>, e: &Expr<'_>) -> (Pat, Pat) {
-    match e.kind {
-        ExprKind::ConstBlock(_) => (Pat::Str("const"), Pat::Str("}")),
-        // Parenthesis are trimmed from the text before the search patterns are matched.
-        // See: `span_matches_pat`
-        ExprKind::Tup([]) => (Pat::Str(")"), Pat::Str("(")),
-        ExprKind::Unary(UnOp::Deref, e) => (Pat::Str("*"), expr_search_pat(tcx, e).1),
-        ExprKind::Unary(UnOp::Not, e) => (Pat::Str("!"), expr_search_pat(tcx, e).1),
-        ExprKind::Unary(UnOp::Neg, e) => (Pat::Str("-"), expr_search_pat(tcx, e).1),
-        ExprKind::Lit(lit) => lit_search_pat(&lit.node),
-        ExprKind::Array(_) | ExprKind::Repeat(..) => (Pat::Str("["), Pat::Str("]")),
-        ExprKind::Call(e, []) | ExprKind::MethodCall(_, e, [], _) => (expr_search_pat(tcx, e).0, Pat::Str("(")),
-        ExprKind::Call(first, [.., last])
-        | ExprKind::MethodCall(_, first, [.., last], _)
-        | ExprKind::Binary(_, first, last)
-        | ExprKind::Tup([first, .., last])
-        | ExprKind::Assign(first, last, _)
-        | ExprKind::AssignOp(_, first, last) => (expr_search_pat(tcx, first).0, expr_search_pat(tcx, last).1),
-        ExprKind::Tup([e]) | ExprKind::DropTemps(e) => expr_search_pat(tcx, e),
-        ExprKind::Cast(e, _) | ExprKind::Type(e, _) => (expr_search_pat(tcx, e).0, Pat::Str("")),
-        ExprKind::Let(let_expr) => (Pat::Str("let"), expr_search_pat(tcx, let_expr.init).1),
-        ExprKind::If(..) => (Pat::Str("if"), Pat::Str("}")),
-        ExprKind::Loop(_, Some(_), _, _) | ExprKind::Block(_, Some(_)) => (Pat::Str("'"), Pat::Str("}")),
-        ExprKind::Loop(_, None, LoopSource::Loop, _) => (Pat::Str("loop"), Pat::Str("}")),
-        ExprKind::Loop(_, None, LoopSource::While, _) => (Pat::Str("while"), Pat::Str("}")),
-        ExprKind::Loop(_, None, LoopSource::ForLoop, _) | ExprKind::Match(_, _, MatchSource::ForLoopDesugar) => {
-            (Pat::Str("for"), Pat::Str("}"))
-        },
-        ExprKind::Match(_, _, MatchSource::Normal) => (Pat::Str("match"), Pat::Str("}")),
-        ExprKind::Match(e, _, MatchSource::TryDesugar(_)) => (expr_search_pat(tcx, e).0, Pat::Str("?")),
-        ExprKind::Match(e, _, MatchSource::AwaitDesugar) | ExprKind::Yield(e, YieldSource::Await { .. }) => {
-            (expr_search_pat(tcx, e).0, Pat::Str("await"))
-        },
-        ExprKind::Closure(&Closure { body, .. }) => (Pat::Str(""), expr_search_pat(tcx, tcx.hir().body(body).value).1),
-        ExprKind::Block(
-            Block {
-                rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided),
-                ..
+    fn expr_search_pat_inner(tcx: TyCtxt<'_>, e: &Expr<'_>, outer_span: Span) -> (Pat, Pat) {
+        // The expression can have subexpressions in different contexts, in which case
+        // building up a search pattern from the macro expansion would lead to false positives;
+        // e.g. `return format!(..)` would be considered to be from a proc macro
+        // if we build up a pattern for the macro expansion and compare it to the invocation `format!()`.
+        // So instead we return an empty pattern such that `span_matches_pat` always returns true.
+        if !e.span.eq_ctxt(outer_span) {
+            return (Pat::Str(""), Pat::Str(""));
+        }
+
+        match e.kind {
+            ExprKind::ConstBlock(_) => (Pat::Str("const"), Pat::Str("}")),
+            // Parenthesis are trimmed from the text before the search patterns are matched.
+            // See: `span_matches_pat`
+            ExprKind::Tup([]) => (Pat::Str(")"), Pat::Str("(")),
+            ExprKind::Unary(UnOp::Deref, e) => (Pat::Str("*"), expr_search_pat_inner(tcx, e, outer_span).1),
+            ExprKind::Unary(UnOp::Not, e) => (Pat::Str("!"), expr_search_pat_inner(tcx, e, outer_span).1),
+            ExprKind::Unary(UnOp::Neg, e) => (Pat::Str("-"), expr_search_pat_inner(tcx, e, outer_span).1),
+            ExprKind::Lit(lit) => lit_search_pat(&lit.node),
+            ExprKind::Array(_) | ExprKind::Repeat(..) => (Pat::Str("["), Pat::Str("]")),
+            ExprKind::Call(e, []) | ExprKind::MethodCall(_, e, [], _) => {
+                (expr_search_pat_inner(tcx, e, outer_span).0, Pat::Str("("))
             },
-            None,
-        ) => (Pat::Str("unsafe"), Pat::Str("}")),
-        ExprKind::Block(_, None) => (Pat::Str("{"), Pat::Str("}")),
-        ExprKind::Field(e, name) => (expr_search_pat(tcx, e).0, Pat::Sym(name.name)),
-        ExprKind::Index(e, _, _) => (expr_search_pat(tcx, e).0, Pat::Str("]")),
-        ExprKind::Path(ref path) => qpath_search_pat(path),
-        ExprKind::AddrOf(_, _, e) => (Pat::Str("&"), expr_search_pat(tcx, e).1),
-        ExprKind::Break(Destination { label: None, .. }, None) => (Pat::Str("break"), Pat::Str("break")),
-        ExprKind::Break(Destination { label: Some(name), .. }, None) => (Pat::Str("break"), Pat::Sym(name.ident.name)),
-        ExprKind::Break(_, Some(e)) => (Pat::Str("break"), expr_search_pat(tcx, e).1),
-        ExprKind::Continue(Destination { label: None, .. }) => (Pat::Str("continue"), Pat::Str("continue")),
-        ExprKind::Continue(Destination { label: Some(name), .. }) => (Pat::Str("continue"), Pat::Sym(name.ident.name)),
-        ExprKind::Ret(None) => (Pat::Str("return"), Pat::Str("return")),
-        ExprKind::Ret(Some(e)) => (Pat::Str("return"), expr_search_pat(tcx, e).1),
-        ExprKind::Struct(path, _, _) => (qpath_search_pat(path).0, Pat::Str("}")),
-        ExprKind::Yield(e, YieldSource::Yield) => (Pat::Str("yield"), expr_search_pat(tcx, e).1),
-        _ => (Pat::Str(""), Pat::Str("")),
+            ExprKind::Call(first, [.., last])
+            | ExprKind::MethodCall(_, first, [.., last], _)
+            | ExprKind::Binary(_, first, last)
+            | ExprKind::Tup([first, .., last])
+            | ExprKind::Assign(first, last, _)
+            | ExprKind::AssignOp(_, first, last) => (
+                expr_search_pat_inner(tcx, first, outer_span).0,
+                expr_search_pat_inner(tcx, last, outer_span).1,
+            ),
+            ExprKind::Tup([e]) | ExprKind::DropTemps(e) => expr_search_pat_inner(tcx, e, outer_span),
+            ExprKind::Cast(e, _) | ExprKind::Type(e, _) => (expr_search_pat_inner(tcx, e, outer_span).0, Pat::Str("")),
+            ExprKind::Let(let_expr) => (Pat::Str("let"), expr_search_pat_inner(tcx, let_expr.init, outer_span).1),
+            ExprKind::If(..) => (Pat::Str("if"), Pat::Str("}")),
+            ExprKind::Loop(_, Some(_), _, _) | ExprKind::Block(_, Some(_)) => (Pat::Str("'"), Pat::Str("}")),
+            ExprKind::Loop(_, None, LoopSource::Loop, _) => (Pat::Str("loop"), Pat::Str("}")),
+            ExprKind::Loop(_, None, LoopSource::While, _) => (Pat::Str("while"), Pat::Str("}")),
+            ExprKind::Loop(_, None, LoopSource::ForLoop, _) | ExprKind::Match(_, _, MatchSource::ForLoopDesugar) => {
+                (Pat::Str("for"), Pat::Str("}"))
+            },
+            ExprKind::Match(_, _, MatchSource::Normal) => (Pat::Str("match"), Pat::Str("}")),
+            ExprKind::Match(e, _, MatchSource::TryDesugar(_)) => {
+                (expr_search_pat_inner(tcx, e, outer_span).0, Pat::Str("?"))
+            },
+            ExprKind::Match(e, _, MatchSource::AwaitDesugar) | ExprKind::Yield(e, YieldSource::Await { .. }) => {
+                (expr_search_pat_inner(tcx, e, outer_span).0, Pat::Str("await"))
+            },
+            ExprKind::Closure(&Closure { body, .. }) => (
+                Pat::Str(""),
+                expr_search_pat_inner(tcx, tcx.hir().body(body).value, outer_span).1,
+            ),
+            ExprKind::Block(
+                Block {
+                    rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided),
+                    ..
+                },
+                None,
+            ) => (Pat::Str("unsafe"), Pat::Str("}")),
+            ExprKind::Block(_, None) => (Pat::Str("{"), Pat::Str("}")),
+            ExprKind::Field(e, name) => (expr_search_pat_inner(tcx, e, outer_span).0, Pat::Sym(name.name)),
+            ExprKind::Index(e, _, _) => (expr_search_pat_inner(tcx, e, outer_span).0, Pat::Str("]")),
+            ExprKind::Path(ref path) => qpath_search_pat(path),
+            ExprKind::AddrOf(_, _, e) => (Pat::Str("&"), expr_search_pat_inner(tcx, e, outer_span).1),
+            ExprKind::Break(Destination { label: None, .. }, None) => (Pat::Str("break"), Pat::Str("break")),
+            ExprKind::Break(Destination { label: Some(name), .. }, None) => {
+                (Pat::Str("break"), Pat::Sym(name.ident.name))
+            },
+            ExprKind::Break(_, Some(e)) => (Pat::Str("break"), expr_search_pat_inner(tcx, e, outer_span).1),
+            ExprKind::Continue(Destination { label: None, .. }) => (Pat::Str("continue"), Pat::Str("continue")),
+            ExprKind::Continue(Destination { label: Some(name), .. }) => {
+                (Pat::Str("continue"), Pat::Sym(name.ident.name))
+            },
+            ExprKind::Ret(None) => (Pat::Str("return"), Pat::Str("return")),
+            ExprKind::Ret(Some(e)) => (Pat::Str("return"), expr_search_pat_inner(tcx, e, outer_span).1),
+            ExprKind::Struct(path, _, _) => (qpath_search_pat(path).0, Pat::Str("}")),
+            ExprKind::Yield(e, YieldSource::Yield) => (Pat::Str("yield"), expr_search_pat_inner(tcx, e, outer_span).1),
+            _ => (Pat::Str(""), Pat::Str("")),
+        }
     }
+
+    expr_search_pat_inner(tcx, e, e.span)
 }
 
 fn fn_header_search_pat(header: FnHeader) -> Pat {
@@ -220,7 +247,7 @@ fn item_search_pat(item: &Item<'_>) -> (Pat, Pat) {
         ItemKind::Const(..) => (Pat::Str("const"), Pat::Str(";")),
         ItemKind::Fn(sig, ..) => (fn_header_search_pat(sig.header), Pat::Str("")),
         ItemKind::ForeignMod { .. } => (Pat::Str("extern"), Pat::Str("}")),
-        ItemKind::TyAlias(..) | ItemKind::OpaqueTy(_) => (Pat::Str("type"), Pat::Str(";")),
+        ItemKind::TyAlias(..) => (Pat::Str("type"), Pat::Str(";")),
         ItemKind::Enum(..) => (Pat::Str("enum"), Pat::Str("}")),
         ItemKind::Struct(VariantData::Struct { .. }, _) => (Pat::Str("struct"), Pat::Str("}")),
         ItemKind::Struct(..) => (Pat::Str("struct"), Pat::Str(";")),
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index 514ec70a40b..10e258444a6 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -1,6 +1,5 @@
 #![feature(array_chunks)]
 #![feature(box_patterns)]
-#![feature(control_flow_enum)]
 #![feature(f128)]
 #![feature(f16)]
 #![feature(if_let_guard)]
diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs
index 4ad7575e720..eecbfb3936a 100644
--- a/src/tools/clippy/clippy_utils/src/source.rs
+++ b/src/tools/clippy/clippy_utils/src/source.rs
@@ -287,6 +287,7 @@ impl SourceFileRange {
         self.sf
             .src
             .as_ref()
+            .map(|src| src.as_str())
             .or_else(|| self.sf.external_src.get().and_then(|src| src.get_source()))
             .and_then(|x| x.get(self.range.clone()))
     }
diff --git a/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.fixed b/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.fixed
index baf939af24e..cdb8fa0454c 100644
--- a/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.fixed
+++ b/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.fixed
@@ -86,6 +86,7 @@ mod issue9612 {
         util();
     }
 
+    #[allow(unconditional_panic)]
     fn util() {
         let _a: u8 = 4.try_into().unwrap();
         let _a: u8 = 5.try_into().expect("");
diff --git a/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.rs b/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.rs
index e300ba18c33..e53d53db5f7 100644
--- a/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.rs
+++ b/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.rs
@@ -86,6 +86,7 @@ mod issue9612 {
         util();
     }
 
+    #[allow(unconditional_panic)]
     fn util() {
         let _a: u8 = 4.try_into().unwrap();
         let _a: u8 = 5.try_into().expect("");
diff --git a/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.stderr b/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.stderr
index 320578bfabc..b58ce9b8af3 100644
--- a/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.stderr
+++ b/src/tools/clippy/tests/ui-toml/unwrap_used/unwrap_used.stderr
@@ -274,7 +274,7 @@ LL |     let _ = &boxed_slice[1];
    |             ~~~~~~~~~~~~~~~
 
 error: called `.get().unwrap()` on a slice
-  --> tests/ui-toml/unwrap_used/unwrap_used.rs:93:17
+  --> tests/ui-toml/unwrap_used/unwrap_used.rs:94:17
    |
 LL |         let _ = Box::new([0]).get(1).unwrap();
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/crashes/ice-12284.rs b/src/tools/clippy/tests/ui/crashes/ice-12284.rs
deleted file mode 100644
index 8d1dbface8e..00000000000
--- a/src/tools/clippy/tests/ui/crashes/ice-12284.rs
+++ /dev/null
@@ -1,10 +0,0 @@
-#![allow(incomplete_features)]
-#![feature(unnamed_fields)]
-
-#[repr(C)]
-struct Foo {
-    _: struct {
-    },
-}
-
-fn main() {}
diff --git a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.fixed b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.fixed
index e3525191423..bda9221a5e1 100644
--- a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.fixed
+++ b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.fixed
@@ -1,5 +1,5 @@
 #![warn(clippy::dbg_macro)]
-#![allow(clippy::unnecessary_operation, clippy::no_effect)]
+#![allow(clippy::unnecessary_operation, clippy::no_effect, clippy::unit_arg)]
 
 fn foo(n: u32) -> u32 {
     if let Some(n) = n.checked_sub(4) { n } else { n }
diff --git a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.rs b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.rs
index 80606c2db05..8244254026b 100644
--- a/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.rs
+++ b/src/tools/clippy/tests/ui/dbg_macro/dbg_macro.rs
@@ -1,5 +1,5 @@
 #![warn(clippy::dbg_macro)]
-#![allow(clippy::unnecessary_operation, clippy::no_effect)]
+#![allow(clippy::unnecessary_operation, clippy::no_effect, clippy::unit_arg)]
 
 fn foo(n: u32) -> u32 {
     if let Some(n) = dbg!(n.checked_sub(4)) { n } else { n }
diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed
index edfffe8fcfe..355f2bc7736 100644
--- a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed
+++ b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed
@@ -3,7 +3,7 @@
 
 #![allow(dead_code, incomplete_features)]
 #![warn(clippy::doc_markdown)]
-#![feature(custom_inner_attributes, generic_const_exprs, const_option)]
+#![feature(custom_inner_attributes, generic_const_exprs)]
 #![rustfmt::skip]
 
 /// The `foo_bar` function does _nothing_. See also `foo::bar`. (note the dot there)
diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.rs b/src/tools/clippy/tests/ui/doc/doc-fixable.rs
index 3c0f6913e32..9ced2677622 100644
--- a/src/tools/clippy/tests/ui/doc/doc-fixable.rs
+++ b/src/tools/clippy/tests/ui/doc/doc-fixable.rs
@@ -3,7 +3,7 @@
 
 #![allow(dead_code, incomplete_features)]
 #![warn(clippy::doc_markdown)]
-#![feature(custom_inner_attributes, generic_const_exprs, const_option)]
+#![feature(custom_inner_attributes, generic_const_exprs)]
 #![rustfmt::skip]
 
 /// The foo_bar function does _nothing_. See also foo::bar. (note the dot there)
diff --git a/src/tools/clippy/tests/ui/get_unwrap.fixed b/src/tools/clippy/tests/ui/get_unwrap.fixed
index 62beb195939..2dd3c30a4e2 100644
--- a/src/tools/clippy/tests/ui/get_unwrap.fixed
+++ b/src/tools/clippy/tests/ui/get_unwrap.fixed
@@ -70,6 +70,7 @@ fn main() {
 mod issue9909 {
     #![allow(clippy::identity_op, clippy::unwrap_used, dead_code)]
 
+    #[allow(unconditional_panic)]
     fn reduced() {
         let f = &[1, 2, 3];
 
diff --git a/src/tools/clippy/tests/ui/get_unwrap.rs b/src/tools/clippy/tests/ui/get_unwrap.rs
index 1e09ff5c67e..94226564cac 100644
--- a/src/tools/clippy/tests/ui/get_unwrap.rs
+++ b/src/tools/clippy/tests/ui/get_unwrap.rs
@@ -70,6 +70,7 @@ fn main() {
 mod issue9909 {
     #![allow(clippy::identity_op, clippy::unwrap_used, dead_code)]
 
+    #[allow(unconditional_panic)]
     fn reduced() {
         let f = &[1, 2, 3];
 
diff --git a/src/tools/clippy/tests/ui/get_unwrap.stderr b/src/tools/clippy/tests/ui/get_unwrap.stderr
index 0f8b279da1e..8eacb249c60 100644
--- a/src/tools/clippy/tests/ui/get_unwrap.stderr
+++ b/src/tools/clippy/tests/ui/get_unwrap.stderr
@@ -266,7 +266,7 @@ LL |         let _ = some_vec.get_mut(0..1).unwrap().to_vec();
    = help: consider using `expect()` to provide a better panic message
 
 error: called `.get().unwrap()` on a slice
-  --> tests/ui/get_unwrap.rs:77:24
+  --> tests/ui/get_unwrap.rs:78:24
    |
 LL |         let _x: &i32 = f.get(1 + 2).unwrap();
    |                        ^^^^^^^^^^^^^^^^^^^^^
@@ -277,7 +277,7 @@ LL |         let _x: &i32 = &f[1 + 2];
    |                        ~~~~~~~~~
 
 error: called `.get().unwrap()` on a slice
-  --> tests/ui/get_unwrap.rs:80:18
+  --> tests/ui/get_unwrap.rs:81:18
    |
 LL |         let _x = f.get(1 + 2).unwrap().to_string();
    |                  ^^^^^^^^^^^^^^^^^^^^^
@@ -288,7 +288,7 @@ LL |         let _x = f[1 + 2].to_string();
    |                  ~~~~~~~~
 
 error: called `.get().unwrap()` on a slice
-  --> tests/ui/get_unwrap.rs:83:18
+  --> tests/ui/get_unwrap.rs:84:18
    |
 LL |         let _x = f.get(1 + 2).unwrap().abs();
    |                  ^^^^^^^^^^^^^^^^^^^^^
@@ -299,7 +299,7 @@ LL |         let _x = f[1 + 2].abs();
    |                  ~~~~~~~~
 
 error: called `.get_mut().unwrap()` on a slice
-  --> tests/ui/get_unwrap.rs:100:33
+  --> tests/ui/get_unwrap.rs:101:33
    |
 LL |                         let b = rest.get_mut(linidx(j, k) - linidx(i, k) - 1).unwrap();
    |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/needless_return.fixed b/src/tools/clippy/tests/ui/needless_return.fixed
index ca422e605d6..aa2a274525b 100644
--- a/src/tools/clippy/tests/ui/needless_return.fixed
+++ b/src/tools/clippy/tests/ui/needless_return.fixed
@@ -1,3 +1,4 @@
+//@aux-build:proc_macros.rs
 #![feature(yeet_expr)]
 #![allow(unused)]
 #![allow(
@@ -9,6 +10,9 @@
 )]
 #![warn(clippy::needless_return)]
 
+extern crate proc_macros;
+use proc_macros::with_span;
+
 use std::cell::RefCell;
 
 macro_rules! the_answer {
@@ -359,6 +363,10 @@ fn issue12907() -> String {
     "".split("").next().unwrap().to_string()
 }
 
+fn issue13458() {
+    with_span!(span return);
+}
+
 fn main() {}
 
 fn a(x: Option<u8>) -> Option<u8> {
diff --git a/src/tools/clippy/tests/ui/needless_return.rs b/src/tools/clippy/tests/ui/needless_return.rs
index aad6e13136f..bf67cfd3698 100644
--- a/src/tools/clippy/tests/ui/needless_return.rs
+++ b/src/tools/clippy/tests/ui/needless_return.rs
@@ -1,3 +1,4 @@
+//@aux-build:proc_macros.rs
 #![feature(yeet_expr)]
 #![allow(unused)]
 #![allow(
@@ -9,6 +10,9 @@
 )]
 #![warn(clippy::needless_return)]
 
+extern crate proc_macros;
+use proc_macros::with_span;
+
 use std::cell::RefCell;
 
 macro_rules! the_answer {
@@ -369,6 +373,10 @@ fn issue12907() -> String {
     return "".split("").next().unwrap().to_string();
 }
 
+fn issue13458() {
+    with_span!(span return);
+}
+
 fn main() {}
 
 fn a(x: Option<u8>) -> Option<u8> {
diff --git a/src/tools/clippy/tests/ui/needless_return.stderr b/src/tools/clippy/tests/ui/needless_return.stderr
index da0fa220d8c..d3c2a6badc0 100644
--- a/src/tools/clippy/tests/ui/needless_return.stderr
+++ b/src/tools/clippy/tests/ui/needless_return.stderr
@@ -1,5 +1,5 @@
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:25:5
+  --> tests/ui/needless_return.rs:29:5
    |
 LL |     return true;
    |     ^^^^^^^^^^^
@@ -13,7 +13,7 @@ LL +     true
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:29:5
+  --> tests/ui/needless_return.rs:33:5
    |
 LL |     return true;
    |     ^^^^^^^^^^^
@@ -25,7 +25,7 @@ LL +     true
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:34:5
+  --> tests/ui/needless_return.rs:38:5
    |
 LL |     return true;;;
    |     ^^^^^^^^^^^
@@ -37,7 +37,7 @@ LL +     true
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:39:5
+  --> tests/ui/needless_return.rs:43:5
    |
 LL |     return true;; ; ;
    |     ^^^^^^^^^^^
@@ -49,7 +49,7 @@ LL +     true
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:44:9
+  --> tests/ui/needless_return.rs:48:9
    |
 LL |         return true;
    |         ^^^^^^^^^^^
@@ -61,7 +61,7 @@ LL +         true
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:46:9
+  --> tests/ui/needless_return.rs:50:9
    |
 LL |         return false;
    |         ^^^^^^^^^^^^
@@ -73,7 +73,7 @@ LL +         false
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:52:17
+  --> tests/ui/needless_return.rs:56:17
    |
 LL |         true => return false,
    |                 ^^^^^^^^^^^^
@@ -84,7 +84,7 @@ LL |         true => false,
    |                 ~~~~~
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:54:13
+  --> tests/ui/needless_return.rs:58:13
    |
 LL |             return true;
    |             ^^^^^^^^^^^
@@ -96,7 +96,7 @@ LL +             true
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:61:9
+  --> tests/ui/needless_return.rs:65:9
    |
 LL |         return true;
    |         ^^^^^^^^^^^
@@ -108,7 +108,7 @@ LL +         true
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:63:16
+  --> tests/ui/needless_return.rs:67:16
    |
 LL |     let _ = || return true;
    |                ^^^^^^^^^^^
@@ -119,7 +119,7 @@ LL |     let _ = || true;
    |                ~~~~
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:67:5
+  --> tests/ui/needless_return.rs:71:5
    |
 LL |     return the_answer!();
    |     ^^^^^^^^^^^^^^^^^^^^
@@ -131,7 +131,7 @@ LL +     the_answer!()
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:70:21
+  --> tests/ui/needless_return.rs:74:21
    |
 LL |   fn test_void_fun() {
    |  _____________________^
@@ -146,7 +146,7 @@ LL + fn test_void_fun() {
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:75:11
+  --> tests/ui/needless_return.rs:79:11
    |
 LL |       if b {
    |  ___________^
@@ -161,7 +161,7 @@ LL +     if b {
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:77:13
+  --> tests/ui/needless_return.rs:81:13
    |
 LL |       } else {
    |  _____________^
@@ -176,7 +176,7 @@ LL +     } else {
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:85:14
+  --> tests/ui/needless_return.rs:89:14
    |
 LL |         _ => return,
    |              ^^^^^^
@@ -187,7 +187,7 @@ LL |         _ => (),
    |              ~~
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:93:24
+  --> tests/ui/needless_return.rs:97:24
    |
 LL |               let _ = 42;
    |  ________________________^
@@ -202,7 +202,7 @@ LL +             let _ = 42;
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:96:14
+  --> tests/ui/needless_return.rs:100:14
    |
 LL |         _ => return,
    |              ^^^^^^
@@ -213,7 +213,7 @@ LL |         _ => (),
    |              ~~
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:109:9
+  --> tests/ui/needless_return.rs:113:9
    |
 LL |         return String::from("test");
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -225,7 +225,7 @@ LL +         String::from("test")
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:111:9
+  --> tests/ui/needless_return.rs:115:9
    |
 LL |         return String::new();
    |         ^^^^^^^^^^^^^^^^^^^^
@@ -237,7 +237,7 @@ LL +         String::new()
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:133:32
+  --> tests/ui/needless_return.rs:137:32
    |
 LL |         bar.unwrap_or_else(|_| return)
    |                                ^^^^^^
@@ -248,7 +248,7 @@ LL |         bar.unwrap_or_else(|_| {})
    |                                ~~
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:137:21
+  --> tests/ui/needless_return.rs:141:21
    |
 LL |           let _ = || {
    |  _____________________^
@@ -263,7 +263,7 @@ LL +         let _ = || {
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:140:20
+  --> tests/ui/needless_return.rs:144:20
    |
 LL |         let _ = || return;
    |                    ^^^^^^
@@ -274,7 +274,7 @@ LL |         let _ = || {};
    |                    ~~
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:146:32
+  --> tests/ui/needless_return.rs:150:32
    |
 LL |         res.unwrap_or_else(|_| return Foo)
    |                                ^^^^^^^^^^
@@ -285,7 +285,7 @@ LL |         res.unwrap_or_else(|_| Foo)
    |                                ~~~
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:155:5
+  --> tests/ui/needless_return.rs:159:5
    |
 LL |     return true;
    |     ^^^^^^^^^^^
@@ -297,7 +297,7 @@ LL +     true
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:159:5
+  --> tests/ui/needless_return.rs:163:5
    |
 LL |     return true;
    |     ^^^^^^^^^^^
@@ -309,7 +309,7 @@ LL +     true
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:164:9
+  --> tests/ui/needless_return.rs:168:9
    |
 LL |         return true;
    |         ^^^^^^^^^^^
@@ -321,7 +321,7 @@ LL +         true
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:166:9
+  --> tests/ui/needless_return.rs:170:9
    |
 LL |         return false;
    |         ^^^^^^^^^^^^
@@ -333,7 +333,7 @@ LL +         false
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:172:17
+  --> tests/ui/needless_return.rs:176:17
    |
 LL |         true => return false,
    |                 ^^^^^^^^^^^^
@@ -344,7 +344,7 @@ LL |         true => false,
    |                 ~~~~~
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:174:13
+  --> tests/ui/needless_return.rs:178:13
    |
 LL |             return true;
    |             ^^^^^^^^^^^
@@ -356,7 +356,7 @@ LL +             true
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:181:9
+  --> tests/ui/needless_return.rs:185:9
    |
 LL |         return true;
    |         ^^^^^^^^^^^
@@ -368,7 +368,7 @@ LL +         true
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:183:16
+  --> tests/ui/needless_return.rs:187:16
    |
 LL |     let _ = || return true;
    |                ^^^^^^^^^^^
@@ -379,7 +379,7 @@ LL |     let _ = || true;
    |                ~~~~
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:187:5
+  --> tests/ui/needless_return.rs:191:5
    |
 LL |     return the_answer!();
    |     ^^^^^^^^^^^^^^^^^^^^
@@ -391,7 +391,7 @@ LL +     the_answer!()
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:190:33
+  --> tests/ui/needless_return.rs:194:33
    |
 LL |   async fn async_test_void_fun() {
    |  _________________________________^
@@ -406,7 +406,7 @@ LL + async fn async_test_void_fun() {
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:195:11
+  --> tests/ui/needless_return.rs:199:11
    |
 LL |       if b {
    |  ___________^
@@ -421,7 +421,7 @@ LL +     if b {
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:197:13
+  --> tests/ui/needless_return.rs:201:13
    |
 LL |       } else {
    |  _____________^
@@ -436,7 +436,7 @@ LL +     } else {
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:205:14
+  --> tests/ui/needless_return.rs:209:14
    |
 LL |         _ => return,
    |              ^^^^^^
@@ -447,7 +447,7 @@ LL |         _ => (),
    |              ~~
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:218:9
+  --> tests/ui/needless_return.rs:222:9
    |
 LL |         return String::from("test");
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -459,7 +459,7 @@ LL +         String::from("test")
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:220:9
+  --> tests/ui/needless_return.rs:224:9
    |
 LL |         return String::new();
    |         ^^^^^^^^^^^^^^^^^^^^
@@ -471,7 +471,7 @@ LL +         String::new()
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:236:5
+  --> tests/ui/needless_return.rs:240:5
    |
 LL |     return format!("Hello {}", "world!");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -483,7 +483,7 @@ LL +     format!("Hello {}", "world!")
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:277:9
+  --> tests/ui/needless_return.rs:281:9
    |
 LL |         return true;
    |         ^^^^^^^^^^^
@@ -497,7 +497,7 @@ LL ~     }
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:279:9
+  --> tests/ui/needless_return.rs:283:9
    |
 LL |         return false;
    |         ^^^^^^^^^^^^
@@ -509,7 +509,7 @@ LL ~     }
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:286:13
+  --> tests/ui/needless_return.rs:290:13
    |
 LL |             return 10;
    |             ^^^^^^^^^
@@ -524,7 +524,7 @@ LL ~     }
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:289:13
+  --> tests/ui/needless_return.rs:293:13
    |
 LL |             return 100;
    |             ^^^^^^^^^^
@@ -537,7 +537,7 @@ LL ~     }
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:297:9
+  --> tests/ui/needless_return.rs:301:9
    |
 LL |         return 0;
    |         ^^^^^^^^
@@ -549,7 +549,7 @@ LL ~     }
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:304:13
+  --> tests/ui/needless_return.rs:308:13
    |
 LL |             return *(x as *const isize);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -564,7 +564,7 @@ LL ~     }
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:306:13
+  --> tests/ui/needless_return.rs:310:13
    |
 LL |             return !*(x as *const isize);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -577,7 +577,7 @@ LL ~     }
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:313:20
+  --> tests/ui/needless_return.rs:317:20
    |
 LL |           let _ = 42;
    |  ____________________^
@@ -594,7 +594,7 @@ LL +         let _ = 42;
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:320:20
+  --> tests/ui/needless_return.rs:324:20
    |
 LL |         let _ = 42; return;
    |                    ^^^^^^^
@@ -606,7 +606,7 @@ LL +         let _ = 42;
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:332:9
+  --> tests/ui/needless_return.rs:336:9
    |
 LL |         return Ok(format!("ok!"));
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -618,7 +618,7 @@ LL +         Ok(format!("ok!"))
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:334:9
+  --> tests/ui/needless_return.rs:338:9
    |
 LL |         return Err(format!("err!"));
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -630,7 +630,7 @@ LL +         Err(format!("err!"))
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:340:9
+  --> tests/ui/needless_return.rs:344:9
    |
 LL |         return if true { 1 } else { 2 };
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -642,7 +642,7 @@ LL +         if true { 1 } else { 2 }
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:344:9
+  --> tests/ui/needless_return.rs:348:9
    |
 LL |         return if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else { 5 };
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -654,7 +654,7 @@ LL +         (if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:365:5
+  --> tests/ui/needless_return.rs:369:5
    |
 LL |     return { "a".to_string() } + "b" + { "c" };
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -666,7 +666,7 @@ LL +     ({ "a".to_string() } + "b" + { "c" })
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:369:5
+  --> tests/ui/needless_return.rs:373:5
    |
 LL |     return "".split("").next().unwrap().to_string();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/transmute_float_to_int.fixed b/src/tools/clippy/tests/ui/transmute_float_to_int.fixed
index 83814ca43b9..075a198918a 100644
--- a/src/tools/clippy/tests/ui/transmute_float_to_int.fixed
+++ b/src/tools/clippy/tests/ui/transmute_float_to_int.fixed
@@ -1,7 +1,7 @@
 #![warn(clippy::transmute_float_to_int)]
 #![allow(clippy::missing_transmute_annotations)]
-#![feature(f128, f128_const)]
-#![feature(f16, f16_const)]
+#![feature(f128)]
+#![feature(f16)]
 
 fn float_to_int() {
     let _: u32 = unsafe { 1f32.to_bits() };
diff --git a/src/tools/clippy/tests/ui/transmute_float_to_int.rs b/src/tools/clippy/tests/ui/transmute_float_to_int.rs
index 64d6e917203..12541b2f7cf 100644
--- a/src/tools/clippy/tests/ui/transmute_float_to_int.rs
+++ b/src/tools/clippy/tests/ui/transmute_float_to_int.rs
@@ -1,7 +1,7 @@
 #![warn(clippy::transmute_float_to_int)]
 #![allow(clippy::missing_transmute_annotations)]
-#![feature(f128, f128_const)]
-#![feature(f16, f16_const)]
+#![feature(f128)]
+#![feature(f16)]
 
 fn float_to_int() {
     let _: u32 = unsafe { std::mem::transmute(1f32) };
diff --git a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed
index e95054a7ccb..617d32d1fa7 100644
--- a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed
+++ b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.fixed
@@ -84,8 +84,11 @@ fn issue_10449() {
 }
 
 // Pointers cannot be cast to integers in const contexts
+#[allow(ptr_to_integer_transmute_in_consts, reason = "This is tested in the compiler test suite")]
 const fn issue_12402<P>(ptr: *const P) {
-    unsafe { transmute::<*const i32, usize>(&42i32) };
-    unsafe { transmute::<fn(*const P), usize>(issue_12402) };
-    let _ = unsafe { transmute::<_, usize>(ptr) };
+    // This test exists even though the compiler lints against it
+    // to test that clippy's transmute lints do not trigger on this.
+    unsafe { std::mem::transmute::<*const i32, usize>(&42i32) };
+    unsafe { std::mem::transmute::<fn(*const P), usize>(issue_12402) };
+    let _ = unsafe { std::mem::transmute::<_, usize>(ptr) };
 }
diff --git a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs
index e5fcdef7a1c..d68db3c2deb 100644
--- a/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs
+++ b/src/tools/clippy/tests/ui/transmutes_expressible_as_ptr_casts.rs
@@ -84,8 +84,11 @@ fn issue_10449() {
 }
 
 // Pointers cannot be cast to integers in const contexts
+#[allow(ptr_to_integer_transmute_in_consts, reason = "This is tested in the compiler test suite")]
 const fn issue_12402<P>(ptr: *const P) {
-    unsafe { transmute::<*const i32, usize>(&42i32) };
-    unsafe { transmute::<fn(*const P), usize>(issue_12402) };
-    let _ = unsafe { transmute::<_, usize>(ptr) };
+    // This test exists even though the compiler lints against it
+    // to test that clippy's transmute lints do not trigger on this.
+    unsafe { std::mem::transmute::<*const i32, usize>(&42i32) };
+    unsafe { std::mem::transmute::<fn(*const P), usize>(issue_12402) };
+    let _ = unsafe { std::mem::transmute::<_, usize>(ptr) };
 }
diff --git a/src/tools/compiletest/src/command-list.rs b/src/tools/compiletest/src/command-list.rs
index a4cedbf66e2..4d57907f26f 100644
--- a/src/tools/compiletest/src/command-list.rs
+++ b/src/tools/compiletest/src/command-list.rs
@@ -42,11 +42,14 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "ignore-cdb",
     "ignore-compare-mode-next-solver",
     "ignore-compare-mode-polonius",
+    "ignore-coverage-map",
+    "ignore-coverage-run",
     "ignore-cross-compile",
     "ignore-debug",
     "ignore-eabi",
     "ignore-emscripten",
     "ignore-endian-big",
+    "ignore-enzyme",
     "ignore-freebsd",
     "ignore-fuchsia",
     "ignore-gdb",
@@ -64,23 +67,6 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "ignore-loongarch64",
     "ignore-macabi",
     "ignore-macos",
-    "ignore-mode-assembly",
-    "ignore-mode-codegen",
-    "ignore-mode-codegen-units",
-    "ignore-mode-coverage-map",
-    "ignore-mode-coverage-run",
-    "ignore-mode-crashes",
-    "ignore-mode-debuginfo",
-    "ignore-mode-incremental",
-    "ignore-mode-js-doc-test",
-    "ignore-mode-mir-opt",
-    "ignore-mode-pretty",
-    "ignore-mode-run-make",
-    "ignore-mode-run-pass-valgrind",
-    "ignore-mode-rustdoc",
-    "ignore-mode-rustdoc-json",
-    "ignore-mode-ui",
-    "ignore-mode-ui-fulldeps",
     "ignore-msp430",
     "ignore-msvc",
     "ignore-musl",
@@ -144,7 +130,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "needs-git-hash",
     "needs-llvm-components",
     "needs-llvm-zstd",
-    "needs-profiler-support",
+    "needs-profiler-runtime",
     "needs-relocation-model-pic",
     "needs-run-enabled",
     "needs-rust-lld",
@@ -223,6 +209,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "pretty-compare-only",
     "pretty-expanded",
     "pretty-mode",
+    "reference",
     "regex-error-pattern",
     "remap-src-base",
     "revisions",
diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs
index adc89cad72f..a5418ad8384 100644
--- a/src/tools/compiletest/src/common.rs
+++ b/src/tools/compiletest/src/common.rs
@@ -53,7 +53,6 @@ macro_rules! string_enum {
 string_enum! {
     #[derive(Clone, Copy, PartialEq, Debug)]
     pub enum Mode {
-        RunPassValgrind => "run-pass-valgrind",
         Pretty => "pretty",
         DebugInfo => "debuginfo",
         Codegen => "codegen",
@@ -207,13 +206,6 @@ pub struct Config {
     /// Path to LLVM's bin directory.
     pub llvm_bin_dir: Option<PathBuf>,
 
-    /// The valgrind path.
-    pub valgrind_path: Option<String>,
-
-    /// Whether to fail if we can't run run-pass-valgrind tests under valgrind
-    /// (or, alternatively, to silently run them like regular run-pass tests).
-    pub force_valgrind: bool,
-
     /// The path to the Clang executable to run Clang-based tests with. If
     /// `None` then these tests will be ignored.
     pub run_clang_based_tests_with: Option<String>,
@@ -393,8 +385,8 @@ pub struct Config {
     pub git_merge_commit_email: String,
 
     /// True if the profiler runtime is enabled for this target.
-    /// Used by the "needs-profiler-support" header in test files.
-    pub profiler_support: bool,
+    /// Used by the "needs-profiler-runtime" directive in test files.
+    pub profiler_runtime: bool,
 }
 
 impl Config {
diff --git a/src/tools/compiletest/src/debuggers.rs b/src/tools/compiletest/src/debuggers.rs
new file mode 100644
index 00000000000..b605bc813f1
--- /dev/null
+++ b/src/tools/compiletest/src/debuggers.rs
@@ -0,0 +1,272 @@
+use std::env;
+use std::ffi::OsString;
+use std::path::PathBuf;
+use std::process::Command;
+use std::sync::Arc;
+
+use crate::common::{Config, Debugger};
+
+pub(crate) fn configure_cdb(config: &Config) -> Option<Arc<Config>> {
+    config.cdb.as_ref()?;
+
+    Some(Arc::new(Config { debugger: Some(Debugger::Cdb), ..config.clone() }))
+}
+
+pub(crate) fn configure_gdb(config: &Config) -> Option<Arc<Config>> {
+    config.gdb_version?;
+
+    if config.matches_env("msvc") {
+        return None;
+    }
+
+    if config.remote_test_client.is_some() && !config.target.contains("android") {
+        println!(
+            "WARNING: debuginfo tests are not available when \
+             testing with remote"
+        );
+        return None;
+    }
+
+    if config.target.contains("android") {
+        println!(
+            "{} debug-info test uses tcp 5039 port.\
+             please reserve it",
+            config.target
+        );
+
+        // android debug-info test uses remote debugger so, we test 1 thread
+        // at once as they're all sharing the same TCP port to communicate
+        // over.
+        //
+        // we should figure out how to lift this restriction! (run them all
+        // on different ports allocated dynamically).
+        env::set_var("RUST_TEST_THREADS", "1");
+    }
+
+    Some(Arc::new(Config { debugger: Some(Debugger::Gdb), ..config.clone() }))
+}
+
+pub(crate) fn configure_lldb(config: &Config) -> Option<Arc<Config>> {
+    config.lldb_python_dir.as_ref()?;
+
+    if let Some(350) = config.lldb_version {
+        println!(
+            "WARNING: The used version of LLDB (350) has a \
+             known issue that breaks debuginfo tests. See \
+             issue #32520 for more information. Skipping all \
+             LLDB-based tests!",
+        );
+        return None;
+    }
+
+    Some(Arc::new(Config { debugger: Some(Debugger::Lldb), ..config.clone() }))
+}
+
+/// Returns `true` if the given target is an Android target for the
+/// purposes of GDB testing.
+pub(crate) fn is_android_gdb_target(target: &str) -> bool {
+    matches!(
+        &target[..],
+        "arm-linux-androideabi" | "armv7-linux-androideabi" | "aarch64-linux-android"
+    )
+}
+
+/// Returns `true` if the given target is a MSVC target for the purposes of CDB testing.
+fn is_pc_windows_msvc_target(target: &str) -> bool {
+    target.ends_with("-pc-windows-msvc")
+}
+
+fn find_cdb(target: &str) -> Option<OsString> {
+    if !(cfg!(windows) && is_pc_windows_msvc_target(target)) {
+        return None;
+    }
+
+    let pf86 = env::var_os("ProgramFiles(x86)").or_else(|| env::var_os("ProgramFiles"))?;
+    let cdb_arch = if cfg!(target_arch = "x86") {
+        "x86"
+    } else if cfg!(target_arch = "x86_64") {
+        "x64"
+    } else if cfg!(target_arch = "aarch64") {
+        "arm64"
+    } else if cfg!(target_arch = "arm") {
+        "arm"
+    } else {
+        return None; // No compatible CDB.exe in the Windows 10 SDK
+    };
+
+    let mut path = PathBuf::new();
+    path.push(pf86);
+    path.push(r"Windows Kits\10\Debuggers"); // We could check 8.1 etc. too?
+    path.push(cdb_arch);
+    path.push(r"cdb.exe");
+
+    if !path.exists() {
+        return None;
+    }
+
+    Some(path.into_os_string())
+}
+
+/// Returns Path to CDB
+pub(crate) fn analyze_cdb(
+    cdb: Option<String>,
+    target: &str,
+) -> (Option<OsString>, Option<[u16; 4]>) {
+    let cdb = cdb.map(OsString::from).or_else(|| find_cdb(target));
+
+    let mut version = None;
+    if let Some(cdb) = cdb.as_ref() {
+        if let Ok(output) = Command::new(cdb).arg("/version").output() {
+            if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() {
+                version = extract_cdb_version(&first_line);
+            }
+        }
+    }
+
+    (cdb, version)
+}
+
+pub(crate) fn extract_cdb_version(full_version_line: &str) -> Option<[u16; 4]> {
+    // Example full_version_line: "cdb version 10.0.18362.1"
+    let version = full_version_line.rsplit(' ').next()?;
+    let mut components = version.split('.');
+    let major: u16 = components.next().unwrap().parse().unwrap();
+    let minor: u16 = components.next().unwrap().parse().unwrap();
+    let patch: u16 = components.next().unwrap_or("0").parse().unwrap();
+    let build: u16 = components.next().unwrap_or("0").parse().unwrap();
+    Some([major, minor, patch, build])
+}
+
+/// Returns (Path to GDB, GDB Version)
+pub(crate) fn analyze_gdb(
+    gdb: Option<String>,
+    target: &str,
+    android_cross_path: &PathBuf,
+) -> (Option<String>, Option<u32>) {
+    #[cfg(not(windows))]
+    const GDB_FALLBACK: &str = "gdb";
+    #[cfg(windows)]
+    const GDB_FALLBACK: &str = "gdb.exe";
+
+    let fallback_gdb = || {
+        if is_android_gdb_target(target) {
+            let mut gdb_path = match android_cross_path.to_str() {
+                Some(x) => x.to_owned(),
+                None => panic!("cannot find android cross path"),
+            };
+            gdb_path.push_str("/bin/gdb");
+            gdb_path
+        } else {
+            GDB_FALLBACK.to_owned()
+        }
+    };
+
+    let gdb = match gdb {
+        None => fallback_gdb(),
+        Some(ref s) if s.is_empty() => fallback_gdb(), // may be empty if configure found no gdb
+        Some(ref s) => s.to_owned(),
+    };
+
+    let mut version_line = None;
+    if let Ok(output) = Command::new(&gdb).arg("--version").output() {
+        if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() {
+            version_line = Some(first_line.to_string());
+        }
+    }
+
+    let version = match version_line {
+        Some(line) => extract_gdb_version(&line),
+        None => return (None, None),
+    };
+
+    (Some(gdb), version)
+}
+
+pub(crate) fn extract_gdb_version(full_version_line: &str) -> Option<u32> {
+    let full_version_line = full_version_line.trim();
+
+    // GDB versions look like this: "major.minor.patch?.yyyymmdd?", with both
+    // of the ? sections being optional
+
+    // We will parse up to 3 digits for each component, ignoring the date
+
+    // We skip text in parentheses.  This avoids accidentally parsing
+    // the openSUSE version, which looks like:
+    //  GNU gdb (GDB; openSUSE Leap 15.0) 8.1
+    // This particular form is documented in the GNU coding standards:
+    // https://www.gnu.org/prep/standards/html_node/_002d_002dversion.html#g_t_002d_002dversion
+
+    let unbracketed_part = full_version_line.split('[').next().unwrap();
+    let mut splits = unbracketed_part.trim_end().rsplit(' ');
+    let version_string = splits.next().unwrap();
+
+    let mut splits = version_string.split('.');
+    let major = splits.next().unwrap();
+    let minor = splits.next().unwrap();
+    let patch = splits.next();
+
+    let major: u32 = major.parse().unwrap();
+    let (minor, patch): (u32, u32) = match minor.find(not_a_digit) {
+        None => {
+            let minor = minor.parse().unwrap();
+            let patch: u32 = match patch {
+                Some(patch) => match patch.find(not_a_digit) {
+                    None => patch.parse().unwrap(),
+                    Some(idx) if idx > 3 => 0,
+                    Some(idx) => patch[..idx].parse().unwrap(),
+                },
+                None => 0,
+            };
+            (minor, patch)
+        }
+        // There is no patch version after minor-date (e.g. "4-2012").
+        Some(idx) => {
+            let minor = minor[..idx].parse().unwrap();
+            (minor, 0)
+        }
+    };
+
+    Some(((major * 1000) + minor) * 1000 + patch)
+}
+
+/// Returns LLDB version
+pub(crate) fn extract_lldb_version(full_version_line: &str) -> Option<u32> {
+    // Extract the major LLDB version from the given version string.
+    // LLDB version strings are different for Apple and non-Apple platforms.
+    // The Apple variant looks like this:
+    //
+    // LLDB-179.5 (older versions)
+    // lldb-300.2.51 (new versions)
+    //
+    // We are only interested in the major version number, so this function
+    // will return `Some(179)` and `Some(300)` respectively.
+    //
+    // Upstream versions look like:
+    // lldb version 6.0.1
+    //
+    // There doesn't seem to be a way to correlate the Apple version
+    // with the upstream version, and since the tests were originally
+    // written against Apple versions, we make a fake Apple version by
+    // multiplying the first number by 100. This is a hack.
+
+    let full_version_line = full_version_line.trim();
+
+    if let Some(apple_ver) =
+        full_version_line.strip_prefix("LLDB-").or_else(|| full_version_line.strip_prefix("lldb-"))
+    {
+        if let Some(idx) = apple_ver.find(not_a_digit) {
+            let version: u32 = apple_ver[..idx].parse().unwrap();
+            return Some(version);
+        }
+    } else if let Some(lldb_ver) = full_version_line.strip_prefix("lldb version ") {
+        if let Some(idx) = lldb_ver.find(not_a_digit) {
+            let version: u32 = lldb_ver[..idx].parse().ok()?;
+            return Some(version * 100);
+        }
+    }
+    None
+}
+
+fn not_a_digit(c: char) -> bool {
+    !c.is_ascii_digit()
+}
diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs
index 83a10c56208..099e620ffe0 100644
--- a/src/tools/compiletest/src/header.rs
+++ b/src/tools/compiletest/src/header.rs
@@ -5,17 +5,17 @@ use std::io::BufReader;
 use std::io::prelude::*;
 use std::path::{Path, PathBuf};
 use std::process::Command;
-use std::sync::OnceLock;
 
-use regex::Regex;
 use tracing::*;
 
 use crate::common::{Config, Debugger, FailMode, Mode, PassMode};
+use crate::debuggers::{extract_cdb_version, extract_gdb_version};
+use crate::header::auxiliary::{AuxProps, parse_and_update_aux};
 use crate::header::cfg::{MatchOutcome, parse_cfg_name_directive};
 use crate::header::needs::CachedNeedsConditions;
 use crate::util::static_regex;
-use crate::{extract_cdb_version, extract_gdb_version};
 
+pub(crate) mod auxiliary;
 mod cfg;
 mod needs;
 #[cfg(test)]
@@ -35,9 +35,10 @@ impl HeadersCache {
 /// the test.
 #[derive(Default)]
 pub struct EarlyProps {
-    pub aux: Vec<String>,
-    pub aux_bin: Vec<String>,
-    pub aux_crate: Vec<(String, String)>,
+    /// Auxiliary crates that should be built and made available to this test.
+    /// Included in [`EarlyProps`] so that the indicated files can participate
+    /// in up-to-date checking. Building happens via [`TestProps::aux`] instead.
+    pub(crate) aux: AuxProps,
     pub revisions: Vec<String>,
 }
 
@@ -56,23 +57,9 @@ impl EarlyProps {
             &mut poisoned,
             testfile,
             rdr,
-            &mut |HeaderLine { directive: ln, .. }| {
-                config.push_name_value_directive(ln, directives::AUX_BUILD, &mut props.aux, |r| {
-                    r.trim().to_string()
-                });
-                config.push_name_value_directive(
-                    ln,
-                    directives::AUX_BIN,
-                    &mut props.aux_bin,
-                    |r| r.trim().to_string(),
-                );
-                config.push_name_value_directive(
-                    ln,
-                    directives::AUX_CRATE,
-                    &mut props.aux_crate,
-                    Config::parse_aux_crate,
-                );
-                config.parse_and_update_revisions(ln, &mut props.revisions);
+            &mut |DirectiveLine { directive: ln, .. }| {
+                parse_and_update_aux(config, ln, &mut props.aux);
+                config.parse_and_update_revisions(testfile, ln, &mut props.revisions);
             },
         );
 
@@ -100,18 +87,8 @@ pub struct TestProps {
     // If present, the name of a file that this test should match when
     // pretty-printed
     pub pp_exact: Option<PathBuf>,
-    // Other crates that should be compiled (typically from the same
-    // directory as the test, but for backwards compatibility reasons
-    // we also check the auxiliary directory)
-    pub aux_builds: Vec<String>,
-    // Auxiliary crates that should be compiled as `#![crate_type = "bin"]`.
-    pub aux_bins: Vec<String>,
-    // Similar to `aux_builds`, but a list of NAME=somelib.rs of dependencies
-    // to build and pass with the `--extern` flag.
-    pub aux_crates: Vec<(String, String)>,
-    /// Similar to `aux_builds`, but also passes the resulting dylib path to
-    /// `-Zcodegen-backend`.
-    pub aux_codegen_backend: Option<String>,
+    /// Auxiliary crates that should be built and made available to this test.
+    pub(crate) aux: AuxProps,
     // Environment settings to use for compiling
     pub rustc_env: Vec<(String, String)>,
     // Environment variables to unset prior to compiling.
@@ -278,10 +255,7 @@ impl TestProps {
             run_flags: vec![],
             doc_flags: vec![],
             pp_exact: None,
-            aux_builds: vec![],
-            aux_bins: vec![],
-            aux_crates: vec![],
-            aux_codegen_backend: None,
+            aux: Default::default(),
             revisions: vec![],
             rustc_env: vec![
                 ("RUSTC_ICE".to_string(), "0".to_string()),
@@ -370,7 +344,7 @@ impl TestProps {
                 &mut poisoned,
                 testfile,
                 file,
-                &mut |HeaderLine { header_revision, directive: ln, .. }| {
+                &mut |DirectiveLine { header_revision, directive: ln, .. }| {
                     if header_revision.is_some() && header_revision != test_revision {
                         return;
                     }
@@ -417,7 +391,7 @@ impl TestProps {
                         has_edition = true;
                     }
 
-                    config.parse_and_update_revisions(ln, &mut self.revisions);
+                    config.parse_and_update_revisions(testfile, ln, &mut self.revisions);
 
                     if let Some(flags) = config.parse_name_value_directive(ln, RUN_FLAGS) {
                         self.run_flags.extend(split_flags(&flags));
@@ -456,21 +430,10 @@ impl TestProps {
                         PRETTY_COMPARE_ONLY,
                         &mut self.pretty_compare_only,
                     );
-                    config.push_name_value_directive(ln, AUX_BUILD, &mut self.aux_builds, |r| {
-                        r.trim().to_string()
-                    });
-                    config.push_name_value_directive(ln, AUX_BIN, &mut self.aux_bins, |r| {
-                        r.trim().to_string()
-                    });
-                    config.push_name_value_directive(
-                        ln,
-                        AUX_CRATE,
-                        &mut self.aux_crates,
-                        Config::parse_aux_crate,
-                    );
-                    if let Some(r) = config.parse_name_value_directive(ln, AUX_CODEGEN_BACKEND) {
-                        self.aux_codegen_backend = Some(r.trim().to_owned());
-                    }
+
+                    // Call a helper method to deal with aux-related directives.
+                    parse_and_update_aux(config, ln, &mut self.aux);
+
                     config.push_name_value_directive(
                         ln,
                         EXEC_ENV,
@@ -717,7 +680,7 @@ impl TestProps {
 
 /// Extract an `(Option<line_revision>, directive)` directive from a line if comment is present.
 ///
-/// See [`HeaderLine`] for a diagram.
+/// See [`DirectiveLine`] for a diagram.
 pub fn line_directive<'line>(
     comment: &str,
     original_line: &'line str,
@@ -775,17 +738,13 @@ const KNOWN_JSONDOCCK_DIRECTIVE_NAMES: &[&str] =
 /// ```text
 /// //@ compile-flags: -O
 ///     ^^^^^^^^^^^^^^^^^ directive
-/// ^^^^^^^^^^^^^^^^^^^^^ original_line
 ///
 /// //@ [foo] compile-flags: -O
 ///      ^^^                    header_revision
 ///           ^^^^^^^^^^^^^^^^^ directive
-/// ^^^^^^^^^^^^^^^^^^^^^^^^^^^ original_line
 /// ```
-struct HeaderLine<'ln> {
+struct DirectiveLine<'ln> {
     line_number: usize,
-    /// Raw line from the test file, including comment prefix and any revision.
-    original_line: &'ln str,
     /// Some header directives start with a revision name in square brackets
     /// (e.g. `[foo]`), and only apply to that revision of the test.
     /// If present, this field contains the revision name (e.g. `foo`).
@@ -797,7 +756,6 @@ struct HeaderLine<'ln> {
 
 pub(crate) struct CheckDirectiveResult<'ln> {
     is_known_directive: bool,
-    directive_name: &'ln str,
     trailing_directive: Option<&'ln str>,
 }
 
@@ -832,11 +790,7 @@ pub(crate) fn check_directive<'a>(
     }
     .then_some(trailing);
 
-    CheckDirectiveResult {
-        is_known_directive: is_known(&directive_name),
-        directive_name: directive_ln,
-        trailing_directive,
-    }
+    CheckDirectiveResult { is_known_directive: is_known(&directive_name), trailing_directive }
 }
 
 fn iter_header(
@@ -845,150 +799,119 @@ fn iter_header(
     poisoned: &mut bool,
     testfile: &Path,
     rdr: impl Read,
-    it: &mut dyn FnMut(HeaderLine<'_>),
+    it: &mut dyn FnMut(DirectiveLine<'_>),
 ) {
     if testfile.is_dir() {
         return;
     }
 
-    // Coverage tests in coverage-run mode always have these extra directives,
-    // without needing to specify them manually in every test file.
-    // (Some of the comments below have been copied over from the old
-    // `tests/run-make/coverage-reports/Makefile`, which no longer exists.)
+    // Coverage tests in coverage-run mode always have these extra directives, without needing to
+    // specify them manually in every test file. (Some of the comments below have been copied over
+    // from the old `tests/run-make/coverage-reports/Makefile`, which no longer exists.)
+    //
+    // FIXME(jieyouxu): I feel like there's a better way to do this, leaving for later.
     if mode == Mode::CoverageRun {
         let extra_directives: &[&str] = &[
-            "needs-profiler-support",
-            // FIXME(pietroalbini): this test currently does not work on cross-compiled
-            // targets because remote-test is not capable of sending back the *.profraw
-            // files generated by the LLVM instrumentation.
+            "needs-profiler-runtime",
+            // FIXME(pietroalbini): this test currently does not work on cross-compiled targets
+            // because remote-test is not capable of sending back the *.profraw files generated by
+            // the LLVM instrumentation.
             "ignore-cross-compile",
         ];
         // Process the extra implied directives, with a dummy line number of 0.
         for directive in extra_directives {
-            it(HeaderLine { line_number: 0, original_line: "", header_revision: None, directive });
+            it(DirectiveLine { line_number: 0, header_revision: None, directive });
         }
     }
 
+    // NOTE(jieyouxu): once we get rid of `Makefile`s we can unconditionally check for `//@`.
     let comment = if testfile.extension().is_some_and(|e| e == "rs") { "//@" } else { "#" };
 
     let mut rdr = BufReader::with_capacity(1024, rdr);
     let mut ln = String::new();
     let mut line_number = 0;
 
-    // Match on error annotations like `//~ERROR`.
-    static REVISION_MAGIC_COMMENT_RE: OnceLock<Regex> = OnceLock::new();
-    let revision_magic_comment_re =
-        REVISION_MAGIC_COMMENT_RE.get_or_init(|| Regex::new("//(\\[.*\\])?~.*").unwrap());
-
     loop {
         line_number += 1;
         ln.clear();
         if rdr.read_line(&mut ln).unwrap() == 0 {
             break;
         }
-
-        // Assume that any directives will be found before the first
-        // module or function. This doesn't seem to be an optimization
-        // with a warm page cache. Maybe with a cold one.
-        let original_line = &ln;
         let ln = ln.trim();
+
+        // Assume that any directives will be found before the first module or function. This
+        // doesn't seem to be an optimization with a warm page cache. Maybe with a cold one.
+        // FIXME(jieyouxu): this will cause `//@` directives in the rest of the test file to
+        // not be checked.
         if ln.starts_with("fn") || ln.starts_with("mod") {
             return;
+        }
 
-        // First try to accept `ui_test` style comments (`//@`)
-        } else if let Some((header_revision, non_revisioned_directive_line)) =
-            line_directive(comment, ln)
-        {
-            // Perform unknown directive check on Rust files.
-            if testfile.extension().map(|e| e == "rs").unwrap_or(false) {
-                let directive_ln = non_revisioned_directive_line.trim();
-
-                let CheckDirectiveResult { is_known_directive, trailing_directive, .. } =
-                    check_directive(directive_ln, mode, ln);
-
-                if !is_known_directive {
-                    *poisoned = true;
-
-                    eprintln!(
-                        "error: detected unknown compiletest test directive `{}` in {}:{}",
-                        directive_ln,
-                        testfile.display(),
-                        line_number,
-                    );
-
-                    return;
-                }
+        let Some((header_revision, non_revisioned_directive_line)) = line_directive(comment, ln)
+        else {
+            continue;
+        };
 
-                if let Some(trailing_directive) = &trailing_directive {
-                    *poisoned = true;
+        // Perform unknown directive check on Rust files.
+        if testfile.extension().map(|e| e == "rs").unwrap_or(false) {
+            let directive_ln = non_revisioned_directive_line.trim();
 
-                    eprintln!(
-                        "error: detected trailing compiletest test directive `{}` in {}:{}\n \
-                          help: put the trailing directive in it's own line: `//@ {}`",
-                        trailing_directive,
-                        testfile.display(),
-                        line_number,
-                        trailing_directive,
-                    );
+            let CheckDirectiveResult { is_known_directive, trailing_directive } =
+                check_directive(directive_ln, mode, ln);
 
-                    return;
-                }
-            }
+            if !is_known_directive {
+                *poisoned = true;
 
-            it(HeaderLine {
-                line_number,
-                original_line,
-                header_revision,
-                directive: non_revisioned_directive_line,
-            });
-        // Then we try to check for legacy-style candidates, which are not the magic ~ERROR family
-        // error annotations.
-        } else if !revision_magic_comment_re.is_match(ln) {
-            let Some((_, rest)) = line_directive("//", ln) else {
-                continue;
-            };
+                eprintln!(
+                    "error: detected unknown compiletest test directive `{}` in {}:{}",
+                    directive_ln,
+                    testfile.display(),
+                    line_number,
+                );
 
-            if rest.trim_start().starts_with(':') {
-                // This is likely a markdown link:
-                // `[link_name]: https://example.org`
-                continue;
+                return;
             }
 
-            let rest = rest.trim_start();
-
-            let CheckDirectiveResult { is_known_directive, directive_name, .. } =
-                check_directive(rest, mode, ln);
-
-            if is_known_directive {
+            if let Some(trailing_directive) = &trailing_directive {
                 *poisoned = true;
+
                 eprintln!(
-                    "error: detected legacy-style directive {} in compiletest test: {}:{}, please use `ui_test`-style directives `//@` instead: {:#?}",
-                    directive_name,
+                    "error: detected trailing compiletest test directive `{}` in {}:{}\n \
+                      help: put the trailing directive in it's own line: `//@ {}`",
+                    trailing_directive,
                     testfile.display(),
                     line_number,
-                    line_directive("//", ln),
+                    trailing_directive,
                 );
+
                 return;
             }
         }
+
+        it(DirectiveLine {
+            line_number,
+            header_revision,
+            directive: non_revisioned_directive_line,
+        });
     }
 }
 
 impl Config {
-    fn parse_aux_crate(r: String) -> (String, String) {
-        let mut parts = r.trim().splitn(2, '=');
-        (
-            parts.next().expect("missing aux-crate name (e.g. log=log.rs)").to_string(),
-            parts.next().expect("missing aux-crate value (e.g. log=log.rs)").to_string(),
-        )
-    }
-
-    fn parse_and_update_revisions(&self, line: &str, existing: &mut Vec<String>) {
+    fn parse_and_update_revisions(&self, testfile: &Path, line: &str, existing: &mut Vec<String>) {
         if let Some(raw) = self.parse_name_value_directive(line, "revisions") {
+            if self.mode == Mode::RunMake {
+                panic!("`run-make` tests do not support revisions: {}", testfile.display());
+            }
+
             let mut duplicates: HashSet<_> = existing.iter().cloned().collect();
             for revision in raw.split_whitespace().map(|r| r.to_string()) {
                 if !duplicates.insert(revision.clone()) {
-                    panic!("Duplicate revision: `{}` in line `{}`", revision, raw);
+                    panic!(
+                        "duplicate revision: `{}` in line `{}`: {}",
+                        revision,
+                        raw,
+                        testfile.display()
+                    );
                 }
                 existing.push(revision);
             }
@@ -1362,13 +1285,14 @@ pub fn make_test_description<R: Read>(
 
     let mut local_poisoned = false;
 
+    // Scan through the test file to handle `ignore-*`, `only-*`, and `needs-*` directives.
     iter_header(
         config.mode,
         &config.suite,
         &mut local_poisoned,
         path,
         src,
-        &mut |HeaderLine { header_revision, original_line, directive: ln, line_number }| {
+        &mut |DirectiveLine { header_revision, directive: ln, line_number }| {
             if header_revision.is_some() && header_revision != test_revision {
                 return;
             }
@@ -1393,17 +1317,7 @@ pub fn make_test_description<R: Read>(
                 };
             }
 
-            if let Some((_, post)) = original_line.trim_start().split_once("//") {
-                let post = post.trim_start();
-                if post.starts_with("ignore-tidy") {
-                    // Not handled by compiletest.
-                } else {
-                    decision!(cfg::handle_ignore(config, ln));
-                }
-            } else {
-                decision!(cfg::handle_ignore(config, ln));
-            }
-
+            decision!(cfg::handle_ignore(config, ln));
             decision!(cfg::handle_only(config, ln));
             decision!(needs::handle_needs(&cache.needs, config, ln));
             decision!(ignore_llvm(config, ln));
diff --git a/src/tools/compiletest/src/header/auxiliary.rs b/src/tools/compiletest/src/header/auxiliary.rs
new file mode 100644
index 00000000000..6f6538ce196
--- /dev/null
+++ b/src/tools/compiletest/src/header/auxiliary.rs
@@ -0,0 +1,60 @@
+//! Code for dealing with test directives that request an "auxiliary" crate to
+//! be built and made available to the test in some way.
+
+use std::iter;
+
+use crate::common::Config;
+use crate::header::directives::{AUX_BIN, AUX_BUILD, AUX_CODEGEN_BACKEND, AUX_CRATE};
+
+/// Properties parsed from `aux-*` test directives.
+#[derive(Clone, Debug, Default)]
+pub(crate) struct AuxProps {
+    /// Other crates that should be built and made available to this test.
+    /// These are filenames relative to `./auxiliary/` in the test's directory.
+    pub(crate) builds: Vec<String>,
+    /// Auxiliary crates that should be compiled as `#![crate_type = "bin"]`.
+    pub(crate) bins: Vec<String>,
+    /// Similar to `builds`, but a list of NAME=somelib.rs of dependencies
+    /// to build and pass with the `--extern` flag.
+    pub(crate) crates: Vec<(String, String)>,
+    /// Similar to `builds`, but also uses the resulting dylib as a
+    /// `-Zcodegen-backend` when compiling the test file.
+    pub(crate) codegen_backend: Option<String>,
+}
+
+impl AuxProps {
+    /// Yields all of the paths (relative to `./auxiliary/`) that have been
+    /// specified in `aux-*` directives for this test.
+    pub(crate) fn all_aux_path_strings(&self) -> impl Iterator<Item = &str> {
+        let Self { builds, bins, crates, codegen_backend } = self;
+
+        iter::empty()
+            .chain(builds.iter().map(String::as_str))
+            .chain(bins.iter().map(String::as_str))
+            .chain(crates.iter().map(|(_, path)| path.as_str()))
+            .chain(codegen_backend.iter().map(String::as_str))
+    }
+}
+
+/// If the given test directive line contains an `aux-*` directive, parse it
+/// and update [`AuxProps`] accordingly.
+pub(super) fn parse_and_update_aux(config: &Config, ln: &str, aux: &mut AuxProps) {
+    if !ln.starts_with("aux-") {
+        return;
+    }
+
+    config.push_name_value_directive(ln, AUX_BUILD, &mut aux.builds, |r| r.trim().to_string());
+    config.push_name_value_directive(ln, AUX_BIN, &mut aux.bins, |r| r.trim().to_string());
+    config.push_name_value_directive(ln, AUX_CRATE, &mut aux.crates, parse_aux_crate);
+    if let Some(r) = config.parse_name_value_directive(ln, AUX_CODEGEN_BACKEND) {
+        aux.codegen_backend = Some(r.trim().to_owned());
+    }
+}
+
+fn parse_aux_crate(r: String) -> (String, String) {
+    let mut parts = r.trim().splitn(2, '=');
+    (
+        parts.next().expect("missing aux-crate name (e.g. log=log.rs)").to_string(),
+        parts.next().expect("missing aux-crate value (e.g. log=log.rs)").to_string(),
+    )
+}
diff --git a/src/tools/compiletest/src/header/cfg.rs b/src/tools/compiletest/src/header/cfg.rs
index 948568e63c2..b9314f0abbb 100644
--- a/src/tools/compiletest/src/header/cfg.rs
+++ b/src/tools/compiletest/src/header/cfg.rs
@@ -1,6 +1,6 @@
 use std::collections::HashSet;
 
-use crate::common::{CompareMode, Config, Debugger, Mode};
+use crate::common::{CompareMode, Config, Debugger};
 use crate::header::IgnoreDecision;
 
 const EXTRA_ARCHS: &[&str] = &["spirv"];
@@ -166,6 +166,12 @@ pub(super) fn parse_cfg_name_directive<'a>(
         message: "when the target vendor is Apple"
     }
 
+    condition! {
+        name: "enzyme",
+        condition: config.has_enzyme,
+        message: "when rustc is built with LLVM Enzyme"
+    }
+
     // Technically the locally built compiler uses the "dev" channel rather than the "nightly"
     // channel, even though most people don't know or won't care about it. To avoid confusion, we
     // treat the "dev" channel as the "nightly" channel when processing the directive.
@@ -217,13 +223,10 @@ pub(super) fn parse_cfg_name_directive<'a>(
     }
     // Coverage tests run the same test file in multiple modes.
     // If a particular test should not be run in one of the modes, ignore it
-    // with "ignore-mode-coverage-map" or "ignore-mode-coverage-run".
+    // with "ignore-coverage-map" or "ignore-coverage-run".
     condition! {
-        name: format!("mode-{}", config.mode.to_str()),
-        allowed_names: ContainsPrefixed {
-            prefix: "mode-",
-            inner: Mode::STR_VARIANTS,
-        },
+        name: config.mode.to_str(),
+        allowed_names: ["coverage-map", "coverage-run"],
         message: "when the test mode is {name}",
     }
 
diff --git a/src/tools/compiletest/src/header/needs.rs b/src/tools/compiletest/src/header/needs.rs
index f5dd722ed37..a744fb61b9c 100644
--- a/src/tools/compiletest/src/header/needs.rs
+++ b/src/tools/compiletest/src/header/needs.rs
@@ -100,9 +100,9 @@ pub(super) fn handle_needs(
             ignore_reason: "ignored on targets without unwinding support",
         },
         Need {
-            name: "needs-profiler-support",
-            condition: cache.profiler_support,
-            ignore_reason: "ignored when profiler support is disabled",
+            name: "needs-profiler-runtime",
+            condition: config.profiler_runtime,
+            ignore_reason: "ignored when the profiler runtime is not available",
         },
         Need {
             name: "needs-force-clang-based-tests",
@@ -220,7 +220,6 @@ pub(super) struct CachedNeedsConditions {
     sanitizer_memtag: bool,
     sanitizer_shadow_call_stack: bool,
     sanitizer_safestack: bool,
-    profiler_support: bool,
     xray: bool,
     rust_lld: bool,
     dlltool: bool,
@@ -247,7 +246,6 @@ impl CachedNeedsConditions {
             sanitizer_memtag: sanitizers.contains(&Sanitizer::Memtag),
             sanitizer_shadow_call_stack: sanitizers.contains(&Sanitizer::ShadowCallStack),
             sanitizer_safestack: sanitizers.contains(&Sanitizer::Safestack),
-            profiler_support: config.profiler_support,
             xray: config.target_cfg().xray,
 
             // For tests using the `needs-rust-lld` directive (e.g. for `-Clink-self-contained=+linker`),
diff --git a/src/tools/compiletest/src/header/test-auxillary/known_legacy_directive.rs b/src/tools/compiletest/src/header/test-auxillary/known_legacy_directive.rs
deleted file mode 100644
index 108ca432e13..00000000000
--- a/src/tools/compiletest/src/header/test-auxillary/known_legacy_directive.rs
+++ /dev/null
@@ -1 +0,0 @@
-// ignore-wasm
diff --git a/src/tools/compiletest/src/header/tests.rs b/src/tools/compiletest/src/header/tests.rs
index ae661200c6c..c3c9496c4d2 100644
--- a/src/tools/compiletest/src/header/tests.rs
+++ b/src/tools/compiletest/src/header/tests.rs
@@ -1,6 +1,5 @@
 use std::io::Read;
 use std::path::Path;
-use std::str::FromStr;
 
 use super::iter_header;
 use crate::common::{Config, Debugger, Mode};
@@ -70,7 +69,7 @@ struct ConfigBuilder {
     llvm_version: Option<String>,
     git_hash: bool,
     system_llvm: bool,
-    profiler_support: bool,
+    profiler_runtime: bool,
 }
 
 impl ConfigBuilder {
@@ -114,8 +113,8 @@ impl ConfigBuilder {
         self
     }
 
-    fn profiler_support(&mut self, s: bool) -> &mut Self {
-        self.profiler_support = s;
+    fn profiler_runtime(&mut self, is_available: bool) -> &mut Self {
+        self.profiler_runtime = is_available;
         self
     }
 
@@ -163,8 +162,8 @@ impl ConfigBuilder {
         if self.system_llvm {
             args.push("--system-llvm".to_owned());
         }
-        if self.profiler_support {
-            args.push("--profiler-support".to_owned());
+        if self.profiler_runtime {
+            args.push("--profiler-runtime".to_owned());
         }
 
         args.push("--rustc-path".to_string());
@@ -243,7 +242,8 @@ fn aux_build() {
         //@ aux-build: b.rs
         "
         )
-        .aux,
+        .aux
+        .builds,
         vec!["a.rs", "b.rs"],
     );
 }
@@ -369,12 +369,12 @@ fn sanitizers() {
 }
 
 #[test]
-fn profiler_support() {
-    let config: Config = cfg().profiler_support(false).build();
-    assert!(check_ignore(&config, "//@ needs-profiler-support"));
+fn profiler_runtime() {
+    let config: Config = cfg().profiler_runtime(false).build();
+    assert!(check_ignore(&config, "//@ needs-profiler-runtime"));
 
-    let config: Config = cfg().profiler_support(true).build();
-    assert!(!check_ignore(&config, "//@ needs-profiler-support"));
+    let config: Config = cfg().profiler_runtime(true).build();
+    assert!(!check_ignore(&config, "//@ needs-profiler-runtime"));
 }
 
 #[test]
@@ -423,7 +423,7 @@ fn test_extract_version_range() {
 }
 
 #[test]
-#[should_panic(expected = "Duplicate revision: `rpass1` in line ` rpass1 rpass1`")]
+#[should_panic(expected = "duplicate revision: `rpass1` in line ` rpass1 rpass1`")]
 fn test_duplicate_revisions() {
     let config: Config = cfg().build();
     parse_rs(&config, "//@ revisions: rpass1 rpass1");
@@ -573,19 +573,15 @@ fn families() {
 }
 
 #[test]
-fn ignore_mode() {
-    for &mode in Mode::STR_VARIANTS {
-        // Indicate profiler support so that "coverage-run" tests aren't skipped.
-        let config: Config = cfg().mode(mode).profiler_support(true).build();
-        let other = if mode == "coverage-run" { "coverage-map" } else { "coverage-run" };
+fn ignore_coverage() {
+    // Indicate profiler runtime availability so that "coverage-run" tests aren't skipped.
+    let config = cfg().mode("coverage-map").profiler_runtime(true).build();
+    assert!(check_ignore(&config, "//@ ignore-coverage-map"));
+    assert!(!check_ignore(&config, "//@ ignore-coverage-run"));
 
-        assert_ne!(mode, other);
-        assert_eq!(config.mode, Mode::from_str(mode).unwrap());
-        assert_ne!(config.mode, Mode::from_str(other).unwrap());
-
-        assert!(check_ignore(&config, &format!("//@ ignore-mode-{mode}")));
-        assert!(!check_ignore(&config, &format!("//@ ignore-mode-{other}")));
-    }
+    let config = cfg().mode("coverage-run").profiler_runtime(true).build();
+    assert!(!check_ignore(&config, "//@ ignore-coverage-map"));
+    assert!(check_ignore(&config, "//@ ignore-coverage-run"));
 }
 
 #[test]
@@ -622,17 +618,6 @@ fn test_unknown_directive_check() {
 }
 
 #[test]
-fn test_known_legacy_directive_check() {
-    let mut poisoned = false;
-    run_path(
-        &mut poisoned,
-        Path::new("a.rs"),
-        include_bytes!("./test-auxillary/known_legacy_directive.rs"),
-    );
-    assert!(poisoned);
-}
-
-#[test]
 fn test_known_directive_check_no_error() {
     let mut poisoned = false;
     run_path(
diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs
index a8355ee9590..18000e5602c 100644
--- a/src/tools/compiletest/src/lib.rs
+++ b/src/tools/compiletest/src/lib.rs
@@ -10,6 +10,7 @@ mod tests;
 
 pub mod common;
 pub mod compute_diff;
+mod debuggers;
 pub mod errors;
 pub mod header;
 mod json;
@@ -36,8 +37,8 @@ use walkdir::WalkDir;
 
 use self::header::{EarlyProps, make_test_description};
 use crate::common::{
-    Config, Debugger, Mode, PassMode, TestPaths, UI_EXTENSIONS, expected_output_path,
-    output_base_dir, output_relative_path,
+    Config, Mode, PassMode, TestPaths, UI_EXTENSIONS, expected_output_path, output_base_dir,
+    output_relative_path,
 };
 use crate::header::HeadersCache;
 use crate::util::logv;
@@ -53,8 +54,6 @@ pub fn parse_config(args: Vec<String>) -> Config {
         .reqopt("", "python", "path to python to use for doc tests", "PATH")
         .optopt("", "jsondocck-path", "path to jsondocck to use for doc tests", "PATH")
         .optopt("", "jsondoclint-path", "path to jsondoclint to use for doc tests", "PATH")
-        .optopt("", "valgrind-path", "path to Valgrind executable for Valgrind tests", "PROGRAM")
-        .optflag("", "force-valgrind", "fail if Valgrind tests cannot be run under Valgrind")
         .optopt("", "run-clang-based-tests-with", "path to Clang executable", "PATH")
         .optopt("", "llvm-filecheck", "path to LLVM's FileCheck binary", "DIR")
         .reqopt("", "src-base", "directory to scan for test files", "PATH")
@@ -65,7 +64,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
             "",
             "mode",
             "which sort of compile tests to run",
-            "run-pass-valgrind | pretty | debug-info | codegen | rustdoc \
+            "pretty | debug-info | codegen | rustdoc \
             | rustdoc-json | codegen-units | incremental | run-make | ui \
             | js-doc-test | mir-opt | assembly | crashes",
         )
@@ -155,7 +154,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
         .optflag("", "force-rerun", "rerun tests even if the inputs are unchanged")
         .optflag("", "only-modified", "only run tests that result been modified")
         .optflag("", "nocapture", "")
-        .optflag("", "profiler-support", "is the profiler runtime enabled for this target")
+        .optflag("", "profiler-runtime", "is the profiler runtime enabled for this target")
         .optflag("h", "help", "show this message")
         .reqopt("", "channel", "current Rust channel", "CHANNEL")
         .optflag(
@@ -206,9 +205,11 @@ pub fn parse_config(args: Vec<String>) -> Config {
 
     let target = opt_str2(matches.opt_str("target"));
     let android_cross_path = opt_path(matches, "android-cross-path");
-    let (cdb, cdb_version) = analyze_cdb(matches.opt_str("cdb"), &target);
-    let (gdb, gdb_version) = analyze_gdb(matches.opt_str("gdb"), &target, &android_cross_path);
-    let lldb_version = matches.opt_str("lldb-version").as_deref().and_then(extract_lldb_version);
+    let (cdb, cdb_version) = debuggers::analyze_cdb(matches.opt_str("cdb"), &target);
+    let (gdb, gdb_version) =
+        debuggers::analyze_gdb(matches.opt_str("gdb"), &target, &android_cross_path);
+    let lldb_version =
+        matches.opt_str("lldb-version").as_deref().and_then(debuggers::extract_lldb_version);
     let color = match matches.opt_str("color").as_deref() {
         Some("auto") | None => ColorConfig::AutoColor,
         Some("always") => ColorConfig::AlwaysColor,
@@ -269,8 +270,6 @@ pub fn parse_config(args: Vec<String>) -> Config {
         python: matches.opt_str("python").unwrap(),
         jsondocck_path: matches.opt_str("jsondocck-path"),
         jsondoclint_path: matches.opt_str("jsondoclint-path"),
-        valgrind_path: matches.opt_str("valgrind-path"),
-        force_valgrind: matches.opt_present("force-valgrind"),
         run_clang_based_tests_with: matches.opt_str("run-clang-based-tests-with"),
         llvm_filecheck: matches.opt_str("llvm-filecheck").map(PathBuf::from),
         llvm_bin_dir: matches.opt_str("llvm-bin-dir").map(PathBuf::from),
@@ -359,7 +358,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
         nightly_branch: matches.opt_str("nightly-branch").unwrap(),
         git_merge_commit_email: matches.opt_str("git-merge-commit-email").unwrap(),
 
-        profiler_support: matches.opt_present("profiler-support"),
+        profiler_runtime: matches.opt_present("profiler-runtime"),
     }
 }
 
@@ -447,9 +446,9 @@ pub fn run_tests(config: Arc<Config>) {
     if let Mode::DebugInfo = config.mode {
         // Debugging emscripten code doesn't make sense today
         if !config.target.contains("emscripten") {
-            configs.extend(configure_cdb(&config));
-            configs.extend(configure_gdb(&config));
-            configs.extend(configure_lldb(&config));
+            configs.extend(debuggers::configure_cdb(&config));
+            configs.extend(debuggers::configure_gdb(&config));
+            configs.extend(debuggers::configure_lldb(&config));
         }
     } else {
         configs.push(config.clone());
@@ -502,62 +501,6 @@ pub fn run_tests(config: Arc<Config>) {
     }
 }
 
-fn configure_cdb(config: &Config) -> Option<Arc<Config>> {
-    config.cdb.as_ref()?;
-
-    Some(Arc::new(Config { debugger: Some(Debugger::Cdb), ..config.clone() }))
-}
-
-fn configure_gdb(config: &Config) -> Option<Arc<Config>> {
-    config.gdb_version?;
-
-    if config.matches_env("msvc") {
-        return None;
-    }
-
-    if config.remote_test_client.is_some() && !config.target.contains("android") {
-        println!(
-            "WARNING: debuginfo tests are not available when \
-             testing with remote"
-        );
-        return None;
-    }
-
-    if config.target.contains("android") {
-        println!(
-            "{} debug-info test uses tcp 5039 port.\
-             please reserve it",
-            config.target
-        );
-
-        // android debug-info test uses remote debugger so, we test 1 thread
-        // at once as they're all sharing the same TCP port to communicate
-        // over.
-        //
-        // we should figure out how to lift this restriction! (run them all
-        // on different ports allocated dynamically).
-        env::set_var("RUST_TEST_THREADS", "1");
-    }
-
-    Some(Arc::new(Config { debugger: Some(Debugger::Gdb), ..config.clone() }))
-}
-
-fn configure_lldb(config: &Config) -> Option<Arc<Config>> {
-    config.lldb_python_dir.as_ref()?;
-
-    if let Some(350) = config.lldb_version {
-        println!(
-            "WARNING: The used version of LLDB (350) has a \
-             known issue that breaks debuginfo tests. See \
-             issue #32520 for more information. Skipping all \
-             LLDB-based tests!",
-        );
-        return None;
-    }
-
-    Some(Arc::new(Config { debugger: Some(Debugger::Lldb), ..config.clone() }))
-}
-
 pub fn test_opts(config: &Config) -> test::TestOpts {
     if env::var("RUST_TEST_NOCAPTURE").is_ok() {
         eprintln!(
@@ -866,7 +809,8 @@ fn files_related_to_test(
         related.push(testpaths.file.clone());
     }
 
-    for aux in &props.aux {
+    for aux in props.aux.all_aux_path_strings() {
+        // FIXME(Zalathar): Perform all `auxiliary` path resolution in one place.
         let path = testpaths.file.parent().unwrap().join("auxiliary").join(aux);
         related.push(path);
     }
@@ -984,212 +928,6 @@ fn make_test_closure(
     }))
 }
 
-/// Returns `true` if the given target is an Android target for the
-/// purposes of GDB testing.
-fn is_android_gdb_target(target: &str) -> bool {
-    matches!(
-        &target[..],
-        "arm-linux-androideabi" | "armv7-linux-androideabi" | "aarch64-linux-android"
-    )
-}
-
-/// Returns `true` if the given target is a MSVC target for the purposes of CDB testing.
-fn is_pc_windows_msvc_target(target: &str) -> bool {
-    target.ends_with("-pc-windows-msvc")
-}
-
-fn find_cdb(target: &str) -> Option<OsString> {
-    if !(cfg!(windows) && is_pc_windows_msvc_target(target)) {
-        return None;
-    }
-
-    let pf86 = env::var_os("ProgramFiles(x86)").or_else(|| env::var_os("ProgramFiles"))?;
-    let cdb_arch = if cfg!(target_arch = "x86") {
-        "x86"
-    } else if cfg!(target_arch = "x86_64") {
-        "x64"
-    } else if cfg!(target_arch = "aarch64") {
-        "arm64"
-    } else if cfg!(target_arch = "arm") {
-        "arm"
-    } else {
-        return None; // No compatible CDB.exe in the Windows 10 SDK
-    };
-
-    let mut path = PathBuf::new();
-    path.push(pf86);
-    path.push(r"Windows Kits\10\Debuggers"); // We could check 8.1 etc. too?
-    path.push(cdb_arch);
-    path.push(r"cdb.exe");
-
-    if !path.exists() {
-        return None;
-    }
-
-    Some(path.into_os_string())
-}
-
-/// Returns Path to CDB
-fn analyze_cdb(cdb: Option<String>, target: &str) -> (Option<OsString>, Option<[u16; 4]>) {
-    let cdb = cdb.map(OsString::from).or_else(|| find_cdb(target));
-
-    let mut version = None;
-    if let Some(cdb) = cdb.as_ref() {
-        if let Ok(output) = Command::new(cdb).arg("/version").output() {
-            if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() {
-                version = extract_cdb_version(&first_line);
-            }
-        }
-    }
-
-    (cdb, version)
-}
-
-fn extract_cdb_version(full_version_line: &str) -> Option<[u16; 4]> {
-    // Example full_version_line: "cdb version 10.0.18362.1"
-    let version = full_version_line.rsplit(' ').next()?;
-    let mut components = version.split('.');
-    let major: u16 = components.next().unwrap().parse().unwrap();
-    let minor: u16 = components.next().unwrap().parse().unwrap();
-    let patch: u16 = components.next().unwrap_or("0").parse().unwrap();
-    let build: u16 = components.next().unwrap_or("0").parse().unwrap();
-    Some([major, minor, patch, build])
-}
-
-/// Returns (Path to GDB, GDB Version)
-fn analyze_gdb(
-    gdb: Option<String>,
-    target: &str,
-    android_cross_path: &PathBuf,
-) -> (Option<String>, Option<u32>) {
-    #[cfg(not(windows))]
-    const GDB_FALLBACK: &str = "gdb";
-    #[cfg(windows)]
-    const GDB_FALLBACK: &str = "gdb.exe";
-
-    let fallback_gdb = || {
-        if is_android_gdb_target(target) {
-            let mut gdb_path = match android_cross_path.to_str() {
-                Some(x) => x.to_owned(),
-                None => panic!("cannot find android cross path"),
-            };
-            gdb_path.push_str("/bin/gdb");
-            gdb_path
-        } else {
-            GDB_FALLBACK.to_owned()
-        }
-    };
-
-    let gdb = match gdb {
-        None => fallback_gdb(),
-        Some(ref s) if s.is_empty() => fallback_gdb(), // may be empty if configure found no gdb
-        Some(ref s) => s.to_owned(),
-    };
-
-    let mut version_line = None;
-    if let Ok(output) = Command::new(&gdb).arg("--version").output() {
-        if let Some(first_line) = String::from_utf8_lossy(&output.stdout).lines().next() {
-            version_line = Some(first_line.to_string());
-        }
-    }
-
-    let version = match version_line {
-        Some(line) => extract_gdb_version(&line),
-        None => return (None, None),
-    };
-
-    (Some(gdb), version)
-}
-
-fn extract_gdb_version(full_version_line: &str) -> Option<u32> {
-    let full_version_line = full_version_line.trim();
-
-    // GDB versions look like this: "major.minor.patch?.yyyymmdd?", with both
-    // of the ? sections being optional
-
-    // We will parse up to 3 digits for each component, ignoring the date
-
-    // We skip text in parentheses.  This avoids accidentally parsing
-    // the openSUSE version, which looks like:
-    //  GNU gdb (GDB; openSUSE Leap 15.0) 8.1
-    // This particular form is documented in the GNU coding standards:
-    // https://www.gnu.org/prep/standards/html_node/_002d_002dversion.html#g_t_002d_002dversion
-
-    let unbracketed_part = full_version_line.split('[').next().unwrap();
-    let mut splits = unbracketed_part.trim_end().rsplit(' ');
-    let version_string = splits.next().unwrap();
-
-    let mut splits = version_string.split('.');
-    let major = splits.next().unwrap();
-    let minor = splits.next().unwrap();
-    let patch = splits.next();
-
-    let major: u32 = major.parse().unwrap();
-    let (minor, patch): (u32, u32) = match minor.find(not_a_digit) {
-        None => {
-            let minor = minor.parse().unwrap();
-            let patch: u32 = match patch {
-                Some(patch) => match patch.find(not_a_digit) {
-                    None => patch.parse().unwrap(),
-                    Some(idx) if idx > 3 => 0,
-                    Some(idx) => patch[..idx].parse().unwrap(),
-                },
-                None => 0,
-            };
-            (minor, patch)
-        }
-        // There is no patch version after minor-date (e.g. "4-2012").
-        Some(idx) => {
-            let minor = minor[..idx].parse().unwrap();
-            (minor, 0)
-        }
-    };
-
-    Some(((major * 1000) + minor) * 1000 + patch)
-}
-
-/// Returns LLDB version
-fn extract_lldb_version(full_version_line: &str) -> Option<u32> {
-    // Extract the major LLDB version from the given version string.
-    // LLDB version strings are different for Apple and non-Apple platforms.
-    // The Apple variant looks like this:
-    //
-    // LLDB-179.5 (older versions)
-    // lldb-300.2.51 (new versions)
-    //
-    // We are only interested in the major version number, so this function
-    // will return `Some(179)` and `Some(300)` respectively.
-    //
-    // Upstream versions look like:
-    // lldb version 6.0.1
-    //
-    // There doesn't seem to be a way to correlate the Apple version
-    // with the upstream version, and since the tests were originally
-    // written against Apple versions, we make a fake Apple version by
-    // multiplying the first number by 100. This is a hack.
-
-    let full_version_line = full_version_line.trim();
-
-    if let Some(apple_ver) =
-        full_version_line.strip_prefix("LLDB-").or_else(|| full_version_line.strip_prefix("lldb-"))
-    {
-        if let Some(idx) = apple_ver.find(not_a_digit) {
-            let version: u32 = apple_ver[..idx].parse().unwrap();
-            return Some(version);
-        }
-    } else if let Some(lldb_ver) = full_version_line.strip_prefix("lldb version ") {
-        if let Some(idx) = lldb_ver.find(not_a_digit) {
-            let version: u32 = lldb_ver[..idx].parse().ok()?;
-            return Some(version * 100);
-        }
-    }
-    None
-}
-
-fn not_a_digit(c: char) -> bool {
-    !c.is_ascii_digit()
-}
-
 fn check_overlapping_tests(found_paths: &HashSet<PathBuf>) {
     let mut collisions = Vec::new();
     for path in found_paths {
diff --git a/src/tools/compiletest/src/main.rs b/src/tools/compiletest/src/main.rs
index 7b85e6f80b3..b0f87593f95 100644
--- a/src/tools/compiletest/src/main.rs
+++ b/src/tools/compiletest/src/main.rs
@@ -18,15 +18,11 @@ fn main() {
 
     let config = Arc::new(parse_config(env::args().collect()));
 
-    if config.valgrind_path.is_none() && config.force_valgrind {
-        panic!("Can't find Valgrind to run Valgrind tests");
-    }
-
     if !config.has_tidy && config.mode == Mode::Rustdoc {
         eprintln!("warning: `tidy` is not installed; diffs will not be generated");
     }
 
-    if !config.profiler_support && config.mode == Mode::CoverageRun {
+    if !config.profiler_runtime && config.mode == Mode::CoverageRun {
         let actioned = if config.bless { "blessed" } else { "checked" };
         eprintln!(
             r#"
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 74d86d2b521..29f9925de16 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -20,9 +20,9 @@ use tracing::*;
 use crate::common::{
     Assembly, Codegen, CodegenUnits, CompareMode, Config, CoverageMap, CoverageRun, Crashes,
     DebugInfo, Debugger, FailMode, Incremental, JsDocTest, MirOpt, PassMode, Pretty, RunMake,
-    RunPassValgrind, Rustdoc, RustdocJson, TestPaths, UI_EXTENSIONS, UI_FIXED, UI_RUN_STDERR,
-    UI_RUN_STDOUT, UI_STDERR, UI_STDOUT, UI_SVG, UI_WINDOWS_SVG, Ui, expected_output_path,
-    incremental_dir, output_base_dir, output_base_name, output_testname_unique,
+    Rustdoc, RustdocJson, TestPaths, UI_EXTENSIONS, UI_FIXED, UI_RUN_STDERR, UI_RUN_STDOUT,
+    UI_STDERR, UI_STDOUT, UI_SVG, UI_WINDOWS_SVG, Ui, expected_output_path, incremental_dir,
+    output_base_dir, output_base_name, output_testname_unique,
 };
 use crate::compute_diff::{write_diff, write_filtered_diff};
 use crate::errors::{self, Error, ErrorKind};
@@ -49,7 +49,6 @@ mod run_make;
 mod rustdoc;
 mod rustdoc_json;
 mod ui;
-mod valgrind;
 // tidy-alphabet-end
 
 #[cfg(test)]
@@ -253,7 +252,6 @@ impl<'test> TestCx<'test> {
             self.fatal("cannot use should-ice in a test that is not cfail");
         }
         match self.config.mode {
-            RunPassValgrind => self.run_valgrind_test(),
             Pretty => self.run_pretty_test(),
             DebugInfo => self.run_debuginfo_test(),
             Codegen => self.run_codegen_test(),
@@ -320,10 +318,29 @@ impl<'test> TestCx<'test> {
         }
     }
 
-    fn check_if_test_should_compile(&self, proc_res: &ProcRes, pm: Option<PassMode>) {
-        if self.should_compile_successfully(pm) {
+    fn check_if_test_should_compile(
+        &self,
+        fail_mode: Option<FailMode>,
+        pass_mode: Option<PassMode>,
+        proc_res: &ProcRes,
+    ) {
+        if self.should_compile_successfully(pass_mode) {
             if !proc_res.status.success() {
-                self.fatal_proc_rec("test compilation failed although it shouldn't!", proc_res);
+                match (fail_mode, pass_mode) {
+                    (Some(FailMode::Build), Some(PassMode::Check)) => {
+                        // A `build-fail` test needs to `check-pass`.
+                        self.fatal_proc_rec(
+                            "`build-fail` test is required to pass check build, but check build failed",
+                            proc_res,
+                        );
+                    }
+                    _ => {
+                        self.fatal_proc_rec(
+                            "test compilation failed although it shouldn't!",
+                            proc_res,
+                        );
+                    }
+                }
             }
         } else {
             if proc_res.status.success() {
@@ -843,13 +860,13 @@ impl<'test> TestCx<'test> {
     /// Auxiliaries, no matter how deep, have the same root_out_dir and root_testpaths.
     fn document(&self, root_out_dir: &Path, root_testpaths: &TestPaths) -> ProcRes {
         if self.props.build_aux_docs {
-            for rel_ab in &self.props.aux_builds {
+            for rel_ab in &self.props.aux.builds {
                 let aux_testpaths = self.compute_aux_test_paths(root_testpaths, rel_ab);
-                let aux_props =
+                let props_for_aux =
                     self.props.from_aux_file(&aux_testpaths.file, self.revision, self.config);
                 let aux_cx = TestCx {
                     config: self.config,
-                    props: &aux_props,
+                    props: &props_for_aux,
                     testpaths: &aux_testpaths,
                     revision: self.revision,
                 };
@@ -1061,11 +1078,11 @@ impl<'test> TestCx<'test> {
     fn aux_output_dir(&self) -> PathBuf {
         let aux_dir = self.aux_output_dir_name();
 
-        if !self.props.aux_builds.is_empty() {
+        if !self.props.aux.builds.is_empty() {
             remove_and_create_dir_all(&aux_dir);
         }
 
-        if !self.props.aux_bins.is_empty() {
+        if !self.props.aux.bins.is_empty() {
             let aux_bin_dir = self.aux_bin_output_dir_name();
             remove_and_create_dir_all(&aux_dir);
             remove_and_create_dir_all(&aux_bin_dir);
@@ -1075,15 +1092,15 @@ impl<'test> TestCx<'test> {
     }
 
     fn build_all_auxiliary(&self, of: &TestPaths, aux_dir: &Path, rustc: &mut Command) {
-        for rel_ab in &self.props.aux_builds {
+        for rel_ab in &self.props.aux.builds {
             self.build_auxiliary(of, rel_ab, &aux_dir, false /* is_bin */);
         }
 
-        for rel_ab in &self.props.aux_bins {
+        for rel_ab in &self.props.aux.bins {
             self.build_auxiliary(of, rel_ab, &aux_dir, true /* is_bin */);
         }
 
-        for (aux_name, aux_path) in &self.props.aux_crates {
+        for (aux_name, aux_path) in &self.props.aux.crates {
             let aux_type = self.build_auxiliary(of, &aux_path, &aux_dir, false /* is_bin */);
             let lib_name =
                 get_lib_name(&aux_path.trim_end_matches(".rs").replace('-', "_"), aux_type);
@@ -1099,7 +1116,7 @@ impl<'test> TestCx<'test> {
 
         // Build any `//@ aux-codegen-backend`, and pass the resulting library
         // to `-Zcodegen-backend` when compiling the test file.
-        if let Some(aux_file) = &self.props.aux_codegen_backend {
+        if let Some(aux_file) = &self.props.aux.codegen_backend {
             let aux_type = self.build_auxiliary(of, aux_file, aux_dir, false);
             if let Some(lib_name) = get_lib_name(aux_file.trim_end_matches(".rs"), aux_type) {
                 let lib_path = aux_dir.join(&lib_name);
@@ -1500,8 +1517,7 @@ impl<'test> TestCx<'test> {
             Crashes => {
                 set_mir_dump_dir(&mut rustc);
             }
-            RunPassValgrind | Pretty | DebugInfo | Rustdoc | RustdocJson | RunMake
-            | CodegenUnits | JsDocTest => {
+            Pretty | DebugInfo | Rustdoc | RustdocJson | RunMake | CodegenUnits | JsDocTest => {
                 // do not use JSON output
             }
         }
@@ -1783,58 +1799,14 @@ impl<'test> TestCx<'test> {
         proc_res.fatal(None, || on_failure(*self));
     }
 
-    fn get_output_file(&self, extension: &str) -> TargetLocation {
-        let thin_lto = self.props.compile_flags.iter().any(|s| s.ends_with("lto=thin"));
-        if thin_lto {
-            TargetLocation::ThisDirectory(self.output_base_dir())
-        } else {
-            // This works with both `--emit asm` (as default output name for the assembly)
-            // and `ptx-linker` because the latter can write output at requested location.
-            let output_path = self.output_base_name().with_extension(extension);
-
-            TargetLocation::ThisFile(output_path.clone())
-        }
-    }
-
-    fn get_filecheck_file(&self, extension: &str) -> PathBuf {
-        let thin_lto = self.props.compile_flags.iter().any(|s| s.ends_with("lto=thin"));
-        if thin_lto {
-            let name = self.testpaths.file.file_stem().unwrap().to_str().unwrap();
-            let canonical_name = name.replace('-', "_");
-            let mut output_file = None;
-            for entry in self.output_base_dir().read_dir().unwrap() {
-                if let Ok(entry) = entry {
-                    let entry_path = entry.path();
-                    let entry_file = entry_path.file_name().unwrap().to_str().unwrap();
-                    if entry_file.starts_with(&format!("{}.{}", name, canonical_name))
-                        && entry_file.ends_with(extension)
-                    {
-                        assert!(
-                            output_file.is_none(),
-                            "thinlto doesn't support multiple cgu tests"
-                        );
-                        output_file = Some(entry_file.to_string());
-                    }
-                }
-            }
-            if let Some(output_file) = output_file {
-                self.output_base_dir().join(output_file)
-            } else {
-                self.output_base_name().with_extension(extension)
-            }
-        } else {
-            self.output_base_name().with_extension(extension)
-        }
-    }
-
     // codegen tests (using FileCheck)
 
     fn compile_test_and_save_ir(&self) -> (ProcRes, PathBuf) {
-        let output_file = self.get_output_file("ll");
+        let output_path = self.output_base_name().with_extension("ll");
         let input_file = &self.testpaths.file;
         let rustc = self.make_compile_args(
             input_file,
-            output_file,
+            TargetLocation::ThisFile(output_path.clone()),
             Emit::LlvmIr,
             AllowUnused::No,
             LinkToAux::Yes,
@@ -1842,35 +1814,27 @@ impl<'test> TestCx<'test> {
         );
 
         let proc_res = self.compose_and_run_compiler(rustc, None, self.testpaths);
-        let output_path = self.get_filecheck_file("ll");
         (proc_res, output_path)
     }
 
     fn compile_test_and_save_assembly(&self) -> (ProcRes, PathBuf) {
-        let output_file = self.get_output_file("s");
+        // This works with both `--emit asm` (as default output name for the assembly)
+        // and `ptx-linker` because the latter can write output at requested location.
+        let output_path = self.output_base_name().with_extension("s");
         let input_file = &self.testpaths.file;
 
-        let mut emit = Emit::None;
-        match self.props.assembly_output.as_ref().map(AsRef::as_ref) {
-            Some("emit-asm") => {
-                emit = Emit::Asm;
-            }
-
-            Some("bpf-linker") => {
-                emit = Emit::LinkArgsAsm;
-            }
-
-            Some("ptx-linker") => {
-                // No extra flags needed.
-            }
-
-            Some(header) => self.fatal(&format!("unknown 'assembly-output' header: {header}")),
-            None => self.fatal("missing 'assembly-output' header"),
-        }
+        // Use the `//@ assembly-output:` directive to determine how to emit assembly.
+        let emit = match self.props.assembly_output.as_deref() {
+            Some("emit-asm") => Emit::Asm,
+            Some("bpf-linker") => Emit::LinkArgsAsm,
+            Some("ptx-linker") => Emit::None, // No extra flags needed.
+            Some(other) => self.fatal(&format!("unknown 'assembly-output' directive: {other}")),
+            None => self.fatal("missing 'assembly-output' directive"),
+        };
 
         let rustc = self.make_compile_args(
             input_file,
-            output_file,
+            TargetLocation::ThisFile(output_path.clone()),
             emit,
             AllowUnused::No,
             LinkToAux::Yes,
@@ -1878,7 +1842,6 @@ impl<'test> TestCx<'test> {
         );
 
         let proc_res = self.compose_and_run_compiler(rustc, None, self.testpaths);
-        let output_path = self.get_filecheck_file("s");
         (proc_res, output_path)
     }
 
@@ -2109,6 +2072,10 @@ impl<'test> TestCx<'test> {
             .collect()
     }
 
+    /// This method is used for `//@ check-test-line-numbers-match`.
+    ///
+    /// It checks that doctests line in the displayed doctest "name" matches where they are
+    /// defined in source code.
     fn check_rustdoc_test_option(&self, res: ProcRes) {
         let mut other_files = Vec::new();
         let mut files: HashMap<String, Vec<usize>> = HashMap::new();
@@ -2651,33 +2618,6 @@ impl<'test> TestCx<'test> {
         }
     }
 
-    // FIXME(jieyouxu): `run_rpass_test` is hoisted out here and not in incremental because
-    // apparently valgrind test falls back to `run_rpass_test` if valgrind isn't available, which
-    // seems highly questionable to me.
-    fn run_rpass_test(&self) {
-        let emit_metadata = self.should_emit_metadata(self.pass_mode());
-        let should_run = self.run_if_enabled();
-        let proc_res = self.compile_test(should_run, emit_metadata);
-
-        if !proc_res.status.success() {
-            self.fatal_proc_rec("compilation failed!", &proc_res);
-        }
-
-        // FIXME(#41968): Move this check to tidy?
-        if !errors::load_errors(&self.testpaths.file, self.revision).is_empty() {
-            self.fatal("run-pass tests with expected warnings should be moved to ui/");
-        }
-
-        if let WillExecute::Disabled = should_run {
-            return;
-        }
-
-        let proc_res = self.exec_compiled_test();
-        if !proc_res.status.success() {
-            self.fatal_proc_rec("test run failed!", &proc_res);
-        }
-    }
-
     fn aggressive_rm_rf(&self, path: &Path) -> io::Result<()> {
         for e in path.read_dir()? {
             let entry = e?;
diff --git a/src/tools/compiletest/src/runtest/debuginfo.rs b/src/tools/compiletest/src/runtest/debuginfo.rs
index 36127414ab1..bd0845b4524 100644
--- a/src/tools/compiletest/src/runtest/debuginfo.rs
+++ b/src/tools/compiletest/src/runtest/debuginfo.rs
@@ -9,8 +9,8 @@ use tracing::debug;
 use super::debugger::DebuggerCommands;
 use super::{Debugger, Emit, ProcRes, TestCx, Truncated, WillExecute};
 use crate::common::Config;
+use crate::debuggers::{extract_gdb_version, is_android_gdb_target};
 use crate::util::logv;
-use crate::{extract_gdb_version, is_android_gdb_target};
 
 impl TestCx<'_> {
     pub(super) fn run_debuginfo_test(&self) {
diff --git a/src/tools/compiletest/src/runtest/incremental.rs b/src/tools/compiletest/src/runtest/incremental.rs
index 81b006292e4..591aff0defe 100644
--- a/src/tools/compiletest/src/runtest/incremental.rs
+++ b/src/tools/compiletest/src/runtest/incremental.rs
@@ -1,10 +1,6 @@
-use super::{TestCx, WillExecute};
+use super::{FailMode, TestCx, WillExecute};
 use crate::errors;
 
-// FIXME(jieyouxu): `run_rpass_test` got hoisted out of this because apparently valgrind falls back
-// to `run_rpass_test` if valgrind isn't available, which is questionable, but keeping it for
-// refactoring changes to preserve current behavior.
-
 impl TestCx<'_> {
     pub(super) fn run_incremental_test(&self) {
         // Basic plan for a test incremental/foo/bar.rs:
@@ -73,10 +69,34 @@ impl TestCx<'_> {
         }
     }
 
+    fn run_rpass_test(&self) {
+        let emit_metadata = self.should_emit_metadata(self.pass_mode());
+        let should_run = self.run_if_enabled();
+        let proc_res = self.compile_test(should_run, emit_metadata);
+
+        if !proc_res.status.success() {
+            self.fatal_proc_rec("compilation failed!", &proc_res);
+        }
+
+        // FIXME(#41968): Move this check to tidy?
+        if !errors::load_errors(&self.testpaths.file, self.revision).is_empty() {
+            self.fatal("run-pass tests with expected warnings should be moved to ui/");
+        }
+
+        if let WillExecute::Disabled = should_run {
+            return;
+        }
+
+        let proc_res = self.exec_compiled_test();
+        if !proc_res.status.success() {
+            self.fatal_proc_rec("test run failed!", &proc_res);
+        }
+    }
+
     fn run_cfail_test(&self) {
         let pm = self.pass_mode();
         let proc_res = self.compile_test(WillExecute::No, self.should_emit_metadata(pm));
-        self.check_if_test_should_compile(&proc_res, pm);
+        self.check_if_test_should_compile(Some(FailMode::Build), pm, &proc_res);
         self.check_no_compiler_crash(&proc_res, self.props.should_ice);
 
         let output_to_check = self.get_output(&proc_res);
@@ -115,12 +135,6 @@ impl TestCx<'_> {
 
         let proc_res = self.exec_compiled_test();
 
-        // The value our Makefile configures valgrind to return on failure
-        const VALGRIND_ERR: i32 = 100;
-        if proc_res.status.code() == Some(VALGRIND_ERR) {
-            self.fatal_proc_rec("run-fail test isn't valgrind-clean!", &proc_res);
-        }
-
         let output_to_check = self.get_output(&proc_res);
         self.check_correct_failure_status(&proc_res);
         self.check_all_error_patterns(&output_to_check, &proc_res, pm);
diff --git a/src/tools/compiletest/src/runtest/ui.rs b/src/tools/compiletest/src/runtest/ui.rs
index bd8ef952a86..bb747c68029 100644
--- a/src/tools/compiletest/src/runtest/ui.rs
+++ b/src/tools/compiletest/src/runtest/ui.rs
@@ -18,14 +18,14 @@ impl TestCx<'_> {
             let pm = Some(PassMode::Check);
             let proc_res =
                 self.compile_test_general(WillExecute::No, Emit::Metadata, pm, Vec::new());
-            self.check_if_test_should_compile(&proc_res, pm);
+            self.check_if_test_should_compile(self.props.fail_mode, pm, &proc_res);
         }
 
         let pm = self.pass_mode();
         let should_run = self.should_run(pm);
         let emit_metadata = self.should_emit_metadata(pm);
         let proc_res = self.compile_test(should_run, emit_metadata);
-        self.check_if_test_should_compile(&proc_res, pm);
+        self.check_if_test_should_compile(self.props.fail_mode, pm, &proc_res);
         if matches!(proc_res.truncated, Truncated::Yes)
             && !self.props.dont_check_compiler_stdout
             && !self.props.dont_check_compiler_stderr
diff --git a/src/tools/compiletest/src/runtest/valgrind.rs b/src/tools/compiletest/src/runtest/valgrind.rs
deleted file mode 100644
index 8d72c4be9ff..00000000000
--- a/src/tools/compiletest/src/runtest/valgrind.rs
+++ /dev/null
@@ -1,34 +0,0 @@
-use super::{Emit, TestCx, WillExecute};
-
-impl TestCx<'_> {
-    pub(super) fn run_valgrind_test(&self) {
-        assert!(self.revision.is_none(), "revisions not relevant here");
-
-        // FIXME(jieyouxu): does this really make any sense? If a valgrind test isn't testing
-        // valgrind, what is it even testing?
-        if self.config.valgrind_path.is_none() {
-            assert!(!self.config.force_valgrind);
-            return self.run_rpass_test();
-        }
-
-        let should_run = self.run_if_enabled();
-        let mut proc_res = self.compile_test(should_run, Emit::None);
-
-        if !proc_res.status.success() {
-            self.fatal_proc_rec("compilation failed!", &proc_res);
-        }
-
-        if let WillExecute::Disabled = should_run {
-            return;
-        }
-
-        let mut new_config = self.config.clone();
-        new_config.runner = new_config.valgrind_path.clone();
-        let new_cx = TestCx { config: &new_config, ..*self };
-        proc_res = new_cx.exec_compiled_test();
-
-        if !proc_res.status.success() {
-            self.fatal_proc_rec("test run failed!", &proc_res);
-        }
-    }
-}
diff --git a/src/tools/compiletest/src/tests.rs b/src/tools/compiletest/src/tests.rs
index 7c2e7b0f023..680579c59ae 100644
--- a/src/tools/compiletest/src/tests.rs
+++ b/src/tools/compiletest/src/tests.rs
@@ -1,5 +1,8 @@
-use super::header::extract_llvm_version;
-use super::*;
+use std::ffi::OsString;
+
+use crate::debuggers::{extract_gdb_version, extract_lldb_version};
+use crate::header::extract_llvm_version;
+use crate::is_test;
 
 #[test]
 fn test_extract_gdb_version() {
diff --git a/src/tools/coverage-dump/src/covfun.rs b/src/tools/coverage-dump/src/covfun.rs
index c779dd0583c..33fac3edccd 100644
--- a/src/tools/coverage-dump/src/covfun.rs
+++ b/src/tools/coverage-dump/src/covfun.rs
@@ -56,6 +56,7 @@ pub(crate) fn dump_covfun_mappings(
             expression_resolver.push_operands(lhs, rhs);
         }
 
+        let mut max_counter = None;
         for i in 0..num_files {
             let num_mappings = parser.read_uleb128_u32()?;
             println!("Number of file {i} mappings: {num_mappings}");
@@ -63,6 +64,11 @@ pub(crate) fn dump_covfun_mappings(
             for _ in 0..num_mappings {
                 let (kind, region) = parser.read_mapping_kind_and_region()?;
                 println!("- {kind:?} at {region:?}");
+                kind.for_each_term(|term| {
+                    if let CovTerm::Counter(n) = term {
+                        max_counter = max_counter.max(Some(n));
+                    }
+                });
 
                 match kind {
                     // Also print expression mappings in resolved form.
@@ -83,6 +89,16 @@ pub(crate) fn dump_covfun_mappings(
         }
 
         parser.ensure_empty()?;
+
+        // Printing the highest counter ID seen in the functions mappings makes
+        // it easier to determine whether a change to coverage instrumentation
+        // has increased or decreased the number of physical counters needed.
+        // (It's possible for the generated code to have more counters that
+        // aren't used by any mappings, but that should hopefully be rare.)
+        println!("Highest counter ID seen: {}", match max_counter {
+            Some(id) => format!("c{id}"),
+            None => "(none)".to_owned(),
+        });
         println!();
     }
     Ok(())
@@ -271,6 +287,32 @@ enum MappingKind {
     },
 }
 
+impl MappingKind {
+    fn for_each_term(&self, mut callback: impl FnMut(CovTerm)) {
+        match *self {
+            Self::Code(term) => callback(term),
+            Self::Gap(term) => callback(term),
+            Self::Expansion(_id) => {}
+            Self::Skip => {}
+            Self::Branch { r#true, r#false } => {
+                callback(r#true);
+                callback(r#false);
+            }
+            Self::MCDCBranch {
+                r#true,
+                r#false,
+                condition_id: _,
+                true_next_id: _,
+                false_next_id: _,
+            } => {
+                callback(r#true);
+                callback(r#false);
+            }
+            Self::MCDCDecision { bitmap_idx: _, conditions_num: _ } => {}
+        }
+    }
+}
+
 struct MappingRegion {
     /// Offset of this region's start line, relative to the *start line* of
     /// the *previous mapping* (or 0). Line numbers are 1-based.
diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs
index b04919bdd3e..f7c752033c5 100644
--- a/src/tools/jsondoclint/src/validator.rs
+++ b/src/tools/jsondoclint/src/validator.rs
@@ -418,7 +418,7 @@ impl<'a> Validator<'a> {
         } else if !self.missing_ids.contains(id) {
             self.missing_ids.insert(id);
 
-            let sels = json_find::find_selector(&self.krate_json, &Value::String(id.0.clone()));
+            let sels = json_find::find_selector(&self.krate_json, &Value::Number(id.0.into()));
             assert_ne!(sels.len(), 0);
 
             self.fail(id, ErrorKind::NotFound(sels))
diff --git a/src/tools/jsondoclint/src/validator/tests.rs b/src/tools/jsondoclint/src/validator/tests.rs
index d15aa7db315..e842e1318db 100644
--- a/src/tools/jsondoclint/src/validator/tests.rs
+++ b/src/tools/jsondoclint/src/validator/tests.rs
@@ -15,24 +15,20 @@ fn check(krate: &Crate, errs: &[Error]) {
     assert_eq!(errs, &validator.errs[..]);
 }
 
-fn id(s: &str) -> Id {
-    Id(s.to_owned())
-}
-
 #[test]
 fn errors_on_missing_links() {
     let k = Crate {
-        root: id("0"),
+        root: Id(0),
         crate_version: None,
         includes_private: false,
-        index: FxHashMap::from_iter([(id("0"), Item {
+        index: FxHashMap::from_iter([(Id(0), Item {
             name: Some("root".to_owned()),
-            id: id(""),
+            id: Id(0),
             crate_id: 0,
             span: None,
             visibility: Visibility::Public,
             docs: None,
-            links: FxHashMap::from_iter([("Not Found".to_owned(), id("1"))]),
+            links: FxHashMap::from_iter([("Not Found".to_owned(), Id(1))]),
             attrs: vec![],
             deprecation: None,
             inner: ItemEnum::Module(Module { is_crate: true, items: vec![], is_stripped: false }),
@@ -49,7 +45,7 @@ fn errors_on_missing_links() {
             SelectorPart::Field("links".to_owned()),
             SelectorPart::Field("Not Found".to_owned()),
         ]]),
-        id: id("1"),
+        id: Id(1),
     }]);
 }
 
@@ -58,28 +54,28 @@ fn errors_on_missing_links() {
 #[test]
 fn errors_on_local_in_paths_and_not_index() {
     let krate = Crate {
-        root: id("0:0:1572"),
+        root: Id(0),
         crate_version: None,
         includes_private: false,
         index: FxHashMap::from_iter([
-            (id("0:0:1572"), Item {
-                id: id("0:0:1572"),
+            (Id(0), Item {
+                id: Id(0),
                 crate_id: 0,
                 name: Some("microcore".to_owned()),
                 span: None,
                 visibility: Visibility::Public,
                 docs: None,
-                links: FxHashMap::from_iter([(("prim@i32".to_owned(), id("0:1:1571")))]),
+                links: FxHashMap::from_iter([(("prim@i32".to_owned(), Id(2)))]),
                 attrs: Vec::new(),
                 deprecation: None,
                 inner: ItemEnum::Module(Module {
                     is_crate: true,
-                    items: vec![id("0:1:717")],
+                    items: vec![Id(1)],
                     is_stripped: false,
                 }),
             }),
-            (id("0:1:717"), Item {
-                id: id("0:1:717"),
+            (Id(1), Item {
+                id: Id(1),
                 crate_id: 0,
                 name: Some("i32".to_owned()),
                 span: None,
@@ -91,7 +87,7 @@ fn errors_on_local_in_paths_and_not_index() {
                 inner: ItemEnum::Primitive(Primitive { name: "i32".to_owned(), impls: vec![] }),
             }),
         ]),
-        paths: FxHashMap::from_iter([(id("0:1:1571"), ItemSummary {
+        paths: FxHashMap::from_iter([(Id(2), ItemSummary {
             crate_id: 0,
             path: vec!["microcore".to_owned(), "i32".to_owned()],
             kind: ItemKind::Primitive,
@@ -101,7 +97,7 @@ fn errors_on_local_in_paths_and_not_index() {
     };
 
     check(&krate, &[Error {
-        id: id("0:1:1571"),
+        id: Id(2),
         kind: ErrorKind::Custom("Id for local item in `paths` but not in `index`".to_owned()),
     }]);
 }
@@ -110,11 +106,11 @@ fn errors_on_local_in_paths_and_not_index() {
 #[should_panic = "LOCAL_CRATE_ID is wrong"]
 fn checks_local_crate_id_is_correct() {
     let krate = Crate {
-        root: id("root"),
+        root: Id(0),
         crate_version: None,
         includes_private: false,
-        index: FxHashMap::from_iter([(id("root"), Item {
-            id: id("root"),
+        index: FxHashMap::from_iter([(Id(0), Item {
+            id: Id(0),
             crate_id: LOCAL_CRATE_ID.wrapping_add(1),
             name: Some("irrelavent".to_owned()),
             span: None,
diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs
index 5b1bad28c07..475139a3b51 100644
--- a/src/tools/miri/src/diagnostics.rs
+++ b/src/tools/miri/src/diagnostics.rs
@@ -473,14 +473,14 @@ pub fn report_leaks<'tcx>(
     leaks: Vec<(AllocId, MemoryKind, Allocation<Provenance, AllocExtra<'tcx>, MiriAllocBytes>)>,
 ) {
     let mut any_pruned = false;
-    for (id, kind, mut alloc) in leaks {
+    for (id, kind, alloc) in leaks {
         let mut title = format!(
             "memory leaked: {id:?} ({}, size: {:?}, align: {:?})",
             kind,
             alloc.size().bytes(),
             alloc.align.bytes()
         );
-        let Some(backtrace) = alloc.extra.backtrace.take() else {
+        let Some(backtrace) = alloc.extra.backtrace else {
             ecx.tcx.dcx().err(title);
             continue;
         };
diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs
index 75dce211dd4..9f93f151668 100644
--- a/src/tools/miri/src/eval.rs
+++ b/src/tools/miri/src/eval.rs
@@ -473,7 +473,7 @@ pub fn eval_entry<'tcx>(
         }
         // Check for memory leaks.
         info!("Additional static roots: {:?}", ecx.machine.static_roots);
-        let leaks = ecx.find_leaked_allocations(&ecx.machine.static_roots);
+        let leaks = ecx.take_leaked_allocations(|ecx| &ecx.machine.static_roots);
         if !leaks.is_empty() {
             report_leaks(&ecx, leaks);
             tcx.dcx().note("set `MIRIFLAGS=-Zmiri-ignore-leaks` to disable this check");
diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs
index 665dd7c441a..9f772cfa982 100644
--- a/src/tools/miri/src/intrinsics/mod.rs
+++ b/src/tools/miri/src/intrinsics/mod.rs
@@ -295,6 +295,37 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 this.write_scalar(res, dest)?;
             }
 
+            "fmuladdf32" => {
+                let [a, b, c] = check_arg_count(args)?;
+                let a = this.read_scalar(a)?.to_f32()?;
+                let b = this.read_scalar(b)?.to_f32()?;
+                let c = this.read_scalar(c)?.to_f32()?;
+                let fuse: bool = this.machine.rng.get_mut().gen();
+                let res = if fuse {
+                    // FIXME: Using host floats, to work around https://github.com/rust-lang/rustc_apfloat/issues/11
+                    a.to_host().mul_add(b.to_host(), c.to_host()).to_soft()
+                } else {
+                    ((a * b).value + c).value
+                };
+                let res = this.adjust_nan(res, &[a, b, c]);
+                this.write_scalar(res, dest)?;
+            }
+            "fmuladdf64" => {
+                let [a, b, c] = check_arg_count(args)?;
+                let a = this.read_scalar(a)?.to_f64()?;
+                let b = this.read_scalar(b)?.to_f64()?;
+                let c = this.read_scalar(c)?.to_f64()?;
+                let fuse: bool = this.machine.rng.get_mut().gen();
+                let res = if fuse {
+                    // FIXME: Using host floats, to work around https://github.com/rust-lang/rustc_apfloat/issues/11
+                    a.to_host().mul_add(b.to_host(), c.to_host()).to_soft()
+                } else {
+                    ((a * b).value + c).value
+                };
+                let res = this.adjust_nan(res, &[a, b, c]);
+                this.write_scalar(res, dest)?;
+            }
+
             "powf32" => {
                 let [f1, f2] = check_arg_count(args)?;
                 let f1 = this.read_scalar(f1)?.to_f32()?;
diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs
index 330147c8f1c..f089d1e1bcc 100644
--- a/src/tools/miri/src/lib.rs
+++ b/src/tools/miri/src/lib.rs
@@ -1,6 +1,5 @@
 #![feature(rustc_private)]
 #![feature(cell_update)]
-#![feature(const_option)]
 #![feature(float_gamma)]
 #![feature(map_try_insert)]
 #![feature(never_type)]
diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs
index 0d28a4ed3e4..af0ca00a0c0 100644
--- a/src/tools/miri/src/machine.rs
+++ b/src/tools/miri/src/machine.rs
@@ -321,7 +321,7 @@ impl ProvenanceExtra {
 }
 
 /// Extra per-allocation data
-#[derive(Debug, Clone)]
+#[derive(Debug)]
 pub struct AllocExtra<'tcx> {
     /// Global state of the borrow tracker, if enabled.
     pub borrow_tracker: Option<borrow_tracker::AllocState>,
@@ -338,6 +338,14 @@ pub struct AllocExtra<'tcx> {
     pub backtrace: Option<Vec<FrameInfo<'tcx>>>,
 }
 
+// We need a `Clone` impl because the machine passes `Allocation` through `Cow`...
+// but that should never end up actually cloning our `AllocExtra`.
+impl<'tcx> Clone for AllocExtra<'tcx> {
+    fn clone(&self) -> Self {
+        panic!("our allocations should never be cloned");
+    }
+}
+
 impl VisitProvenance for AllocExtra<'_> {
     fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
         let AllocExtra { borrow_tracker, data_race, weak_memory, backtrace: _ } = self;
diff --git a/src/tools/miri/tests/pass/float.rs b/src/tools/miri/tests/pass/float.rs
index 6ab18a5345e..853d3e80517 100644
--- a/src/tools/miri/tests/pass/float.rs
+++ b/src/tools/miri/tests/pass/float.rs
@@ -30,6 +30,7 @@ fn main() {
     libm();
     test_fast();
     test_algebraic();
+    test_fmuladd();
 }
 
 trait Float: Copy + PartialEq + Debug {
@@ -1041,3 +1042,20 @@ fn test_algebraic() {
     test_operations_f32(11., 2.);
     test_operations_f32(10., 15.);
 }
+
+fn test_fmuladd() {
+    use std::intrinsics::{fmuladdf32, fmuladdf64};
+
+    #[inline(never)]
+    pub fn test_operations_f32(a: f32, b: f32, c: f32) {
+        assert_approx_eq!(unsafe { fmuladdf32(a, b, c) }, a * b + c);
+    }
+
+    #[inline(never)]
+    pub fn test_operations_f64(a: f64, b: f64, c: f64) {
+        assert_approx_eq!(unsafe { fmuladdf64(a, b, c) }, a * b + c);
+    }
+
+    test_operations_f32(0.1, 0.2, 0.3);
+    test_operations_f64(1.1, 1.2, 1.3);
+}
diff --git a/src/tools/miri/tests/pass/intrinsics/fmuladd_nondeterministic.rs b/src/tools/miri/tests/pass/intrinsics/fmuladd_nondeterministic.rs
new file mode 100644
index 00000000000..b46cf1ddf65
--- /dev/null
+++ b/src/tools/miri/tests/pass/intrinsics/fmuladd_nondeterministic.rs
@@ -0,0 +1,44 @@
+#![feature(core_intrinsics)]
+use std::intrinsics::{fmuladdf32, fmuladdf64};
+
+fn main() {
+    let mut saw_zero = false;
+    let mut saw_nonzero = false;
+    for _ in 0..50 {
+        let a = std::hint::black_box(0.1_f64);
+        let b = std::hint::black_box(0.2);
+        let c = std::hint::black_box(-a * b);
+        // It is unspecified whether the following operation is fused or not. The
+        // following evaluates to 0.0 if unfused, and nonzero (-1.66e-18) if fused.
+        let x = unsafe { fmuladdf64(a, b, c) };
+        if x == 0.0 {
+            saw_zero = true;
+        } else {
+            saw_nonzero = true;
+        }
+    }
+    assert!(
+        saw_zero && saw_nonzero,
+        "`fmuladdf64` failed to be evaluated as both fused and unfused"
+    );
+
+    let mut saw_zero = false;
+    let mut saw_nonzero = false;
+    for _ in 0..50 {
+        let a = std::hint::black_box(0.1_f32);
+        let b = std::hint::black_box(0.2);
+        let c = std::hint::black_box(-a * b);
+        // It is unspecified whether the following operation is fused or not. The
+        // following evaluates to 0.0 if unfused, and nonzero (-8.1956386e-10) if fused.
+        let x = unsafe { fmuladdf32(a, b, c) };
+        if x == 0.0 {
+            saw_zero = true;
+        } else {
+            saw_nonzero = true;
+        }
+    }
+    assert!(
+        saw_zero && saw_nonzero,
+        "`fmuladdf32` failed to be evaluated as both fused and unfused"
+    );
+}
diff --git a/src/tools/miri/tests/pass/underscore_pattern.rs b/src/tools/miri/tests/pass/underscore_pattern.rs
index f0afe558954..f59bb9f5c82 100644
--- a/src/tools/miri/tests/pass/underscore_pattern.rs
+++ b/src/tools/miri/tests/pass/underscore_pattern.rs
@@ -1,5 +1,7 @@
 // Various tests ensuring that underscore patterns really just construct the place, but don't check its contents.
 #![feature(strict_provenance)]
+#![feature(never_type)]
+
 use std::ptr;
 
 fn main() {
@@ -9,6 +11,7 @@ fn main() {
     invalid_let();
     dangling_let_type_annotation();
     invalid_let_type_annotation();
+    never();
 }
 
 fn dangling_match() {
@@ -34,6 +37,13 @@ fn invalid_match() {
             _ => {}
         }
     }
+
+    unsafe {
+        let x: Uninit<!> = Uninit { uninit: () };
+        match x.value {
+            _ => {}
+        }
+    }
 }
 
 fn dangling_let() {
@@ -41,6 +51,11 @@ fn dangling_let() {
         let ptr = ptr::without_provenance::<bool>(0x40);
         let _ = *ptr;
     }
+
+    unsafe {
+        let ptr = ptr::without_provenance::<!>(0x40);
+        let _ = *ptr;
+    }
 }
 
 fn invalid_let() {
@@ -49,6 +64,12 @@ fn invalid_let() {
         let ptr = ptr::addr_of!(val).cast::<bool>();
         let _ = *ptr;
     }
+
+    unsafe {
+        let val = 3u8;
+        let ptr = ptr::addr_of!(val).cast::<!>();
+        let _ = *ptr;
+    }
 }
 
 // Adding a type annotation used to change how MIR is generated, make sure we cover both cases.
@@ -57,6 +78,11 @@ fn dangling_let_type_annotation() {
         let ptr = ptr::without_provenance::<bool>(0x40);
         let _: bool = *ptr;
     }
+
+    unsafe {
+        let ptr = ptr::without_provenance::<!>(0x40);
+        let _: ! = *ptr;
+    }
 }
 
 fn invalid_let_type_annotation() {
@@ -65,7 +91,28 @@ fn invalid_let_type_annotation() {
         let ptr = ptr::addr_of!(val).cast::<bool>();
         let _: bool = *ptr;
     }
+
+    unsafe {
+        let val = 3u8;
+        let ptr = ptr::addr_of!(val).cast::<!>();
+        let _: ! = *ptr;
+    }
 }
 
-// FIXME: we should also test `!`, not just `bool` -- but that s currently buggy:
-// https://github.com/rust-lang/rust/issues/117288
+// Regression test from <https://github.com/rust-lang/rust/issues/117288>.
+fn never() {
+    unsafe {
+        let x = 3u8;
+        let x: *const ! = &x as *const u8 as *const _;
+        let _: ! = *x;
+    }
+
+    // Without a type annotation, make sure we don't implicitly coerce `!` to `()`
+    // when we do the noop `*x` (as that would require a `!` *value*, creating
+    // which is UB).
+    unsafe {
+        let x = 3u8;
+        let x: *const ! = &x as *const u8 as *const _;
+        let _ = *x;
+    }
+}
diff --git a/src/tools/opt-dist/src/tests.rs b/src/tools/opt-dist/src/tests.rs
index 82c393d34a6..e401554640c 100644
--- a/src/tools/opt-dist/src/tests.rs
+++ b/src/tools/opt-dist/src/tests.rs
@@ -96,7 +96,6 @@ llvm-config = "{llvm_config}"
         "tests/incremental",
         "tests/mir-opt",
         "tests/pretty",
-        "tests/run-pass-valgrind",
         "tests/ui",
         "tests/crashes",
     ];
diff --git a/src/tools/run-make-support/src/diff/tests.rs b/src/tools/run-make-support/src/diff/tests.rs
index 286548bef61..6096560ca52 100644
--- a/src/tools/run-make-support/src/diff/tests.rs
+++ b/src/tools/run-make-support/src/diff/tests.rs
@@ -1,28 +1,23 @@
-#[cfg(test)]
-mod tests {
-    use crate::*;
-
-    #[test]
-    fn test_diff() {
-        let expected = "foo\nbar\nbaz\n";
-        let actual = "foo\nbar\nbaz\n";
+use crate::diff;
+
+#[test]
+fn test_diff() {
+    let expected = "foo\nbar\nbaz\n";
+    let actual = "foo\nbar\nbaz\n";
+    diff().expected_text("EXPECTED_TEXT", expected).actual_text("ACTUAL_TEXT", actual).run();
+}
+
+#[test]
+fn test_should_panic() {
+    let expected = "foo\nbar\nbaz\n";
+    let actual = "foo\nbaz\nbar\n";
+
+    let output = std::panic::catch_unwind(|| {
         diff().expected_text("EXPECTED_TEXT", expected).actual_text("ACTUAL_TEXT", actual).run();
-    }
-
-    #[test]
-    fn test_should_panic() {
-        let expected = "foo\nbar\nbaz\n";
-        let actual = "foo\nbaz\nbar\n";
-
-        let output = std::panic::catch_unwind(|| {
-            diff()
-                .expected_text("EXPECTED_TEXT", expected)
-                .actual_text("ACTUAL_TEXT", actual)
-                .run();
-        })
-        .unwrap_err();
-
-        let expected_output = "\
+    })
+    .unwrap_err();
+
+    let expected_output = "\
 test failed: `EXPECTED_TEXT` is different from `ACTUAL_TEXT`
 
 --- EXPECTED_TEXT
@@ -34,28 +29,27 @@ test failed: `EXPECTED_TEXT` is different from `ACTUAL_TEXT`
 -baz
 ";
 
-        assert_eq!(output.downcast_ref::<String>().unwrap(), expected_output);
-    }
+    assert_eq!(output.downcast_ref::<String>().unwrap(), expected_output);
+}
 
-    #[test]
-    fn test_normalize() {
-        let expected = "
+#[test]
+fn test_normalize() {
+    let expected = "
 running 2 tests
 ..
 
 test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
 ";
-        let actual = "
+    let actual = "
 running 2 tests
 ..
 
 test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.02s
 ";
 
-        diff()
-            .expected_text("EXPECTED_TEXT", expected)
-            .actual_text("ACTUAL_TEXT", actual)
-            .normalize(r#"finished in \d+\.\d+s"#, "finished in $$TIME")
-            .run();
-    }
+    diff()
+        .expected_text("EXPECTED_TEXT", expected)
+        .actual_text("ACTUAL_TEXT", actual)
+        .normalize(r#"finished in \d+\.\d+s"#, "finished in $$TIME")
+        .run();
 }
diff --git a/src/tools/run-make-support/src/macros.rs b/src/tools/run-make-support/src/macros.rs
index f7fe4f54223..cc3d1281d0a 100644
--- a/src/tools/run-make-support/src/macros.rs
+++ b/src/tools/run-make-support/src/macros.rs
@@ -70,6 +70,30 @@ macro_rules! impl_common_helpers {
                 self
             }
 
+            /// Configuration for the child process’s standard input (stdin) handle.
+            ///
+            /// See [`std::process::Command::stdin`].
+            pub fn stdin<T: Into<::std::process::Stdio>>(&mut self, cfg: T) -> &mut Self {
+                self.cmd.stdin(cfg);
+                self
+            }
+
+            /// Configuration for the child process’s standard output (stdout) handle.
+            ///
+            /// See [`std::process::Command::stdout`].
+            pub fn stdout<T: Into<::std::process::Stdio>>(&mut self, cfg: T) -> &mut Self {
+                self.cmd.stdout(cfg);
+                self
+            }
+
+            /// Configuration for the child process’s standard error (stderr) handle.
+            ///
+            /// See [`std::process::Command::stderr`].
+            pub fn stderr<T: Into<::std::process::Stdio>>(&mut self, cfg: T) -> &mut Self {
+                self.cmd.stderr(cfg);
+                self
+            }
+
             /// Inspect what the underlying [`Command`] is up to the
             /// current construction.
             pub fn inspect<I>(&mut self, inspector: I) -> &mut Self
diff --git a/src/tools/rust-analyzer/.github/workflows/release.yaml b/src/tools/rust-analyzer/.github/workflows/release.yaml
index e11d6e15d10..39ac652de0f 100644
--- a/src/tools/rust-analyzer/.github/workflows/release.yaml
+++ b/src/tools/rust-analyzer/.github/workflows/release.yaml
@@ -16,7 +16,7 @@ env:
   RUSTFLAGS: "-D warnings -W unreachable-pub"
   RUSTUP_MAX_RETRIES: 10
   FETCH_DEPTH: 0 # pull in the tags for the version string
-  MACOSX_DEPLOYMENT_TARGET: 10.15
+  MACOSX_DEPLOYMENT_TARGET: 13.0
   CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER: aarch64-linux-gnu-gcc
   CARGO_TARGET_ARM_UNKNOWN_LINUX_GNUEABIHF_LINKER: arm-linux-gnueabihf-gcc
 
@@ -43,10 +43,10 @@ jobs:
           - os: ubuntu-20.04
             target: arm-unknown-linux-gnueabihf
             code-target: linux-armhf
-          - os: macos-12
+          - os: macos-13
             target: x86_64-apple-darwin
             code-target: darwin-x64
-          - os: macos-12
+          - os: macos-13
             target: aarch64-apple-darwin
             code-target: darwin-arm64
 
diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock
index dc820fcb28d..7891edc2447 100644
--- a/src/tools/rust-analyzer/Cargo.lock
+++ b/src/tools/rust-analyzer/Cargo.lock
@@ -145,9 +145,12 @@ dependencies = [
 
 [[package]]
 name = "cc"
-version = "1.1.10"
+version = "1.1.22"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e9e8aabfac534be767c909e0690571677d49f41bd8465ae876fe043d52ba5292"
+checksum = "9540e661f81799159abee814118cc139a2004b3a3aa3ea37724a1b66530b90e0"
+dependencies = [
+ "shlex",
+]
 
 [[package]]
 name = "cfg"
@@ -1853,6 +1856,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "shlex"
+version = "1.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
+
+[[package]]
 name = "smallvec"
 version = "1.13.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml
index b4587a37961..0b3d6e2a1ef 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.80"
+rust-version = "1.81"
 edition = "2021"
 license = "MIT OR Apache-2.0"
 authors = ["rust-analyzer team"]
diff --git a/src/tools/rust-analyzer/crates/cfg/src/lib.rs b/src/tools/rust-analyzer/crates/cfg/src/lib.rs
index e9daaf7de3c..c2d40086056 100644
--- a/src/tools/rust-analyzer/crates/cfg/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/cfg/src/lib.rs
@@ -49,6 +49,10 @@ impl CfgOptions {
         cfg.fold(&|atom| self.enabled.contains(atom))
     }
 
+    pub fn check_atom(&self, cfg: &CfgAtom) -> bool {
+        self.enabled.contains(cfg)
+    }
+
     pub fn insert_atom(&mut self, key: Symbol) {
         self.enabled.insert(CfgAtom::Flag(key));
     }
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 85fb90fdfb6..d568f6faa72 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
@@ -36,7 +36,7 @@ macro_rules! f {
 }
 
 struct#0:1@58..64#1# MyTraitMap2#0:2@31..42#0# {#0:1@72..73#1#
-    map#0:1@86..89#1#:#0:1@89..90#1# #0:1@89..90#1#::#0:1@91..92#1#std#0:1@93..96#1#::#0:1@96..97#1#collections#0:1@98..109#1#::#0:1@109..110#1#HashSet#0:1@111..118#1#<#0:1@118..119#1#(#0:1@119..120#1#)#0:1@120..121#1#>#0:1@121..122#1#,#0:1@122..123#1#
+    map#0:1@86..89#1#:#0:1@89..90#1# #0:1@89..90#1#::#0:1@91..93#1#std#0:1@93..96#1#::#0:1@96..98#1#collections#0:1@98..109#1#::#0:1@109..111#1#HashSet#0:1@111..118#1#<#0:1@118..119#1#(#0:1@119..120#1#)#0:1@120..121#1#>#0:1@121..122#1#,#0:1@122..123#1#
 }#0:1@132..133#1#
 "#]],
     );
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
index e09ef4f205d..7f1d19719da 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/collector.rs
@@ -6,7 +6,7 @@
 use std::{cmp::Ordering, iter, mem, ops::Not};
 
 use base_db::{CrateId, CrateOrigin, Dependency, LangCrateOrigin};
-use cfg::{CfgExpr, CfgOptions};
+use cfg::{CfgAtom, CfgExpr, CfgOptions};
 use either::Either;
 use hir_expand::{
     attrs::{Attr, AttrId},
@@ -1324,13 +1324,21 @@ impl DefCollector<'_> {
                     };
 
                     // Skip #[test]/#[bench] expansion, which would merely result in more memory usage
-                    // due to duplicating functions into macro expansions
+                    // due to duplicating functions into macro expansions, but only if `cfg(test)` is active,
+                    // otherwise they are expanded to nothing and this can impact e.g. diagnostics (due to things
+                    // being cfg'ed out).
+                    // Ideally we will just expand them to nothing here. But we are only collecting macro calls,
+                    // not expanding them, so we have no way to do that.
                     if matches!(
                         def.kind,
                         MacroDefKind::BuiltInAttr(_, expander)
                         if expander.is_test() || expander.is_bench()
                     ) {
-                        return recollect_without(self);
+                        let test_is_active =
+                            self.cfg_options.check_atom(&CfgAtom::Flag(sym::test.clone()));
+                        if test_is_active {
+                            return recollect_without(self);
+                        }
                     }
 
                     let call_id = || {
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs
index b9afc666f75..2a8691b461c 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs
@@ -4,6 +4,8 @@ use span::{MacroCallId, Span};
 
 use crate::{db::ExpandDatabase, name, tt, ExpandResult, MacroCallKind};
 
+use super::quote;
+
 macro_rules! register_builtin {
     ($(($name:ident, $variant:ident) => $expand:ident),* ) => {
         #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@@ -52,15 +54,15 @@ impl BuiltinAttrExpander {
 }
 
 register_builtin! {
-    (bench, Bench) => dummy_attr_expand,
+    (bench, Bench) => dummy_gate_test_expand,
     (cfg_accessible, CfgAccessible) => dummy_attr_expand,
     (cfg_eval, CfgEval) => dummy_attr_expand,
     (derive, Derive) => derive_expand,
     // derive const is equivalent to derive for our proposes.
     (derive_const, DeriveConst) => derive_expand,
     (global_allocator, GlobalAllocator) => dummy_attr_expand,
-    (test, Test) => dummy_attr_expand,
-    (test_case, TestCase) => dummy_attr_expand
+    (test, Test) => dummy_gate_test_expand,
+    (test_case, TestCase) => dummy_gate_test_expand
 }
 
 pub fn find_builtin_attr(ident: &name::Name) -> Option<BuiltinAttrExpander> {
@@ -76,6 +78,19 @@ fn dummy_attr_expand(
     ExpandResult::ok(tt.clone())
 }
 
+fn dummy_gate_test_expand(
+    _db: &dyn ExpandDatabase,
+    _id: MacroCallId,
+    tt: &tt::Subtree,
+    span: Span,
+) -> ExpandResult<tt::Subtree> {
+    let result = quote::quote! { span=>
+        #[cfg(test)]
+        #tt
+    };
+    ExpandResult::ok(result)
+}
+
 /// We generate a very specific expansion here, as we do not actually expand the `#[derive]` attribute
 /// itself in name res, but we do want to expand it to something for the IDE layer, so that the input
 /// derive attributes can be downmapped, and resolved as proper paths.
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
index 584f9631e34..484a8662eb1 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/db.rs
@@ -16,7 +16,10 @@ use crate::{
     cfg_process,
     declarative::DeclarativeMacroExpander,
     fixup::{self, SyntaxFixupUndoInfo},
-    hygiene::{span_with_call_site_ctxt, span_with_def_site_ctxt, span_with_mixed_site_ctxt},
+    hygiene::{
+        span_with_call_site_ctxt, span_with_def_site_ctxt, span_with_mixed_site_ctxt,
+        SyntaxContextExt as _,
+    },
     proc_macro::ProcMacros,
     span_map::{RealSpanMap, SpanMap, SpanMapRef},
     tt, AstId, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander,
@@ -300,14 +303,16 @@ pub fn expand_speculative(
         token_tree_to_syntax_node(&speculative_expansion.value, expand_to, loc.def.edition);
 
     let syntax_node = node.syntax_node();
-    let token = rev_tmap
+    let (token, _) = rev_tmap
         .ranges_with_span(span_map.span_for_range(token_to_map.text_range()))
-        .filter_map(|range| syntax_node.covering_element(range).into_token())
-        .min_by_key(|t| {
-            // prefer tokens of the same kind and text
+        .filter_map(|(range, ctx)| syntax_node.covering_element(range).into_token().zip(Some(ctx)))
+        .min_by_key(|(t, ctx)| {
+            // prefer tokens of the same kind and text, as well as non opaque marked ones
             // Note the inversion of the score here, as we want to prefer the first token in case
             // of all tokens having the same score
-            (t.kind() != token_to_map.kind()) as u8 + 2 * ((t.text() != token_to_map.text()) as u8)
+            ctx.is_opaque(db) as u8
+                + 2 * (t.kind() != token_to_map.kind()) as u8
+                + 4 * ((t.text() != token_to_map.text()) as u8)
         })?;
     Some((node.syntax_node(), token))
 }
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs b/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs
index cc02332207d..5e1448f7950 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/hygiene.rs
@@ -151,6 +151,7 @@ pub trait SyntaxContextExt {
     fn remove_mark(&mut self, db: &dyn ExpandDatabase) -> (Option<MacroCallId>, Transparency);
     fn outer_mark(self, db: &dyn ExpandDatabase) -> (Option<MacroCallId>, Transparency);
     fn marks(self, db: &dyn ExpandDatabase) -> Vec<(MacroCallId, Transparency)>;
+    fn is_opaque(self, db: &dyn ExpandDatabase) -> bool;
 }
 
 impl SyntaxContextExt for SyntaxContextId {
@@ -177,6 +178,9 @@ impl SyntaxContextExt for SyntaxContextId {
         marks.reverse();
         marks
     }
+    fn is_opaque(self, db: &dyn ExpandDatabase) -> bool {
+        !self.is_root() && db.lookup_intern_syntax_context(self).outer_transparency.is_opaque()
+    }
 }
 
 // FIXME: Make this a SyntaxContextExt method once we have RPIT
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
index 95380979492..56cb5fd375c 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/lib.rs
@@ -25,6 +25,7 @@ mod prettify_macro_expansion_;
 
 use attrs::collect_attrs;
 use rustc_hash::FxHashMap;
+use stdx::TupleExt;
 use triomphe::Arc;
 
 use std::hash::Hash;
@@ -772,14 +773,15 @@ impl ExpansionInfo {
     /// Maps the passed in file range down into a macro expansion if it is the input to a macro call.
     ///
     /// Note this does a linear search through the entire backing vector of the spanmap.
+    // FIXME: Consider adding a reverse map to ExpansionInfo to get rid of the linear search which
+    // potentially results in quadratic look ups (notably this might improve semantic highlighting perf)
     pub fn map_range_down_exact(
         &self,
         span: Span,
-    ) -> Option<InMacroFile<impl Iterator<Item = SyntaxToken> + '_>> {
-        let tokens = self
-            .exp_map
-            .ranges_with_span_exact(span)
-            .flat_map(move |range| self.expanded.value.covering_element(range).into_token());
+    ) -> Option<InMacroFile<impl Iterator<Item = (SyntaxToken, SyntaxContextId)> + '_>> {
+        let tokens = self.exp_map.ranges_with_span_exact(span).flat_map(move |(range, ctx)| {
+            self.expanded.value.covering_element(range).into_token().zip(Some(ctx))
+        });
 
         Some(InMacroFile::new(self.expanded.file_id, tokens))
     }
@@ -791,11 +793,10 @@ impl ExpansionInfo {
     pub fn map_range_down(
         &self,
         span: Span,
-    ) -> Option<InMacroFile<impl Iterator<Item = SyntaxToken> + '_>> {
-        let tokens = self
-            .exp_map
-            .ranges_with_span(span)
-            .flat_map(move |range| self.expanded.value.covering_element(range).into_token());
+    ) -> Option<InMacroFile<impl Iterator<Item = (SyntaxToken, SyntaxContextId)> + '_>> {
+        let tokens = self.exp_map.ranges_with_span(span).flat_map(move |(range, ctx)| {
+            self.expanded.value.covering_element(range).into_token().zip(Some(ctx))
+        });
 
         Some(InMacroFile::new(self.expanded.file_id, tokens))
     }
@@ -845,7 +846,8 @@ impl ExpansionInfo {
                     self.arg.file_id,
                     arg_map
                         .ranges_with_span_exact(span)
-                        .filter(|range| range.intersect(arg_range).is_some())
+                        .filter(|(range, _)| range.intersect(arg_range).is_some())
+                        .map(TupleExt::head)
                         .collect(),
                 )
             }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs
index e74e3d78988..f7bacbd49b3 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/chalk_db.rs
@@ -382,8 +382,9 @@ impl chalk_solve::RustIrDatabase<Interner> for ChalkContext<'_> {
     }
 
     fn is_object_safe(&self, trait_id: chalk_ir::TraitId<Interner>) -> bool {
+        // FIXME: When cargo is updated, change to dyn_compatibility
         let trait_ = from_chalk_trait_id(trait_id);
-        crate::object_safety::object_safety(self.db, trait_).is_none()
+        crate::dyn_compatibility::dyn_compatibility(self.db, trait_).is_none()
     }
 
     fn closure_kind(
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 ce5a821ea2b..5620d80adb5 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/db.rs
@@ -20,11 +20,11 @@ use triomphe::Arc;
 use crate::{
     chalk_db,
     consteval::ConstEvalError,
+    dyn_compatibility::DynCompatibilityViolation,
     layout::{Layout, LayoutError},
     lower::{GenericDefaults, GenericPredicates},
     method_resolution::{InherentImpls, TraitImpls, TyFingerprint},
     mir::{BorrowckResult, MirBody, MirLowerError},
-    object_safety::ObjectSafetyViolation,
     Binders, ClosureId, Const, FnDefId, ImplTraitId, ImplTraits, InferenceResult, Interner,
     PolyFnSig, Substitution, TraitEnvironment, TraitRef, Ty, TyDefId, ValueTyDefId,
 };
@@ -108,8 +108,8 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
     #[salsa::invoke(crate::layout::target_data_layout_query)]
     fn target_data_layout(&self, krate: CrateId) -> Result<Arc<TargetDataLayout>, Arc<str>>;
 
-    #[salsa::invoke(crate::object_safety::object_safety_of_trait_query)]
-    fn object_safety_of_trait(&self, trait_: TraitId) -> Option<ObjectSafetyViolation>;
+    #[salsa::invoke(crate::dyn_compatibility::dyn_compatibility_of_trait_query)]
+    fn dyn_compatibility_of_trait(&self, trait_: TraitId) -> Option<DynCompatibilityViolation>;
 
     #[salsa::invoke(crate::lower::ty_query)]
     #[salsa::cycle(crate::lower::ty_recover)]
@@ -280,8 +280,8 @@ pub trait HirDatabase: DefDatabase + Upcast<dyn DefDatabase> {
 }
 
 #[test]
-fn hir_database_is_object_safe() {
-    fn _assert_object_safe(_: &dyn HirDatabase) {}
+fn hir_database_is_dyn_compatible() {
+    fn _assert_dyn_compatible(_: &dyn HirDatabase) {}
 }
 
 #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
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 82517e69917..7f6b7e392b3 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
@@ -58,7 +58,7 @@ impl fmt::Display for CaseType {
         let repr = match self {
             CaseType::LowerSnakeCase => "snake_case",
             CaseType::UpperSnakeCase => "UPPER_SNAKE_CASE",
-            CaseType::UpperCamelCase => "CamelCase",
+            CaseType::UpperCamelCase => "UpperCamelCase",
         };
 
         repr.fmt(f)
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check/case_conv.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check/case_conv.rs
index cbe1af15703..aa0c9e30be1 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check/case_conv.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/decl_check/case_conv.rs
@@ -111,7 +111,7 @@ mod tests {
         check(to_lower_snake_case, "lower_snake_case", expect![[""]]);
         check(to_lower_snake_case, "UPPER_SNAKE_CASE", expect![["upper_snake_case"]]);
         check(to_lower_snake_case, "Weird_Case", expect![["weird_case"]]);
-        check(to_lower_snake_case, "CamelCase", expect![["camel_case"]]);
+        check(to_lower_snake_case, "UpperCamelCase", expect![["upper_camel_case"]]);
         check(to_lower_snake_case, "lowerCamelCase", expect![["lower_camel_case"]]);
         check(to_lower_snake_case, "a", expect![[""]]);
         check(to_lower_snake_case, "abc", expect![[""]]);
@@ -121,8 +121,8 @@ mod tests {
 
     #[test]
     fn test_to_camel_case() {
-        check(to_camel_case, "CamelCase", expect![[""]]);
-        check(to_camel_case, "CamelCase_", expect![[""]]);
+        check(to_camel_case, "UpperCamelCase", expect![[""]]);
+        check(to_camel_case, "UpperCamelCase_", expect![[""]]);
         check(to_camel_case, "_CamelCase", expect![[""]]);
         check(to_camel_case, "lowerCamelCase", expect![["LowerCamelCase"]]);
         check(to_camel_case, "lower_snake_case", expect![["LowerSnakeCase"]]);
@@ -143,7 +143,7 @@ mod tests {
         check(to_upper_snake_case, "UPPER_SNAKE_CASE", expect![[""]]);
         check(to_upper_snake_case, "lower_snake_case", expect![["LOWER_SNAKE_CASE"]]);
         check(to_upper_snake_case, "Weird_Case", expect![["WEIRD_CASE"]]);
-        check(to_upper_snake_case, "CamelCase", expect![["CAMEL_CASE"]]);
+        check(to_upper_snake_case, "UpperCamelCase", expect![["UPPER_CAMEL_CASE"]]);
         check(to_upper_snake_case, "lowerCamelCase", expect![["LOWER_CAMEL_CASE"]]);
         check(to_upper_snake_case, "A", expect![[""]]);
         check(to_upper_snake_case, "ABC", expect![[""]]);
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/object_safety.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs
index a4c66268555..e0d1758210e 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/object_safety.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility.rs
@@ -1,4 +1,4 @@
-//! Compute the object-safety of a trait
+//! Compute the dyn-compatibility of a trait
 
 use std::ops::ControlFlow;
 
@@ -28,14 +28,14 @@ use crate::{
 };
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub enum ObjectSafetyViolation {
+pub enum DynCompatibilityViolation {
     SizedSelf,
     SelfReferential,
     Method(FunctionId, MethodViolationCode),
     AssocConst(ConstId),
     GAT(TypeAliasId),
     // This doesn't exist in rustc, but added for better visualization
-    HasNonSafeSuperTrait(TraitId),
+    HasNonCompatibleSuperTrait(TraitId),
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -50,70 +50,73 @@ pub enum MethodViolationCode {
     UndispatchableReceiver,
 }
 
-pub fn object_safety(db: &dyn HirDatabase, trait_: TraitId) -> Option<ObjectSafetyViolation> {
+pub fn dyn_compatibility(
+    db: &dyn HirDatabase,
+    trait_: TraitId,
+) -> Option<DynCompatibilityViolation> {
     for super_trait in all_super_traits(db.upcast(), trait_).into_iter().skip(1).rev() {
-        if db.object_safety_of_trait(super_trait).is_some() {
-            return Some(ObjectSafetyViolation::HasNonSafeSuperTrait(super_trait));
+        if db.dyn_compatibility_of_trait(super_trait).is_some() {
+            return Some(DynCompatibilityViolation::HasNonCompatibleSuperTrait(super_trait));
         }
     }
 
-    db.object_safety_of_trait(trait_)
+    db.dyn_compatibility_of_trait(trait_)
 }
 
-pub fn object_safety_with_callback<F>(
+pub fn dyn_compatibility_with_callback<F>(
     db: &dyn HirDatabase,
     trait_: TraitId,
     cb: &mut F,
 ) -> ControlFlow<()>
 where
-    F: FnMut(ObjectSafetyViolation) -> ControlFlow<()>,
+    F: FnMut(DynCompatibilityViolation) -> ControlFlow<()>,
 {
     for super_trait in all_super_traits(db.upcast(), trait_).into_iter().skip(1).rev() {
-        if db.object_safety_of_trait(super_trait).is_some() {
-            cb(ObjectSafetyViolation::HasNonSafeSuperTrait(trait_))?;
+        if db.dyn_compatibility_of_trait(super_trait).is_some() {
+            cb(DynCompatibilityViolation::HasNonCompatibleSuperTrait(trait_))?;
         }
     }
 
-    object_safety_of_trait_with_callback(db, trait_, cb)
+    dyn_compatibility_of_trait_with_callback(db, trait_, cb)
 }
 
-pub fn object_safety_of_trait_with_callback<F>(
+pub fn dyn_compatibility_of_trait_with_callback<F>(
     db: &dyn HirDatabase,
     trait_: TraitId,
     cb: &mut F,
 ) -> ControlFlow<()>
 where
-    F: FnMut(ObjectSafetyViolation) -> ControlFlow<()>,
+    F: FnMut(DynCompatibilityViolation) -> ControlFlow<()>,
 {
     // Check whether this has a `Sized` bound
     if generics_require_sized_self(db, trait_.into()) {
-        cb(ObjectSafetyViolation::SizedSelf)?;
+        cb(DynCompatibilityViolation::SizedSelf)?;
     }
 
     // Check if there exist bounds that referencing self
     if predicates_reference_self(db, trait_) {
-        cb(ObjectSafetyViolation::SelfReferential)?;
+        cb(DynCompatibilityViolation::SelfReferential)?;
     }
     if bounds_reference_self(db, trait_) {
-        cb(ObjectSafetyViolation::SelfReferential)?;
+        cb(DynCompatibilityViolation::SelfReferential)?;
     }
 
     // rustc checks for non-lifetime binders here, but we don't support HRTB yet
 
     let trait_data = db.trait_data(trait_);
     for (_, assoc_item) in &trait_data.items {
-        object_safety_violation_for_assoc_item(db, trait_, *assoc_item, cb)?;
+        dyn_compatibility_violation_for_assoc_item(db, trait_, *assoc_item, cb)?;
     }
 
     ControlFlow::Continue(())
 }
 
-pub fn object_safety_of_trait_query(
+pub fn dyn_compatibility_of_trait_query(
     db: &dyn HirDatabase,
     trait_: TraitId,
-) -> Option<ObjectSafetyViolation> {
+) -> Option<DynCompatibilityViolation> {
     let mut res = None;
-    object_safety_of_trait_with_callback(db, trait_, &mut |osv| {
+    dyn_compatibility_of_trait_with_callback(db, trait_, &mut |osv| {
         res = Some(osv);
         ControlFlow::Break(())
     });
@@ -321,14 +324,14 @@ fn contains_illegal_self_type_reference<T: TypeVisitable<Interner>>(
     t.visit_with(visitor.as_dyn(), outer_binder).is_break()
 }
 
-fn object_safety_violation_for_assoc_item<F>(
+fn dyn_compatibility_violation_for_assoc_item<F>(
     db: &dyn HirDatabase,
     trait_: TraitId,
     item: AssocItemId,
     cb: &mut F,
 ) -> ControlFlow<()>
 where
-    F: FnMut(ObjectSafetyViolation) -> ControlFlow<()>,
+    F: FnMut(DynCompatibilityViolation) -> ControlFlow<()>,
 {
     // Any item that has a `Self : Sized` requisite is otherwise
     // exempt from the regulations.
@@ -337,10 +340,10 @@ where
     }
 
     match item {
-        AssocItemId::ConstId(it) => cb(ObjectSafetyViolation::AssocConst(it)),
+        AssocItemId::ConstId(it) => cb(DynCompatibilityViolation::AssocConst(it)),
         AssocItemId::FunctionId(it) => {
             virtual_call_violations_for_method(db, trait_, it, &mut |mvc| {
-                cb(ObjectSafetyViolation::Method(it, mvc))
+                cb(DynCompatibilityViolation::Method(it, mvc))
             })
         }
         AssocItemId::TypeAliasId(it) => {
@@ -350,7 +353,7 @@ where
             } else {
                 let generic_params = db.generic_params(item.into());
                 if !generic_params.is_empty() {
-                    cb(ObjectSafetyViolation::GAT(it))
+                    cb(DynCompatibilityViolation::GAT(it))
                 } else {
                     ControlFlow::Continue(())
                 }
@@ -469,7 +472,7 @@ fn receiver_is_dispatchable(
         return false;
     };
 
-    // `self: Self` can't be dispatched on, but this is already considered object safe.
+    // `self: Self` can't be dispatched on, but this is already considered dyn compatible
     // See rustc's comment on https://github.com/rust-lang/rust/blob/3f121b9461cce02a703a0e7e450568849dfaa074/compiler/rustc_trait_selection/src/traits/object_safety.rs#L433-L437
     if sig
         .skip_binders()
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/object_safety/tests.rs b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility/tests.rs
index c2a9117c5be..3f3e68eeb1c 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/object_safety/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/dyn_compatibility/tests.rs
@@ -5,29 +5,29 @@ use rustc_hash::{FxHashMap, FxHashSet};
 use syntax::ToSmolStr;
 use test_fixture::WithFixture;
 
-use crate::{object_safety::object_safety_with_callback, test_db::TestDB};
+use crate::{dyn_compatibility::dyn_compatibility_with_callback, test_db::TestDB};
 
 use super::{
+    DynCompatibilityViolation,
     MethodViolationCode::{self, *},
-    ObjectSafetyViolation,
 };
 
-use ObjectSafetyViolationKind::*;
+use DynCompatibilityViolationKind::*;
 
 #[allow(clippy::upper_case_acronyms)]
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
-enum ObjectSafetyViolationKind {
+enum DynCompatibilityViolationKind {
     SizedSelf,
     SelfReferential,
     Method(MethodViolationCode),
     AssocConst,
     GAT,
-    HasNonSafeSuperTrait,
+    HasNonCompatibleSuperTrait,
 }
 
-fn check_object_safety<'a>(
+fn check_dyn_compatibility<'a>(
     ra_fixture: &str,
-    expected: impl IntoIterator<Item = (&'a str, Vec<ObjectSafetyViolationKind>)>,
+    expected: impl IntoIterator<Item = (&'a str, Vec<DynCompatibilityViolationKind>)>,
 ) {
     let mut expected: FxHashMap<_, _> =
         expected.into_iter().map(|(id, osvs)| (id, FxHashSet::from_iter(osvs))).collect();
@@ -53,18 +53,20 @@ fn check_object_safety<'a>(
             continue;
         };
         let mut osvs = FxHashSet::default();
-        object_safety_with_callback(&db, trait_id, &mut |osv| {
+        dyn_compatibility_with_callback(&db, trait_id, &mut |osv| {
             osvs.insert(match osv {
-                ObjectSafetyViolation::SizedSelf => SizedSelf,
-                ObjectSafetyViolation::SelfReferential => SelfReferential,
-                ObjectSafetyViolation::Method(_, mvc) => Method(mvc),
-                ObjectSafetyViolation::AssocConst(_) => AssocConst,
-                ObjectSafetyViolation::GAT(_) => GAT,
-                ObjectSafetyViolation::HasNonSafeSuperTrait(_) => HasNonSafeSuperTrait,
+                DynCompatibilityViolation::SizedSelf => SizedSelf,
+                DynCompatibilityViolation::SelfReferential => SelfReferential,
+                DynCompatibilityViolation::Method(_, mvc) => Method(mvc),
+                DynCompatibilityViolation::AssocConst(_) => AssocConst,
+                DynCompatibilityViolation::GAT(_) => GAT,
+                DynCompatibilityViolation::HasNonCompatibleSuperTrait(_) => {
+                    HasNonCompatibleSuperTrait
+                }
             });
             ControlFlow::Continue(())
         });
-        assert_eq!(osvs, expected, "Object safety violations for `{name}` do not match;");
+        assert_eq!(osvs, expected, "Dyn Compatibility violations for `{name}` do not match;");
     }
 
     let remains: Vec<_> = expected.keys().collect();
@@ -73,7 +75,7 @@ fn check_object_safety<'a>(
 
 #[test]
 fn item_bounds_can_reference_self() {
-    check_object_safety(
+    check_dyn_compatibility(
         r#"
 //- minicore: eq
 pub trait Foo {
@@ -88,7 +90,7 @@ pub trait Foo {
 
 #[test]
 fn associated_consts() {
-    check_object_safety(
+    check_dyn_compatibility(
         r#"
 trait Bar {
     const X: usize;
@@ -100,7 +102,7 @@ trait Bar {
 
 #[test]
 fn bounds_reference_self() {
-    check_object_safety(
+    check_dyn_compatibility(
         r#"
 //- minicore: eq
 trait X {
@@ -113,7 +115,7 @@ trait X {
 
 #[test]
 fn by_value_self() {
-    check_object_safety(
+    check_dyn_compatibility(
         r#"
 //- minicore: dispatch_from_dyn
 trait Bar {
@@ -135,7 +137,7 @@ trait Quux {
 
 #[test]
 fn generic_methods() {
-    check_object_safety(
+    check_dyn_compatibility(
         r#"
 //- minicore: dispatch_from_dyn
 trait Bar {
@@ -157,7 +159,7 @@ trait Qax {
 
 #[test]
 fn mentions_self() {
-    check_object_safety(
+    check_dyn_compatibility(
         r#"
 //- minicore: dispatch_from_dyn
 trait Bar {
@@ -182,7 +184,7 @@ trait Quux {
 
 #[test]
 fn no_static() {
-    check_object_safety(
+    check_dyn_compatibility(
         r#"
 //- minicore: dispatch_from_dyn
 trait Foo {
@@ -195,7 +197,7 @@ trait Foo {
 
 #[test]
 fn sized_self() {
-    check_object_safety(
+    check_dyn_compatibility(
         r#"
 //- minicore: dispatch_from_dyn
 trait Bar: Sized {
@@ -205,7 +207,7 @@ trait Bar: Sized {
         [("Bar", vec![SizedSelf])],
     );
 
-    check_object_safety(
+    check_dyn_compatibility(
         r#"
 //- minicore: dispatch_from_dyn
 trait Bar
@@ -220,7 +222,7 @@ trait Bar
 
 #[test]
 fn supertrait_gat() {
-    check_object_safety(
+    check_dyn_compatibility(
         r#"
 //- minicore: dispatch_from_dyn
 trait GatTrait {
@@ -229,13 +231,13 @@ trait GatTrait {
 
 trait SuperTrait<T>: GatTrait {}
 "#,
-        [("GatTrait", vec![GAT]), ("SuperTrait", vec![HasNonSafeSuperTrait])],
+        [("GatTrait", vec![GAT]), ("SuperTrait", vec![HasNonCompatibleSuperTrait])],
     );
 }
 
 #[test]
 fn supertrait_mentions_self() {
-    check_object_safety(
+    check_dyn_compatibility(
         r#"
 //- minicore: dispatch_from_dyn
 trait Bar<T> {
@@ -251,7 +253,7 @@ trait Baz : Bar<Self> {
 
 #[test]
 fn rustc_issue_19538() {
-    check_object_safety(
+    check_dyn_compatibility(
         r#"
 //- minicore: dispatch_from_dyn
 trait Foo {
@@ -260,13 +262,13 @@ trait Foo {
 
 trait Bar: Foo {}
 "#,
-        [("Foo", vec![Method(Generic)]), ("Bar", vec![HasNonSafeSuperTrait])],
+        [("Foo", vec![Method(Generic)]), ("Bar", vec![HasNonCompatibleSuperTrait])],
     );
 }
 
 #[test]
 fn rustc_issue_22040() {
-    check_object_safety(
+    check_dyn_compatibility(
         r#"
 //- minicore: fmt, eq, dispatch_from_dyn
 use core::fmt::Debug;
@@ -281,7 +283,7 @@ trait Expr: Debug + PartialEq {
 
 #[test]
 fn rustc_issue_102762() {
-    check_object_safety(
+    check_dyn_compatibility(
         r#"
 //- minicore: future, send, sync, dispatch_from_dyn, deref
 use core::pin::Pin;
@@ -313,7 +315,7 @@ pub trait Fetcher: Send + Sync {
 
 #[test]
 fn rustc_issue_102933() {
-    check_object_safety(
+    check_dyn_compatibility(
         r#"
 //- minicore: future, dispatch_from_dyn, deref
 use core::future::Future;
@@ -351,7 +353,7 @@ pub trait B2: Service<Response = i32> + B1 {
 
 #[test]
 fn rustc_issue_106247() {
-    check_object_safety(
+    check_dyn_compatibility(
         r#"
 //- minicore: sync, dispatch_from_dyn
 pub trait Trait {
@@ -363,8 +365,8 @@ pub trait Trait {
 }
 
 #[test]
-fn std_error_is_object_safe() {
-    check_object_safety(
+fn std_error_is_dyn_compatible() {
+    check_dyn_compatibility(
         r#"
 //- minicore: fmt, dispatch_from_dyn
 trait Erased<'a>: 'a {}
@@ -380,14 +382,14 @@ pub trait Error: core::fmt::Debug + core::fmt::Display {
 }
 
 #[test]
-fn lifetime_gat_is_object_unsafe() {
-    check_object_safety(
+fn lifetime_gat_is_dyn_incompatible() {
+    check_dyn_compatibility(
         r#"
 //- minicore: dispatch_from_dyn
 trait Foo {
     type Bar<'a>;
 }
 "#,
-        [("Foo", vec![ObjectSafetyViolationKind::GAT])],
+        [("Foo", vec![DynCompatibilityViolationKind::GAT])],
     );
 }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
index 5ed41b99ba3..ef570a20556 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lib.rs
@@ -38,11 +38,11 @@ pub mod consteval;
 pub mod db;
 pub mod diagnostics;
 pub mod display;
+pub mod dyn_compatibility;
 pub mod lang_items;
 pub mod layout;
 pub mod method_resolution;
 pub mod mir;
-pub mod object_safety;
 pub mod primitive;
 pub mod traits;
 
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs
index 878d584a4ef..9830fa1ca7b 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/mir/borrowck.rs
@@ -386,82 +386,91 @@ fn ever_initialized_map(
     fn dfs(
         db: &dyn HirDatabase,
         body: &MirBody,
-        b: BasicBlockId,
         l: LocalId,
+        stack: &mut Vec<BasicBlockId>,
         result: &mut ArenaMap<BasicBlockId, ArenaMap<LocalId, bool>>,
     ) {
-        let mut is_ever_initialized = result[b][l]; // It must be filled, as we use it as mark for dfs
-        let block = &body.basic_blocks[b];
-        for statement in &block.statements {
-            match &statement.kind {
-                StatementKind::Assign(p, _) => {
-                    if p.projection.lookup(&body.projection_store).is_empty() && p.local == l {
-                        is_ever_initialized = true;
+        while let Some(b) = stack.pop() {
+            let mut is_ever_initialized = result[b][l]; // It must be filled, as we use it as mark for dfs
+            let block = &body.basic_blocks[b];
+            for statement in &block.statements {
+                match &statement.kind {
+                    StatementKind::Assign(p, _) => {
+                        if p.projection.lookup(&body.projection_store).is_empty() && p.local == l {
+                            is_ever_initialized = true;
+                        }
                     }
-                }
-                StatementKind::StorageDead(p) => {
-                    if *p == l {
-                        is_ever_initialized = false;
+                    StatementKind::StorageDead(p) => {
+                        if *p == l {
+                            is_ever_initialized = false;
+                        }
                     }
+                    StatementKind::Deinit(_)
+                    | StatementKind::FakeRead(_)
+                    | StatementKind::Nop
+                    | StatementKind::StorageLive(_) => (),
                 }
-                StatementKind::Deinit(_)
-                | StatementKind::FakeRead(_)
-                | StatementKind::Nop
-                | StatementKind::StorageLive(_) => (),
-            }
-        }
-        let Some(terminator) = &block.terminator else {
-            never!(
-                "Terminator should be none only in construction.\nThe body:\n{}",
-                body.pretty_print(db)
-            );
-            return;
-        };
-        let mut process = |target, is_ever_initialized| {
-            if !result[target].contains_idx(l) || !result[target][l] && is_ever_initialized {
-                result[target].insert(l, is_ever_initialized);
-                dfs(db, body, target, l, result);
-            }
-        };
-        match &terminator.kind {
-            TerminatorKind::Goto { target } => process(*target, is_ever_initialized),
-            TerminatorKind::SwitchInt { targets, .. } => {
-                targets.all_targets().iter().for_each(|&it| process(it, is_ever_initialized));
             }
-            TerminatorKind::UnwindResume
-            | TerminatorKind::Abort
-            | TerminatorKind::Return
-            | TerminatorKind::Unreachable => (),
-            TerminatorKind::Call { target, cleanup, destination, .. } => {
-                if destination.projection.lookup(&body.projection_store).is_empty()
-                    && destination.local == l
-                {
-                    is_ever_initialized = true;
+            let Some(terminator) = &block.terminator else {
+                never!(
+                    "Terminator should be none only in construction.\nThe body:\n{}",
+                    body.pretty_print(db)
+                );
+                return;
+            };
+            let mut process = |target, is_ever_initialized| {
+                if !result[target].contains_idx(l) || !result[target][l] && is_ever_initialized {
+                    result[target].insert(l, is_ever_initialized);
+                    stack.push(target);
+                }
+            };
+            match &terminator.kind {
+                TerminatorKind::Goto { target } => process(*target, is_ever_initialized),
+                TerminatorKind::SwitchInt { targets, .. } => {
+                    targets.all_targets().iter().for_each(|&it| process(it, is_ever_initialized));
+                }
+                TerminatorKind::UnwindResume
+                | TerminatorKind::Abort
+                | TerminatorKind::Return
+                | TerminatorKind::Unreachable => (),
+                TerminatorKind::Call { target, cleanup, destination, .. } => {
+                    if destination.projection.lookup(&body.projection_store).is_empty()
+                        && destination.local == l
+                    {
+                        is_ever_initialized = true;
+                    }
+                    target.iter().chain(cleanup).for_each(|&it| process(it, is_ever_initialized));
+                }
+                TerminatorKind::Drop { target, unwind, place: _ } => {
+                    iter::once(target)
+                        .chain(unwind)
+                        .for_each(|&it| process(it, is_ever_initialized));
+                }
+                TerminatorKind::DropAndReplace { .. }
+                | TerminatorKind::Assert { .. }
+                | TerminatorKind::Yield { .. }
+                | TerminatorKind::CoroutineDrop
+                | TerminatorKind::FalseEdge { .. }
+                | TerminatorKind::FalseUnwind { .. } => {
+                    never!("We don't emit these MIR terminators yet");
                 }
-                target.iter().chain(cleanup).for_each(|&it| process(it, is_ever_initialized));
-            }
-            TerminatorKind::Drop { target, unwind, place: _ } => {
-                iter::once(target).chain(unwind).for_each(|&it| process(it, is_ever_initialized));
-            }
-            TerminatorKind::DropAndReplace { .. }
-            | TerminatorKind::Assert { .. }
-            | TerminatorKind::Yield { .. }
-            | TerminatorKind::CoroutineDrop
-            | TerminatorKind::FalseEdge { .. }
-            | TerminatorKind::FalseUnwind { .. } => {
-                never!("We don't emit these MIR terminators yet");
             }
         }
     }
+    let mut stack = Vec::new();
     for &l in &body.param_locals {
         result[body.start_block].insert(l, true);
-        dfs(db, body, body.start_block, l, &mut result);
+        stack.clear();
+        stack.push(body.start_block);
+        dfs(db, body, l, &mut stack, &mut result);
     }
     for l in body.locals.iter().map(|it| it.0) {
         db.unwind_if_cancelled();
         if !result[body.start_block].contains_idx(l) {
             result[body.start_block].insert(l, false);
-            dfs(db, body, body.start_block, l, &mut result);
+            stack.clear();
+            stack.push(body.start_block);
+            dfs(db, body, l, &mut stack, &mut result);
         }
     }
     result
diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index 8f5db32f957..30e023e1a47 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -144,9 +144,9 @@ pub use {
     hir_ty::{
         consteval::ConstEvalError,
         display::{ClosureStyle, HirDisplay, HirDisplayError, HirWrite},
+        dyn_compatibility::{DynCompatibilityViolation, MethodViolationCode},
         layout::LayoutError,
         mir::{MirEvalError, MirLowerError},
-        object_safety::{MethodViolationCode, ObjectSafetyViolation},
         CastError, FnAbi, PointerCast, Safety,
     },
     // FIXME: Properly encapsulate mir
@@ -497,10 +497,9 @@ impl Module {
 
     /// Finds a parent module.
     pub fn parent(self, db: &dyn HirDatabase) -> Option<Module> {
-        // FIXME: handle block expressions as modules (their parent is in a different DefMap)
         let def_map = self.id.def_map(db.upcast());
-        let parent_id = def_map[self.id.local_id].parent?;
-        Some(Module { id: def_map.module_id(parent_id) })
+        let parent_id = def_map.containing_module(self.id.local_id)?;
+        Some(Module { id: parent_id })
     }
 
     /// Finds nearest non-block ancestor `Module` (`self` included).
@@ -557,7 +556,7 @@ impl Module {
         acc: &mut Vec<AnyDiagnostic>,
         style_lints: bool,
     ) {
-        let _p = tracing::info_span!("Module::diagnostics", name = ?self.name(db)).entered();
+        let _p = tracing::info_span!("diagnostics", name = ?self.name(db)).entered();
         let edition = db.crate_graph()[self.id.krate()].edition;
         let def_map = self.id.def_map(db.upcast());
         for diag in def_map.diagnostics() {
@@ -2690,8 +2689,8 @@ impl Trait {
             .count()
     }
 
-    pub fn object_safety(&self, db: &dyn HirDatabase) -> Option<ObjectSafetyViolation> {
-        hir_ty::object_safety::object_safety(db, self.id)
+    pub fn dyn_compatibility(&self, db: &dyn HirDatabase) -> Option<DynCompatibilityViolation> {
+        hir_ty::dyn_compatibility::dyn_compatibility(db, self.id)
     }
 
     fn all_macro_calls(&self, db: &dyn HirDatabase) -> Box<[(AstId<ast::Item>, MacroCallId)]> {
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
index fa14b53dbc3..b27f1fbb5db 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
@@ -24,6 +24,7 @@ use hir_expand::{
     builtin::{BuiltinFnLikeExpander, EagerExpander},
     db::ExpandDatabase,
     files::InRealFile,
+    hygiene::SyntaxContextExt as _,
     inert_attr_macro::find_builtin_attr_idx,
     name::AsName,
     FileRange, InMacroFile, MacroCallId, MacroFileId, MacroFileIdExt,
@@ -32,13 +33,13 @@ use intern::Symbol;
 use itertools::Itertools;
 use rustc_hash::{FxHashMap, FxHashSet};
 use smallvec::{smallvec, SmallVec};
-use span::{EditionedFileId, FileId, HirFileIdRepr};
+use span::{EditionedFileId, FileId, HirFileIdRepr, SyntaxContextId};
 use stdx::TupleExt;
 use syntax::{
     algo::skip_trivia_token,
-    ast::{self, HasAttrs as _, HasGenericParams, HasLoopBody, IsString as _},
-    match_ast, AstNode, AstToken, Direction, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken,
-    TextRange, TextSize,
+    ast::{self, HasAttrs as _, HasGenericParams, IsString as _},
+    AstNode, AstToken, Direction, SyntaxKind, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange,
+    TextSize,
 };
 
 use crate::{
@@ -608,7 +609,7 @@ impl<'db> SemanticsImpl<'db> {
         let quote = string.open_quote_text_range()?;
 
         let token = self.wrap_token_infile(string.syntax().clone()).into_real_file().ok()?;
-        self.descend_into_macros_breakable(token, |token| {
+        self.descend_into_macros_breakable(token, |token, _| {
             (|| {
                 let token = token.value;
                 let string = ast::String::cast(token)?;
@@ -655,7 +656,7 @@ impl<'db> SemanticsImpl<'db> {
         let original_string = ast::String::cast(original_token.clone())?;
         let original_token = self.wrap_token_infile(original_token).into_real_file().ok()?;
         let quote = original_string.open_quote_text_range()?;
-        self.descend_into_macros_breakable(original_token, |token| {
+        self.descend_into_macros_breakable(original_token, |token, _| {
             (|| {
                 let token = token.value;
                 self.resolve_offset_in_format_args(
@@ -718,7 +719,7 @@ impl<'db> SemanticsImpl<'db> {
             // node is just the token, so descend the token
             self.descend_into_macros_impl(
                 InRealFile::new(file_id, first),
-                &mut |InFile { value, .. }| {
+                &mut |InFile { value, .. }, _ctx| {
                     if let Some(node) = value
                         .parent_ancestors()
                         .take_while(|it| it.text_range() == value.text_range())
@@ -732,7 +733,7 @@ impl<'db> SemanticsImpl<'db> {
         } else {
             // Descend first and last token, then zip them to look for the node they belong to
             let mut scratch: SmallVec<[_; 1]> = smallvec![];
-            self.descend_into_macros_impl(InRealFile::new(file_id, first), &mut |token| {
+            self.descend_into_macros_impl(InRealFile::new(file_id, first), &mut |token, _ctx| {
                 scratch.push(token);
                 CONTINUE_NO_BREAKS
             });
@@ -740,7 +741,7 @@ impl<'db> SemanticsImpl<'db> {
             let mut scratch = scratch.into_iter();
             self.descend_into_macros_impl(
                 InRealFile::new(file_id, last),
-                &mut |InFile { value: last, file_id: last_fid }| {
+                &mut |InFile { value: last, file_id: last_fid }, _ctx| {
                     if let Some(InFile { value: first, file_id: first_fid }) = scratch.next() {
                         if first_fid == last_fid {
                             if let Some(p) = first.parent() {
@@ -763,7 +764,9 @@ impl<'db> SemanticsImpl<'db> {
         res
     }
 
-    fn is_inside_macro_call(token: &SyntaxToken) -> bool {
+    // FIXME: This isn't quite right wrt to inner attributes
+    /// Does a syntactic traversal to check whether this token might be inside a macro call
+    pub fn might_be_inside_macro_call(&self, token: &SyntaxToken) -> bool {
         token.parent_ancestors().any(|ancestor| {
             if ast::MacroCall::can_cast(ancestor.kind()) {
                 return true;
@@ -781,25 +784,14 @@ impl<'db> SemanticsImpl<'db> {
         })
     }
 
-    pub fn descend_into_macros_exact_if_in_macro(
-        &self,
-        token: SyntaxToken,
-    ) -> SmallVec<[SyntaxToken; 1]> {
-        if Self::is_inside_macro_call(&token) {
-            self.descend_into_macros_exact(token)
-        } else {
-            smallvec![token]
-        }
-    }
-
     pub fn descend_into_macros_cb(
         &self,
         token: SyntaxToken,
-        mut cb: impl FnMut(InFile<SyntaxToken>),
+        mut cb: impl FnMut(InFile<SyntaxToken>, SyntaxContextId),
     ) {
         if let Ok(token) = self.wrap_token_infile(token).into_real_file() {
-            self.descend_into_macros_impl(token, &mut |t| {
-                cb(t);
+            self.descend_into_macros_impl(token, &mut |t, ctx| {
+                cb(t, ctx);
                 CONTINUE_NO_BREAKS
             });
         }
@@ -808,7 +800,7 @@ impl<'db> SemanticsImpl<'db> {
     pub fn descend_into_macros(&self, token: SyntaxToken) -> SmallVec<[SyntaxToken; 1]> {
         let mut res = smallvec![];
         if let Ok(token) = self.wrap_token_infile(token.clone()).into_real_file() {
-            self.descend_into_macros_impl(token, &mut |t| {
+            self.descend_into_macros_impl(token, &mut |t, _ctx| {
                 res.push(t.value);
                 CONTINUE_NO_BREAKS
             });
@@ -819,10 +811,27 @@ impl<'db> SemanticsImpl<'db> {
         res
     }
 
+    pub fn descend_into_macros_no_opaque(&self, token: SyntaxToken) -> SmallVec<[SyntaxToken; 1]> {
+        let mut res = smallvec![];
+        if let Ok(token) = self.wrap_token_infile(token.clone()).into_real_file() {
+            self.descend_into_macros_impl(token, &mut |t, ctx| {
+                if !ctx.is_opaque(self.db.upcast()) {
+                    // Don't descend into opaque contexts
+                    res.push(t.value);
+                }
+                CONTINUE_NO_BREAKS
+            });
+        }
+        if res.is_empty() {
+            res.push(token);
+        }
+        res
+    }
+
     pub fn descend_into_macros_breakable<T>(
         &self,
         token: InRealFile<SyntaxToken>,
-        mut cb: impl FnMut(InFile<SyntaxToken>) -> ControlFlow<T>,
+        mut cb: impl FnMut(InFile<SyntaxToken>, SyntaxContextId) -> ControlFlow<T>,
     ) -> Option<T> {
         self.descend_into_macros_impl(token.clone(), &mut cb)
     }
@@ -834,10 +843,12 @@ impl<'db> SemanticsImpl<'db> {
         let text = token.text();
         let kind = token.kind();
 
-        self.descend_into_macros_cb(token.clone(), |InFile { value, file_id: _ }| {
+        self.descend_into_macros_cb(token.clone(), |InFile { value, file_id: _ }, ctx| {
             let mapped_kind = value.kind();
             let any_ident_match = || kind.is_any_identifier() && value.kind().is_any_identifier();
-            let matches = (kind == mapped_kind || any_ident_match()) && text == value.text();
+            let matches = (kind == mapped_kind || any_ident_match())
+                && text == value.text()
+                && !ctx.is_opaque(self.db.upcast());
             if matches {
                 r.push(value);
             }
@@ -854,17 +865,21 @@ impl<'db> SemanticsImpl<'db> {
         let text = token.text();
         let kind = token.kind();
         if let Ok(token) = self.wrap_token_infile(token.clone()).into_real_file() {
-            self.descend_into_macros_breakable(token.clone(), |InFile { value, file_id: _ }| {
-                let mapped_kind = value.kind();
-                let any_ident_match =
-                    || kind.is_any_identifier() && value.kind().is_any_identifier();
-                let matches = (kind == mapped_kind || any_ident_match()) && text == value.text();
-                if matches {
-                    ControlFlow::Break(value)
-                } else {
-                    ControlFlow::Continue(())
-                }
-            })
+            self.descend_into_macros_breakable(
+                token.clone(),
+                |InFile { value, file_id: _ }, _ctx| {
+                    let mapped_kind = value.kind();
+                    let any_ident_match =
+                        || kind.is_any_identifier() && value.kind().is_any_identifier();
+                    let matches =
+                        (kind == mapped_kind || any_ident_match()) && text == value.text();
+                    if matches {
+                        ControlFlow::Break(value)
+                    } else {
+                        ControlFlow::Continue(())
+                    }
+                },
+            )
         } else {
             None
         }
@@ -874,7 +889,7 @@ impl<'db> SemanticsImpl<'db> {
     fn descend_into_macros_impl<T>(
         &self,
         InRealFile { value: token, file_id }: InRealFile<SyntaxToken>,
-        f: &mut dyn FnMut(InFile<SyntaxToken>) -> ControlFlow<T>,
+        f: &mut dyn FnMut(InFile<SyntaxToken>, SyntaxContextId) -> ControlFlow<T>,
     ) -> Option<T> {
         let _p = tracing::info_span!("descend_into_macros_impl").entered();
         let (sa, span, file_id) = token
@@ -898,7 +913,8 @@ impl<'db> SemanticsImpl<'db> {
         // These are tracked to know which macro calls we still have to look into
         // the tokens themselves aren't that interesting as the span that is being used to map
         // things down never changes.
-        let mut stack: Vec<(_, SmallVec<[_; 2]>)> = vec![(file_id, smallvec![token])];
+        let mut stack: Vec<(_, SmallVec<[_; 2]>)> =
+            vec![(file_id, smallvec![(token, SyntaxContextId::ROOT)])];
 
         // Process the expansion of a call, pushing all tokens with our span in the expansion back onto our stack
         let process_expansion_for_token = |stack: &mut Vec<_>, macro_file| {
@@ -921,11 +937,11 @@ impl<'db> SemanticsImpl<'db> {
         // Filters out all tokens that contain the given range (usually the macro call), any such
         // token is redundant as the corresponding macro call has already been processed
         let filter_duplicates = |tokens: &mut SmallVec<_>, range: TextRange| {
-            tokens.retain(|t: &mut SyntaxToken| !range.contains_range(t.text_range()))
+            tokens.retain(|(t, _): &mut (SyntaxToken, _)| !range.contains_range(t.text_range()))
         };
 
         while let Some((expansion, ref mut tokens)) = stack.pop() {
-            while let Some(token) = tokens.pop() {
+            while let Some((token, ctx)) = tokens.pop() {
                 let was_not_remapped = (|| {
                     // First expand into attribute invocations
                     let containing_attribute_macro_call = self.with_ctx(|ctx| {
@@ -1036,7 +1052,7 @@ impl<'db> SemanticsImpl<'db> {
                                             let text_range = attr.syntax().text_range();
                                             // remove any other token in this macro input, all their mappings are the
                                             // same as this
-                                            tokens.retain(|t| {
+                                            tokens.retain(|(t, _)| {
                                                 !text_range.contains_range(t.text_range())
                                             });
                                             return process_expansion_for_token(
@@ -1093,7 +1109,7 @@ impl<'db> SemanticsImpl<'db> {
                 .is_none();
 
                 if was_not_remapped {
-                    if let ControlFlow::Break(b) = f(InFile::new(expansion, token)) {
+                    if let ControlFlow::Break(b) = f(InFile::new(expansion, token), ctx) {
                         return Some(b);
                     }
                 }
@@ -1221,26 +1237,10 @@ impl<'db> SemanticsImpl<'db> {
         ToDef::to_def(self, src.as_ref())
     }
 
-    pub fn resolve_label(&self, lifetime: &ast::Lifetime) -> Option<Label> {
-        let text = lifetime.text();
-        let label = lifetime.syntax().ancestors().find_map(|syn| {
-            let label = match_ast! {
-                match syn {
-                    ast::ForExpr(it) => it.label(),
-                    ast::WhileExpr(it) => it.label(),
-                    ast::LoopExpr(it) => it.label(),
-                    ast::BlockExpr(it) => it.label(),
-                    _ => None,
-                }
-            };
-            label.filter(|l| {
-                l.lifetime()
-                    .and_then(|lt| lt.lifetime_ident_token())
-                    .map_or(false, |lt| lt.text() == text)
-            })
-        })?;
-        let src = self.wrap_node_infile(label);
-        ToDef::to_def(self, src.as_ref())
+    pub fn resolve_label(&self, label: &ast::Lifetime) -> Option<Label> {
+        let (parent, label_id) = self
+            .with_ctx(|ctx| ctx.label_ref_to_def(self.wrap_node_infile(label.clone()).as_ref()))?;
+        Some(Label { parent, label_id })
     }
 
     pub fn resolve_type(&self, ty: &ast::Type) -> Option<Type> {
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs
index c1e4e1d1e27..fd6d52d6c9d 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs
@@ -92,7 +92,7 @@ use hir_def::{
         keys::{self, Key},
         DynMap,
     },
-    hir::{BindingId, LabelId},
+    hir::{BindingId, Expr, LabelId},
     AdtId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, ExternCrateId,
     FieldId, FunctionId, GenericDefId, GenericParamId, ImplId, LifetimeParamId, Lookup, MacroId,
     ModuleId, StaticId, StructId, TraitAliasId, TraitId, TypeAliasId, TypeParamId, UnionId, UseId,
@@ -343,6 +343,20 @@ impl SourceToDefCtx<'_, '_> {
         Some((container, label_id))
     }
 
+    pub(super) fn label_ref_to_def(
+        &mut self,
+        src: InFile<&ast::Lifetime>,
+    ) -> Option<(DefWithBodyId, LabelId)> {
+        let break_or_continue = ast::Expr::cast(src.value.syntax().parent()?)?;
+        let container = self.find_pat_or_label_container(src.syntax_ref())?;
+        let (body, source_map) = self.db.body_with_source_map(container);
+        let break_or_continue = source_map.node_expr(src.with_value(&break_or_continue))?;
+        let (Expr::Break { label, .. } | Expr::Continue { label }) = body[break_or_continue] else {
+            return None;
+        };
+        Some((container, label?))
+    }
+
     pub(super) fn item_to_macro_call(&mut self, src: InFile<&ast::Item>) -> Option<MacroCallId> {
         let map = self.dyn_map(src)?;
         map[keys::ATTR_MACRO_CALL].get(&AstPtr::new(src.value)).copied()
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs
index a43a4b5e1a0..61dc72e0b33 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_variable.rs
@@ -1,8 +1,12 @@
 use hir::TypeInfo;
 use ide_db::syntax_helpers::suggest_name;
 use syntax::{
-    ast::{self, edit::IndentLevel, edit_in_place::Indent, make, AstNode, HasName},
-    ted, NodeOrToken,
+    ast::{
+        self, edit::IndentLevel, edit_in_place::Indent, make, syntax_factory::SyntaxFactory,
+        AstNode,
+    },
+    syntax_editor::Position,
+    NodeOrToken,
     SyntaxKind::{BLOCK_EXPR, BREAK_EXPR, COMMENT, LOOP_EXPR, MATCH_GUARD, PATH_EXPR, RETURN_EXPR},
     SyntaxNode, T,
 };
@@ -105,39 +109,46 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
                 ),
             };
 
+            let make = SyntaxFactory::new();
+            let mut editor = edit.make_editor(&expr_replace);
+
+            let pat_name = make.name(&var_name);
+            let name_expr = make.expr_path(make::ext::ident_path(&var_name));
+
+            if let Some(cap) = ctx.config.snippet_cap {
+                let tabstop = edit.make_tabstop_before(cap);
+                editor.add_annotation(pat_name.syntax().clone(), tabstop);
+            }
+
             let ident_pat = match parent {
                 Some(ast::Expr::RefExpr(expr)) if expr.mut_token().is_some() => {
-                    make::ident_pat(false, true, make::name(&var_name))
+                    make.ident_pat(false, true, pat_name)
                 }
                 _ if needs_adjust
                     && !needs_ref
                     && ty.as_ref().is_some_and(|ty| ty.is_mutable_reference()) =>
                 {
-                    make::ident_pat(false, true, make::name(&var_name))
+                    make.ident_pat(false, true, pat_name)
                 }
-                _ => make::ident_pat(false, false, make::name(&var_name)),
+                _ => make.ident_pat(false, false, pat_name),
             };
 
             let to_extract_no_ref = match ty.as_ref().filter(|_| needs_ref) {
                 Some(receiver_type) if receiver_type.is_mutable_reference() => {
-                    make::expr_ref(to_extract_no_ref, true)
+                    make.expr_ref(to_extract_no_ref, true)
                 }
                 Some(receiver_type) if receiver_type.is_reference() => {
-                    make::expr_ref(to_extract_no_ref, false)
+                    make.expr_ref(to_extract_no_ref, false)
                 }
                 _ => to_extract_no_ref,
             };
 
-            let expr_replace = edit.make_syntax_mut(expr_replace);
-            let let_stmt =
-                make::let_stmt(ident_pat.into(), None, Some(to_extract_no_ref)).clone_for_update();
-            let name_expr = make::expr_path(make::ext::ident_path(&var_name)).clone_for_update();
+            let let_stmt = make.let_stmt(ident_pat.into(), None, Some(to_extract_no_ref));
 
             match anchor {
                 Anchor::Before(place) => {
                     let prev_ws = place.prev_sibling_or_token().and_then(|it| it.into_token());
                     let indent_to = IndentLevel::from_node(&place);
-                    let insert_place = edit.make_syntax_mut(place);
 
                     // Adjust ws to insert depending on if this is all inline or on separate lines
                     let trailing_ws = if prev_ws.is_some_and(|it| it.text().starts_with('\n')) {
@@ -146,37 +157,20 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
                         " ".to_owned()
                     };
 
-                    ted::insert_all_raw(
-                        ted::Position::before(insert_place),
+                    editor.insert_all(
+                        Position::before(place),
                         vec![
                             let_stmt.syntax().clone().into(),
                             make::tokens::whitespace(&trailing_ws).into(),
                         ],
                     );
 
-                    ted::replace(expr_replace, name_expr.syntax());
-
-                    if let Some(cap) = ctx.config.snippet_cap {
-                        if let Some(ast::Pat::IdentPat(ident_pat)) = let_stmt.pat() {
-                            if let Some(name) = ident_pat.name() {
-                                edit.add_tabstop_before(cap, name);
-                            }
-                        }
-                    }
+                    editor.replace(expr_replace, name_expr.syntax());
                 }
                 Anchor::Replace(stmt) => {
                     cov_mark::hit!(test_extract_var_expr_stmt);
 
-                    let stmt_replace = edit.make_mut(stmt);
-                    ted::replace(stmt_replace.syntax(), let_stmt.syntax());
-
-                    if let Some(cap) = ctx.config.snippet_cap {
-                        if let Some(ast::Pat::IdentPat(ident_pat)) = let_stmt.pat() {
-                            if let Some(name) = ident_pat.name() {
-                                edit.add_tabstop_before(cap, name);
-                            }
-                        }
-                    }
+                    editor.replace(stmt.syntax(), let_stmt.syntax());
                 }
                 Anchor::WrapInBlock(to_wrap) => {
                     let indent_to = to_wrap.indent_level();
@@ -184,47 +178,22 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
                     let block = if to_wrap.syntax() == &expr_replace {
                         // Since `expr_replace` is the same that needs to be wrapped in a block,
                         // we can just directly replace it with a block
-                        let block =
-                            make::block_expr([let_stmt.into()], Some(name_expr)).clone_for_update();
-                        ted::replace(expr_replace, block.syntax());
-
-                        block
+                        make.block_expr([let_stmt.into()], Some(name_expr))
                     } else {
-                        // `expr_replace` is a descendant of `to_wrap`, so both steps need to be
-                        // handled separately, otherwise we wrap the wrong expression
-                        let to_wrap = edit.make_mut(to_wrap);
-
-                        // Replace the target expr first so that we don't need to find where
-                        // `expr_replace` is in the wrapped `to_wrap`
-                        ted::replace(expr_replace, name_expr.syntax());
-
-                        // Wrap `to_wrap` in a block
-                        let block = make::block_expr([let_stmt.into()], Some(to_wrap.clone()))
-                            .clone_for_update();
-                        ted::replace(to_wrap.syntax(), block.syntax());
-
-                        block
+                        // `expr_replace` is a descendant of `to_wrap`, so we just replace it with `name_expr`.
+                        editor.replace(expr_replace, name_expr.syntax());
+                        make.block_expr([let_stmt.into()], Some(to_wrap.clone()))
                     };
 
-                    if let Some(cap) = ctx.config.snippet_cap {
-                        // Adding a tabstop to `name` requires finding the let stmt again, since
-                        // the existing `let_stmt` is not actually added to the tree
-                        let pat = block.statements().find_map(|stmt| {
-                            let ast::Stmt::LetStmt(let_stmt) = stmt else { return None };
-                            let_stmt.pat()
-                        });
-
-                        if let Some(ast::Pat::IdentPat(ident_pat)) = pat {
-                            if let Some(name) = ident_pat.name() {
-                                edit.add_tabstop_before(cap, name);
-                            }
-                        }
-                    }
+                    editor.replace(to_wrap.syntax(), block.syntax());
 
                     // fixup indentation of block
                     block.indent(indent_to);
                 }
             }
+
+            editor.add_mappings(make.finish_with_mappings());
+            edit.add_file_edits(ctx.file_id(), editor);
             edit.rename();
         },
     )
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs
index 0d403f49b7a..1d05419c96d 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/config.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/config.rs
@@ -7,7 +7,7 @@
 use hir::ImportPathConfig;
 use ide_db::{imports::insert_use::InsertUseConfig, SnippetCap};
 
-use crate::snippet::Snippet;
+use crate::{snippet::Snippet, CompletionFieldsToResolve};
 
 #[derive(Clone, Debug, PartialEq, Eq)]
 pub struct CompletionConfig {
@@ -27,6 +27,7 @@ pub struct CompletionConfig {
     pub prefer_absolute: bool,
     pub snippets: Vec<Snippet>,
     pub limit: Option<usize>,
+    pub fields_to_resolve: CompletionFieldsToResolve,
 }
 
 #[derive(Clone, Debug, PartialEq, Eq)]
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
index 192f1b43fac..e49a9e3b064 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs
@@ -7,7 +7,8 @@ mod tests;
 use std::{iter, ops::ControlFlow};
 
 use hir::{
-    HasAttrs, Local, Name, PathResolution, ScopeDef, Semantics, SemanticsScope, Type, TypeInfo,
+    HasAttrs, Local, ModuleSource, Name, PathResolution, ScopeDef, Semantics, SemanticsScope, Type,
+    TypeInfo,
 };
 use ide_db::{
     base_db::SourceDatabase, famous_defs::FamousDefs, helpers::is_editable_crate, FilePosition,
@@ -743,7 +744,12 @@ impl<'a> CompletionContext<'a> {
             }
         });
 
-        let depth_from_crate_root = iter::successors(module.parent(db), |m| m.parent(db)).count();
+        let depth_from_crate_root = iter::successors(Some(module), |m| m.parent(db))
+            // `BlockExpr` modules are not count as module depth
+            .filter(|m| !matches!(m.definition_source(db).value, ModuleSource::BlockExpr(_)))
+            .count()
+            // exclude `m` itself
+            .saturating_sub(1);
 
         let complete_semicolon = if config.add_semicolon_to_unit {
             let inside_closure_ret = token.parent_ancestors().try_for_each(|ancestor| {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs
index 58d1fad0950..a78976d3fd8 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/lib.rs
@@ -37,6 +37,31 @@ pub use crate::{
     snippet::{Snippet, SnippetScope},
 };
 
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub struct CompletionFieldsToResolve {
+    pub resolve_label_details: bool,
+    pub resolve_tags: bool,
+    pub resolve_detail: bool,
+    pub resolve_documentation: bool,
+    pub resolve_filter_text: bool,
+    pub resolve_text_edit: bool,
+    pub resolve_command: bool,
+}
+
+impl CompletionFieldsToResolve {
+    pub const fn empty() -> Self {
+        Self {
+            resolve_label_details: false,
+            resolve_tags: false,
+            resolve_detail: false,
+            resolve_documentation: false,
+            resolve_filter_text: false,
+            resolve_text_edit: false,
+            resolve_command: false,
+        }
+    }
+}
+
 //FIXME: split the following feature into fine-grained features.
 
 // Feature: Magic Completions
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs
index 9d77d970071..f371012de3f 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests.rs
@@ -37,8 +37,8 @@ use test_fixture::ChangeFixture;
 use test_utils::assert_eq_text;
 
 use crate::{
-    resolve_completion_edits, CallableSnippets, CompletionConfig, CompletionItem,
-    CompletionItemKind,
+    resolve_completion_edits, CallableSnippets, CompletionConfig, CompletionFieldsToResolve,
+    CompletionItem, CompletionItemKind,
 };
 
 /// Lots of basic item definitions
@@ -84,6 +84,7 @@ pub(crate) const TEST_CONFIG: CompletionConfig = CompletionConfig {
     prefer_absolute: false,
     snippets: Vec::new(),
     limit: None,
+    fields_to_resolve: CompletionFieldsToResolve::empty(),
 };
 
 pub(crate) fn completion_list(ra_fixture: &str) -> String {
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs
index abf4438a71f..266109765ab 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/generated/lints.rs
@@ -7081,8 +7081,8 @@ This feature has no tracking issue, and is therefore likely internal to the comp
 "##,
     },
     Lint {
-        label: "object_safe_for_dispatch",
-        description: r##"# `object_safe_for_dispatch`
+        label: "dyn_compatible_for_dispatch",
+        description: r##"# `dyn_compatible_for_dispatch`
 
 The tracking issue for this feature is: [#43561]
 
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs b/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs
index 926fae0d317..4c197b45338 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/imports/merge_imports.rs
@@ -614,7 +614,7 @@ fn path_segment_cmp(a: &ast::PathSegment, b: &ast::PathSegment) -> Ordering {
                 (Some(_), None) => Ordering::Greater,
                 (None, Some(_)) => Ordering::Less,
                 (Some(a_name), Some(b_name)) => {
-                    // snake_case < CamelCase < UPPER_SNAKE_CASE
+                    // snake_case < UpperCamelCase < UPPER_SNAKE_CASE
                     let a_text = a_name.as_str().trim_start_matches("r#");
                     let b_text = b_name.as_str().trim_start_matches("r#");
                     if a_text.starts_with(char::is_lowercase)
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs b/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs
index bb121f4a80a..19d8a15422e 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/prime_caches.rs
@@ -13,6 +13,7 @@ use crate::{
         salsa::{Database, ParallelDatabase, Snapshot},
         Cancelled, CrateId, SourceDatabase, SourceRootDatabase,
     },
+    symbol_index::SymbolsDatabase,
     FxIndexMap, RootDatabase,
 };
 
@@ -54,11 +55,13 @@ pub fn parallel_prime_caches(
         let (progress_sender, progress_receiver) = crossbeam_channel::unbounded();
         let (work_sender, work_receiver) = crossbeam_channel::unbounded();
         let graph = graph.clone();
+        let local_roots = db.local_roots();
         let prime_caches_worker = move |db: Snapshot<RootDatabase>| {
             while let Ok((crate_id, crate_name)) = work_receiver.recv() {
                 progress_sender
                     .send(ParallelPrimeCacheWorkerProgress::BeginCrate { crate_id, crate_name })?;
 
+                // Compute the DefMap and possibly ImportMap
                 let file_id = graph[crate_id].root_file_id;
                 let root_id = db.file_source_root(file_id);
                 if db.source_root(root_id).is_library {
@@ -68,6 +71,19 @@ pub fn parallel_prime_caches(
                     db.import_map(crate_id);
                 }
 
+                // Compute the symbol search index.
+                // This primes the cache for `ide_db::symbol_index::world_symbols()`.
+                //
+                // We do this for workspace crates only (members of local_roots), because doing it
+                // for all dependencies could be *very* unnecessarily slow in a large project.
+                //
+                // FIXME: We should do it unconditionally if the configuration is set to default to
+                // searching dependencies (rust-analyzer.workspace.symbol.search.scope), but we
+                // would need to pipe that configuration information down here.
+                if local_roots.contains(&root_id) {
+                    db.crate_symbols(crate_id.into());
+                }
+
                 progress_sender.send(ParallelPrimeCacheWorkerProgress::EndCrate { crate_id })?;
             }
 
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/search.rs b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
index 4166b08339b..852ee595be4 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/search.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/search.rs
@@ -529,9 +529,13 @@ impl<'a> FindUsages<'a> {
             })
             .into_iter()
             .flat_map(move |token| {
-                sema.descend_into_macros_exact_if_in_macro(token)
-                    .into_iter()
-                    .filter_map(|it| it.parent())
+                if sema.might_be_inside_macro_call(&token) {
+                    sema.descend_into_macros_exact(token)
+                } else {
+                    <_>::from([token])
+                }
+                .into_iter()
+                .filter_map(|it| it.parent())
             })
     }
 
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs
index 83a1eb44a61..bbdeb7cf085 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/incorrect_case.rs
@@ -229,10 +229,10 @@ fn foo() {
         check_diagnostics(
             r#"
 struct non_camel_case_name {}
-    // ^^^^^^^^^^^^^^^^^^^ 💡 warn: Structure `non_camel_case_name` should have CamelCase name, e.g. `NonCamelCaseName`
+    // ^^^^^^^^^^^^^^^^^^^ 💡 warn: Structure `non_camel_case_name` should have UpperCamelCase name, e.g. `NonCamelCaseName`
 
 struct SCREAMING_CASE {}
-    // ^^^^^^^^^^^^^^ 💡 warn: Structure `SCREAMING_CASE` should have CamelCase name, e.g. `ScreamingCase`
+    // ^^^^^^^^^^^^^^ 💡 warn: Structure `SCREAMING_CASE` should have UpperCamelCase name, e.g. `ScreamingCase`
 "#,
         );
     }
@@ -261,10 +261,10 @@ struct SomeStruct { SomeField: u8 }
         check_diagnostics(
             r#"
 enum some_enum { Val(u8) }
-  // ^^^^^^^^^ 💡 warn: Enum `some_enum` should have CamelCase name, e.g. `SomeEnum`
+  // ^^^^^^^^^ 💡 warn: Enum `some_enum` should have UpperCamelCase name, e.g. `SomeEnum`
 
 enum SOME_ENUM {}
-  // ^^^^^^^^^ 💡 warn: Enum `SOME_ENUM` should have CamelCase name, e.g. `SomeEnum`
+  // ^^^^^^^^^ 💡 warn: Enum `SOME_ENUM` should have UpperCamelCase name, e.g. `SomeEnum`
 "#,
         );
     }
@@ -283,7 +283,7 @@ enum AABB {}
         check_diagnostics(
             r#"
 enum SomeEnum { SOME_VARIANT(u8) }
-             // ^^^^^^^^^^^^ 💡 warn: Variant `SOME_VARIANT` should have CamelCase name, e.g. `SomeVariant`
+             // ^^^^^^^^^^^^ 💡 warn: Variant `SOME_VARIANT` should have UpperCamelCase name, e.g. `SomeVariant`
 "#,
         );
     }
@@ -313,7 +313,7 @@ static some_weird_const: u8 = 10;
         check_diagnostics(
             r#"
 struct someStruct;
-    // ^^^^^^^^^^ 💡 warn: Structure `someStruct` should have CamelCase name, e.g. `SomeStruct`
+    // ^^^^^^^^^^ 💡 warn: Structure `someStruct` should have UpperCamelCase name, e.g. `SomeStruct`
 
 impl someStruct {
     fn SomeFunc(&self) {
@@ -530,11 +530,11 @@ extern {
         check_diagnostics(
             r#"
 trait BAD_TRAIT {
-   // ^^^^^^^^^ 💡 warn: Trait `BAD_TRAIT` should have CamelCase name, e.g. `BadTrait`
+   // ^^^^^^^^^ 💡 warn: Trait `BAD_TRAIT` should have UpperCamelCase name, e.g. `BadTrait`
     const bad_const: u8;
        // ^^^^^^^^^ 💡 warn: Constant `bad_const` should have UPPER_SNAKE_CASE name, e.g. `BAD_CONST`
     type BAD_TYPE;
-      // ^^^^^^^^ 💡 warn: Type alias `BAD_TYPE` should have CamelCase name, e.g. `BadType`
+      // ^^^^^^^^ 💡 warn: Type alias `BAD_TYPE` should have UpperCamelCase name, e.g. `BadType`
     fn BAD_FUNCTION();
     // ^^^^^^^^^^^^ 💡 warn: Function `BAD_FUNCTION` should have snake_case name, e.g. `bad_function`
     fn BadFunction();
@@ -552,11 +552,11 @@ trait BAD_TRAIT {
         check_diagnostics_with_disabled(
             r#"
 trait BAD_TRAIT {
-   // ^^^^^^^^^ 💡 warn: Trait `BAD_TRAIT` should have CamelCase name, e.g. `BadTrait`
+   // ^^^^^^^^^ 💡 warn: Trait `BAD_TRAIT` should have UpperCamelCase name, e.g. `BadTrait`
     const bad_const: u8;
        // ^^^^^^^^^ 💡 warn: Constant `bad_const` should have UPPER_SNAKE_CASE name, e.g. `BAD_CONST`
     type BAD_TYPE;
-      // ^^^^^^^^ 💡 warn: Type alias `BAD_TYPE` should have CamelCase name, e.g. `BadType`
+      // ^^^^^^^^ 💡 warn: Type alias `BAD_TYPE` should have UpperCamelCase name, e.g. `BadType`
     fn BAD_FUNCTION(BAD_PARAM: u8);
     // ^^^^^^^^^^^^ 💡 warn: Function `BAD_FUNCTION` should have snake_case name, e.g. `bad_function`
                  // ^^^^^^^^^ 💡 warn: Parameter `BAD_PARAM` should have snake_case name, e.g. `bad_param`
@@ -664,7 +664,7 @@ mod CheckNonstandardStyle {
 mod CheckBadStyle {
   //^^^^^^^^^^^^^ 💡 error: Module `CheckBadStyle` should have snake_case name, e.g. `check_bad_style`
     struct fooo;
-         //^^^^ 💡 error: Structure `fooo` should have CamelCase name, e.g. `Fooo`
+         //^^^^ 💡 error: Structure `fooo` should have UpperCamelCase name, e.g. `Fooo`
 }
 
 mod F {
@@ -676,7 +676,7 @@ mod F {
 
 #[deny(non_snake_case, non_camel_case_types)]
 pub struct some_type {
-         //^^^^^^^^^ 💡 error: Structure `some_type` should have CamelCase name, e.g. `SomeType`
+         //^^^^^^^^^ 💡 error: Structure `some_type` should have UpperCamelCase name, e.g. `SomeType`
     SOME_FIELD: u8,
   //^^^^^^^^^^ 💡 error: Field `SOME_FIELD` should have snake_case name, e.g. `some_field`
     SomeField: u16,
@@ -693,11 +693,11 @@ pub static SomeStatic: u8 = 10;
 
 #[deny(non_snake_case, non_camel_case_types, non_upper_case_globals)]
 trait BAD_TRAIT {
-   // ^^^^^^^^^ 💡 error: Trait `BAD_TRAIT` should have CamelCase name, e.g. `BadTrait`
+   // ^^^^^^^^^ 💡 error: Trait `BAD_TRAIT` should have UpperCamelCase name, e.g. `BadTrait`
     const bad_const: u8;
        // ^^^^^^^^^ 💡 error: Constant `bad_const` should have UPPER_SNAKE_CASE name, e.g. `BAD_CONST`
     type BAD_TYPE;
-      // ^^^^^^^^ 💡 error: Type alias `BAD_TYPE` should have CamelCase name, e.g. `BadType`
+      // ^^^^^^^^ 💡 error: Type alias `BAD_TYPE` should have UpperCamelCase name, e.g. `BadType`
     fn BAD_FUNCTION(BAD_PARAM: u8);
     // ^^^^^^^^^^^^ 💡 error: Function `BAD_FUNCTION` should have snake_case name, e.g. `bad_function`
                  // ^^^^^^^^^ 💡 error: Parameter `BAD_PARAM` should have snake_case name, e.g. `bad_param`
@@ -952,7 +952,7 @@ fn foo() {
     let FOO;
     #[allow(non_snake_case)]
     struct qux;
-        // ^^^ 💡 warn: Structure `qux` should have CamelCase name, e.g. `Qux`
+        // ^^^ 💡 warn: Structure `qux` should have UpperCamelCase name, e.g. `Qux`
 
     fn BAZ() {
     // ^^^ 💡 error: Function `BAZ` should have snake_case name, e.g. `baz`
diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs b/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs
index 6ae9dde84be..9dacbd8badf 100644
--- a/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/goto_declaration.rs
@@ -29,7 +29,7 @@ pub(crate) fn goto_declaration(
         .find(|it| matches!(it.kind(), IDENT | T![self] | T![super] | T![crate] | T![Self]))?;
     let range = original_token.text_range();
     let info: Vec<NavigationTarget> = sema
-        .descend_into_macros(original_token)
+        .descend_into_macros_no_opaque(original_token)
         .iter()
         .filter_map(|token| {
             let parent = token.parent()?;
diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
index 8836166d969..c61b2ba84f2 100644
--- a/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/goto_definition.rs
@@ -83,7 +83,7 @@ pub(crate) fn goto_definition(
     }
 
     let navs = sema
-        .descend_into_macros(original_token.clone())
+        .descend_into_macros_no_opaque(original_token.clone())
         .into_iter()
         .filter_map(|token| {
             let parent = token.parent()?;
@@ -2661,6 +2661,24 @@ fn foo() {
     }
 
     #[test]
+    fn label_inside_macro() {
+        check(
+            r#"
+macro_rules! m {
+    ($s:stmt) => { $s };
+}
+
+fn foo() {
+    'label: loop {
+ // ^^^^^^
+        m!(continue 'label$0);
+    }
+}
+"#,
+        );
+    }
+
+    #[test]
     fn goto_def_on_return_in_try() {
         check(
             r#"
diff --git a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs
index ca04b7bb5a9..c7ebd9a3531 100644
--- a/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/goto_type_definition.rs
@@ -69,7 +69,7 @@ pub(crate) fn goto_type_definition(
     }
 
     let range = token.text_range();
-    sema.descend_into_macros(token)
+    sema.descend_into_macros_no_opaque(token)
         .into_iter()
         .filter_map(|token| {
             let ty = sema
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
index 83adf6548a8..01fa316d5fc 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
@@ -3,9 +3,9 @@ use std::{mem, ops::Not};
 
 use either::Either;
 use hir::{
-    db::ExpandDatabase, Adt, AsAssocItem, AsExternAssocItem, CaptureKind, HasCrate, HasSource,
-    HirDisplay, Layout, LayoutError, MethodViolationCode, Name, ObjectSafetyViolation, Semantics,
-    Trait, Type, TypeInfo,
+    db::ExpandDatabase, Adt, AsAssocItem, AsExternAssocItem, AssocItemContainer, CaptureKind,
+    DynCompatibilityViolation, HasCrate, HasSource, HirDisplay, Layout, LayoutError,
+    MethodViolationCode, Name, Semantics, Trait, Type, TypeInfo,
 };
 use ide_db::{
     base_db::SourceDatabase,
@@ -529,10 +529,10 @@ pub(super) fn definition(
         _ => None,
     };
 
-    let object_safety_info = if let Definition::Trait(it) = def {
-        let mut object_safety_info = String::new();
-        render_object_safety(db, &mut object_safety_info, it.object_safety(db));
-        Some(object_safety_info)
+    let dyn_compatibility_info = if let Definition::Trait(it) = def {
+        let mut dyn_compatibility_info = String::new();
+        render_dyn_compatibility(db, &mut dyn_compatibility_info, it.dyn_compatibility(db));
+        Some(dyn_compatibility_info)
     } else {
         None
     };
@@ -546,8 +546,8 @@ pub(super) fn definition(
         desc.push_str(&layout_info);
         desc.push('\n');
     }
-    if let Some(object_safety_info) = object_safety_info {
-        desc.push_str(&object_safety_info);
+    if let Some(dyn_compatibility_info) = dyn_compatibility_info {
+        desc.push_str(&dyn_compatibility_info);
         desc.push('\n');
     }
     desc.push_str(&label);
@@ -813,7 +813,15 @@ fn definition_mod_path(db: &RootDatabase, def: &Definition, edition: Edition) ->
     if matches!(def, Definition::GenericParam(_) | Definition::Local(_) | Definition::Label(_)) {
         return None;
     }
-    def.module(db).map(|module| path(db, module, definition_owner_name(db, def, edition), edition))
+    let container: Option<Definition> =
+        def.as_assoc_item(db).and_then(|assoc| match assoc.container(db) {
+            AssocItemContainer::Trait(trait_) => Some(trait_.into()),
+            AssocItemContainer::Impl(impl_) => impl_.self_ty(db).as_adt().map(|adt| adt.into()),
+        });
+    container
+        .unwrap_or(*def)
+        .module(db)
+        .map(|module| path(db, module, definition_owner_name(db, def, edition), edition))
 }
 
 fn markup(docs: Option<String>, desc: String, mod_path: Option<String>) -> Markup {
@@ -980,24 +988,24 @@ fn keyword_hints(
     }
 }
 
-fn render_object_safety(
+fn render_dyn_compatibility(
     db: &RootDatabase,
     buf: &mut String,
-    safety: Option<ObjectSafetyViolation>,
+    safety: Option<DynCompatibilityViolation>,
 ) {
     let Some(osv) = safety else {
-        buf.push_str("// Object Safety: Yes");
+        buf.push_str("// Dyn Compatible: Yes");
         return;
     };
-    buf.push_str("// Object Safety: No\n// - Reason: ");
+    buf.push_str("// Dyn Compatible: No\n// - Reason: ");
     match osv {
-        ObjectSafetyViolation::SizedSelf => {
+        DynCompatibilityViolation::SizedSelf => {
             buf.push_str("has a `Self: Sized` bound");
         }
-        ObjectSafetyViolation::SelfReferential => {
+        DynCompatibilityViolation::SelfReferential => {
             buf.push_str("has a bound that references `Self`");
         }
-        ObjectSafetyViolation::Method(func, mvc) => {
+        DynCompatibilityViolation::Method(func, mvc) => {
             let name = hir::Function::from(func).name(db);
             format_to!(
                 buf,
@@ -1020,7 +1028,7 @@ fn render_object_safety(
             };
             buf.push_str(desc);
         }
-        ObjectSafetyViolation::AssocConst(const_) => {
+        DynCompatibilityViolation::AssocConst(const_) => {
             let name = hir::Const::from(const_).name(db);
             if let Some(name) = name {
                 format_to!(buf, "has an associated constant `{}`", name.as_str());
@@ -1028,11 +1036,11 @@ fn render_object_safety(
                 buf.push_str("has an associated constant");
             }
         }
-        ObjectSafetyViolation::GAT(alias) => {
+        DynCompatibilityViolation::GAT(alias) => {
             let name = hir::TypeAlias::from(alias).name(db);
             format_to!(buf, "has a generic associated type `{}`", name.as_str());
         }
-        ObjectSafetyViolation::HasNonSafeSuperTrait(super_trait) => {
+        DynCompatibilityViolation::HasNonCompatibleSuperTrait(super_trait) => {
             let name = hir::Trait::from(super_trait).name(db);
             format_to!(buf, "has a object unsafe supertrait `{}`", name.as_str());
         }
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 cca62d2181f..e60be577f79 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
@@ -7175,7 +7175,7 @@ impl T$0 for () {}
             ```
 
             ```rust
-            // Object Safety: Yes
+            // Dyn Compatible: Yes
             trait T {}
             ```
         "#]],
@@ -7195,7 +7195,7 @@ impl T$0 for () {}
             ```
 
             ```rust
-            // Object Safety: Yes
+            // Dyn Compatible: Yes
             trait T {}
             ```
         "#]],
@@ -7219,7 +7219,7 @@ impl T$0 for () {}
             ```
 
             ```rust
-            // Object Safety: No
+            // Dyn Compatible: No
             // - Reason: has a method `func` that is non dispatchable because of:
             //   - missing a receiver
             trait T { /* … */ }
@@ -7245,7 +7245,7 @@ impl T$0 for () {}
             ```
 
             ```rust
-            // Object Safety: No
+            // Dyn Compatible: No
             // - Reason: has a method `func` that is non dispatchable because of:
             //   - missing a receiver
             trait T {
@@ -7275,7 +7275,7 @@ impl T$0 for () {}
             ```
 
             ```rust
-            // Object Safety: No
+            // Dyn Compatible: No
             // - Reason: has a method `func` that is non dispatchable because of:
             //   - missing a receiver
             trait T {
@@ -7305,7 +7305,7 @@ impl T$0 for () {}
             ```
 
             ```rust
-            // Object Safety: No
+            // Dyn Compatible: No
             // - Reason: has a method `func` that is non dispatchable because of:
             //   - missing a receiver
             trait T {
@@ -8962,3 +8962,29 @@ fn test_hover_function_with_pat_param() {
         "#]],
     );
 }
+
+#[test]
+fn hover_path_inside_block_scope() {
+    check(
+        r#"
+mod m {
+    const _: () = {
+        mod m2 {
+            const C$0: () = ();
+        }
+    };
+}
+"#,
+        expect![[r#"
+            *C*
+
+            ```rust
+            test::m::m2
+            ```
+
+            ```rust
+            const C: () = ()
+            ```
+        "#]],
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/ide/src/lib.rs b/src/tools/rust-analyzer/crates/ide/src/lib.rs
index 547286c3f4d..c46c4c8ce94 100644
--- a/src/tools/rust-analyzer/crates/ide/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/lib.rs
@@ -119,8 +119,8 @@ pub use ide_assists::{
     Assist, AssistConfig, AssistId, AssistKind, AssistResolveStrategy, SingleResolve,
 };
 pub use ide_completion::{
-    CallableSnippets, CompletionConfig, CompletionItem, CompletionItemKind, CompletionRelevance,
-    Snippet, SnippetScope,
+    CallableSnippets, CompletionConfig, CompletionFieldsToResolve, CompletionItem,
+    CompletionItemKind, CompletionRelevance, Snippet, SnippetScope,
 };
 pub use ide_db::{
     base_db::{Cancelled, CrateGraph, CrateId, FileChange, SourceRoot, SourceRootId},
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs
index 927fdaa178c..961b2a4c938 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs
@@ -409,7 +409,8 @@ fn traverse(
                     let mut r = 0;
                     sema.descend_into_macros_breakable(
                         InRealFile::new(file_id, token.clone()),
-                        |tok| {
+                        |tok, _ctx| {
+                            // FIXME: Consider checking ctx transparency for being opaque?
                             let tok = tok.value;
                             let tok_kind = tok.kind();
 
diff --git a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
index baa45174236..0e1606a6991 100644
--- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
@@ -265,6 +265,11 @@ impl ProjectFolders {
                 entries.push(manifest.to_owned());
             }
 
+            for buildfile in ws.buildfiles() {
+                file_set_roots.push(VfsPath::from(buildfile.to_owned()));
+                entries.push(buildfile.to_owned());
+            }
+
             // In case of detached files we do **not** look for a rust-analyzer.toml.
             if !matches!(ws.kind, ProjectWorkspaceKind::DetachedFile { .. }) {
                 let ws_root = ws.workspace_root();
@@ -521,7 +526,7 @@ mod tests {
     #[test]
     fn test_loading_rust_analyzer() {
         let path = Path::new(env!("CARGO_MANIFEST_DIR")).parent().unwrap().parent().unwrap();
-        let cargo_config = CargoConfig::default();
+        let cargo_config = CargoConfig { set_test: true, ..CargoConfig::default() };
         let load_cargo_config = LoadCargoConfig {
             load_out_dirs_from_check: false,
             with_proc_macro_server: ProcMacroServerChoice::None,
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 dceac815e0b..7ea23b4f752 100644
--- a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs
@@ -187,6 +187,12 @@ impl<'a> Converter<'a> {
                 }
 
                 rustc_lexer::TokenKind::RawIdent => IDENT,
+
+                rustc_lexer::TokenKind::GuardedStrPrefix => {
+                    err = "Invalid string literal (reserved syntax)";
+                    ERROR
+                },
+
                 rustc_lexer::TokenKind::Literal { kind, .. } => {
                     self.extend_literal(token_text.len(), kind);
                     return;
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 7cc21bcf131..2dc6f0357e3 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
@@ -100,6 +100,7 @@ pub struct CargoConfig {
     pub invocation_strategy: InvocationStrategy,
     /// Optional path to use instead of `target` when building
     pub target_dir: Option<Utf8PathBuf>,
+    pub set_test: bool,
 }
 
 pub type Package = Idx<PackageData>;
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 30d1ddb636e..5099697a696 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/tests.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/tests.rs
@@ -35,6 +35,7 @@ fn load_cargo_with_overrides(
             rustc: Err(None),
             cargo_config_extra_env: Default::default(),
             error: None,
+            set_test: true,
         },
         cfg_overrides,
         sysroot: Sysroot::empty(),
@@ -242,6 +243,7 @@ fn smoke_test_real_sysroot_cargo() {
             rustc: Err(None),
             cargo_config_extra_env: Default::default(),
             error: None,
+            set_test: true,
         },
         sysroot,
         rustc_cfg: Vec::new(),
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 17b40a87cda..71b9b61e205 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
@@ -78,6 +78,7 @@ pub enum ProjectWorkspaceKind {
         rustc: Result<Box<(CargoWorkspace, WorkspaceBuildScripts)>, Option<String>>,
         /// Environment variables set in the `.cargo/config` file.
         cargo_config_extra_env: FxHashMap<String, String>,
+        set_test: bool,
     },
     /// Project workspace was specified using a `rust-project.json` file.
     Json(ProjectJson),
@@ -98,6 +99,7 @@ pub enum ProjectWorkspaceKind {
         cargo: Option<(CargoWorkspace, WorkspaceBuildScripts, Option<Arc<anyhow::Error>>)>,
         /// Environment variables set in the `.cargo/config` file.
         cargo_config_extra_env: FxHashMap<String, String>,
+        set_test: bool,
     },
 }
 
@@ -112,6 +114,7 @@ impl fmt::Debug for ProjectWorkspace {
                 build_scripts,
                 rustc,
                 cargo_config_extra_env,
+                set_test,
             } => f
                 .debug_struct("Cargo")
                 .field("root", &cargo.workspace_root().file_name())
@@ -126,6 +129,7 @@ impl fmt::Debug for ProjectWorkspace {
                 .field("toolchain", &toolchain)
                 .field("data_layout", &target_layout)
                 .field("cargo_config_extra_env", &cargo_config_extra_env)
+                .field("set_test", set_test)
                 .field("build_scripts", &build_scripts.error().unwrap_or("ok"))
                 .finish(),
             ProjectWorkspaceKind::Json(project) => {
@@ -137,12 +141,14 @@ impl fmt::Debug for ProjectWorkspace {
                     .field("toolchain", &toolchain)
                     .field("data_layout", &target_layout)
                     .field("n_cfg_overrides", &cfg_overrides.len());
+
                 debug_struct.finish()
             }
             ProjectWorkspaceKind::DetachedFile {
                 file,
                 cargo: cargo_script,
                 cargo_config_extra_env,
+                set_test,
             } => f
                 .debug_struct("DetachedFiles")
                 .field("file", &file)
@@ -154,6 +160,7 @@ impl fmt::Debug for ProjectWorkspace {
                 .field("data_layout", &target_layout)
                 .field("n_cfg_overrides", &cfg_overrides.len())
                 .field("cargo_config_extra_env", &cargo_config_extra_env)
+                .field("set_test", set_test)
                 .finish(),
         }
     }
@@ -329,6 +336,7 @@ impl ProjectWorkspace {
                         rustc,
                         cargo_config_extra_env,
                         error: error.map(Arc::new),
+                        set_test: config.set_test,
                     },
                     sysroot,
                     rustc_cfg,
@@ -423,6 +431,7 @@ impl ProjectWorkspace {
                 file: detached_file.to_owned(),
                 cargo: cargo_script,
                 cargo_config_extra_env,
+                set_test: config.set_test,
             },
             sysroot,
             rustc_cfg,
@@ -539,6 +548,17 @@ impl ProjectWorkspace {
         }
     }
 
+    pub fn buildfiles(&self) -> Vec<AbsPathBuf> {
+        match &self.kind {
+            ProjectWorkspaceKind::Json(project) => project
+                .crates()
+                .filter_map(|(_, krate)| krate.build.as_ref().map(|build| build.build_file.clone()))
+                .map(AbsPathBuf::assert)
+                .collect(),
+            _ => vec![],
+        }
+    }
+
     pub fn find_sysroot_proc_macro_srv(&self) -> anyhow::Result<AbsPathBuf> {
         self.sysroot.discover_proc_macro_srv()
     }
@@ -598,6 +618,7 @@ impl ProjectWorkspace {
                 build_scripts,
                 cargo_config_extra_env: _,
                 error: _,
+                set_test: _,
             } => {
                 cargo
                     .packages()
@@ -739,6 +760,7 @@ impl ProjectWorkspace {
                 build_scripts,
                 cargo_config_extra_env: _,
                 error: _,
+                set_test,
             } => (
                 cargo_to_crate_graph(
                     load,
@@ -748,10 +770,11 @@ impl ProjectWorkspace {
                     rustc_cfg.clone(),
                     cfg_overrides,
                     build_scripts,
+                    *set_test,
                 ),
                 sysroot,
             ),
-            ProjectWorkspaceKind::DetachedFile { file, cargo: cargo_script, .. } => (
+            ProjectWorkspaceKind::DetachedFile { file, cargo: cargo_script, set_test, .. } => (
                 if let Some((cargo, build_scripts, _)) = cargo_script {
                     cargo_to_crate_graph(
                         &mut |path| load(path),
@@ -761,6 +784,7 @@ impl ProjectWorkspace {
                         rustc_cfg.clone(),
                         cfg_overrides,
                         build_scripts,
+                        *set_test,
                     )
                 } else {
                     detached_file_to_crate_graph(
@@ -769,6 +793,7 @@ impl ProjectWorkspace {
                         file,
                         sysroot,
                         cfg_overrides,
+                        *set_test,
                     )
                 },
                 sysroot,
@@ -802,6 +827,7 @@ impl ProjectWorkspace {
                     cargo_config_extra_env,
                     build_scripts: _,
                     error: _,
+                    set_test: _,
                 },
                 ProjectWorkspaceKind::Cargo {
                     cargo: o_cargo,
@@ -809,6 +835,7 @@ impl ProjectWorkspace {
                     cargo_config_extra_env: o_cargo_config_extra_env,
                     build_scripts: _,
                     error: _,
+                    set_test: _,
                 },
             ) => {
                 cargo == o_cargo
@@ -823,11 +850,13 @@ impl ProjectWorkspace {
                     file,
                     cargo: Some((cargo_script, _, _)),
                     cargo_config_extra_env,
+                    set_test: _,
                 },
                 ProjectWorkspaceKind::DetachedFile {
                     file: o_file,
                     cargo: Some((o_cargo_script, _, _)),
                     cargo_config_extra_env: o_cargo_config_extra_env,
+                    set_test: _,
                 },
             ) => {
                 file == o_file
@@ -976,6 +1005,7 @@ fn cargo_to_crate_graph(
     rustc_cfg: Vec<CfgAtom>,
     override_cfg: &CfgOverrides,
     build_scripts: &WorkspaceBuildScripts,
+    set_test: bool,
 ) -> (CrateGraph, ProcMacroPaths) {
     let _p = tracing::info_span!("cargo_to_crate_graph").entered();
     let mut res = (CrateGraph::default(), ProcMacroPaths::default());
@@ -1000,8 +1030,10 @@ fn cargo_to_crate_graph(
             let mut cfg_options = cfg_options.clone();
 
             if cargo[pkg].is_local {
-                // Add test cfg for local crates
-                cfg_options.insert_atom(sym::test.clone());
+                if set_test {
+                    // Add test cfg for local crates
+                    cfg_options.insert_atom(sym::test.clone());
+                }
                 cfg_options.insert_atom(sym::rust_analyzer.clone());
             }
 
@@ -1162,6 +1194,7 @@ fn detached_file_to_crate_graph(
     detached_file: &ManifestPath,
     sysroot: &Sysroot,
     override_cfg: &CfgOverrides,
+    set_test: bool,
 ) -> (CrateGraph, ProcMacroPaths) {
     let _p = tracing::info_span!("detached_file_to_crate_graph").entered();
     let mut crate_graph = CrateGraph::default();
@@ -1169,7 +1202,9 @@ fn detached_file_to_crate_graph(
         sysroot_to_crate_graph(&mut crate_graph, sysroot, rustc_cfg.clone(), load);
 
     let mut cfg_options = CfgOptions::from_iter(rustc_cfg);
-    cfg_options.insert_atom(sym::test.clone());
+    if set_test {
+        cfg_options.insert_atom(sym::test.clone());
+    }
     cfg_options.insert_atom(sym::rust_analyzer.clone());
     override_cfg.apply(&mut cfg_options, "");
     let cfg_options = Arc::new(cfg_options);
@@ -1415,6 +1450,7 @@ fn sysroot_to_crate_graph(
                     ..Default::default()
                 },
                 &WorkspaceBuildScripts::default(),
+                false,
             );
 
             let mut pub_deps = vec![];
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs
index 41b42573f08..ecc8333503e 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/bin/main.rs
@@ -137,6 +137,7 @@ fn setup_logging(log_file_flag: Option<PathBuf>) -> anyhow::Result<()> {
         filter: env::var("RA_LOG").ok().unwrap_or_else(|| "error".to_owned()),
         chalk_filter: env::var("CHALK_DEBUG").ok(),
         profile_filter: env::var("RA_PROFILE").ok(),
+        json_profile_filter: std::env::var("RA_PROFILE_JSON").ok(),
     }
     .init()?;
 
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 e899e0e8eea..4844c514ae9 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
@@ -65,6 +65,7 @@ impl flags::AnalysisStats {
                 false => Some(RustLibSource::Discover),
             },
             all_targets: true,
+            set_test: true,
             ..Default::default()
         };
         let no_progress = &|_| ();
@@ -81,7 +82,13 @@ impl flags::AnalysisStats {
             with_proc_macro_server: if self.disable_proc_macros {
                 ProcMacroServerChoice::None
             } else {
-                ProcMacroServerChoice::Sysroot
+                match self.proc_macro_srv {
+                    Some(ref path) => {
+                        let path = vfs::AbsPathBuf::assert_utf8(path.to_owned());
+                        ProcMacroServerChoice::Explicit(path)
+                    }
+                    None => ProcMacroServerChoice::Sysroot,
+                }
             },
             prefill_caches: false,
         };
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs
index 73e71658d17..60d621b214a 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/flags.rs
@@ -76,6 +76,8 @@ xflags::xflags! {
             optional --disable-build-scripts
             /// Don't use expand proc macros.
             optional --disable-proc-macros
+            /// Run the proc-macro-srv binary at the specified path.
+            optional --proc-macro-srv path: PathBuf
             /// Skip body lowering.
             optional --skip-lowering
             /// Skip type inference.
@@ -120,7 +122,7 @@ xflags::xflags! {
             optional --disable-build-scripts
             /// Don't use expand proc macros.
             optional --disable-proc-macros
-            /// Run a custom proc-macro-srv binary.
+            /// Run the proc-macro-srv binary at the specified path.
             optional --proc-macro-srv path: PathBuf
         }
 
@@ -133,7 +135,7 @@ xflags::xflags! {
             optional --disable-build-scripts
             /// Don't use expand proc macros.
             optional --disable-proc-macros
-            /// Run a custom proc-macro-srv binary.
+            /// Run the proc-macro-srv binary at the specified path.
             optional --proc-macro-srv path: PathBuf
         }
 
@@ -233,6 +235,7 @@ pub struct AnalysisStats {
     pub no_sysroot: bool,
     pub disable_build_scripts: bool,
     pub disable_proc_macros: bool,
+    pub proc_macro_srv: Option<PathBuf>,
     pub skip_lowering: bool,
     pub skip_inference: bool,
     pub skip_mir_stats: bool,
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs
index e4263a3f667..ca8acf57bff 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs
@@ -277,6 +277,7 @@ impl flags::Lsif {
         let cargo_config = &CargoConfig {
             sysroot: Some(RustLibSource::Discover),
             all_targets: true,
+            set_test: true,
             ..Default::default()
         };
         let no_progress = &|_| ();
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs
index f90ebcfdb2e..11534bbeba9 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs
@@ -16,6 +16,7 @@ impl flags::RunTests {
         let cargo_config = CargoConfig {
             sysroot: Some(RustLibSource::Discover),
             all_targets: true,
+            set_test: true,
             ..Default::default()
         };
         let load_cargo_config = LoadCargoConfig {
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 730f3c08abb..30378db0b38 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
@@ -70,6 +70,7 @@ impl Tester {
         let cargo_config = CargoConfig {
             sysroot: Some(RustLibSource::Discover),
             all_targets: true,
+            set_test: true,
             ..Default::default()
         };
 
@@ -85,6 +86,7 @@ impl Tester {
                 file: ManifestPath::try_from(tmp_file).unwrap(),
                 cargo: None,
                 cargo_config_extra_env: Default::default(),
+                set_test: true,
             },
             sysroot,
             rustc_cfg: vec![],
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
index e9198977dea..ff009e69547 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
@@ -24,11 +24,6 @@ impl flags::Scip {
         let now = Instant::now();
 
         let no_progress = &|s| (eprintln!("rust-analyzer: Loading {s}"));
-        let load_cargo_config = LoadCargoConfig {
-            load_out_dirs_from_check: true,
-            with_proc_macro_server: ProcMacroServerChoice::Sysroot,
-            prefill_caches: true,
-        };
         let root =
             vfs::AbsPathBuf::assert_utf8(std::env::current_dir()?.join(&self.path)).normalize();
 
@@ -51,6 +46,11 @@ impl flags::Scip {
             // FIXME @alibektas : What happens to errors without logging?
             error!(?error_sink, "Config Error(s)");
         }
+        let load_cargo_config = LoadCargoConfig {
+            load_out_dirs_from_check: true,
+            with_proc_macro_server: ProcMacroServerChoice::Sysroot,
+            prefill_caches: true,
+        };
         let cargo_config = config.cargo(None);
         let (db, vfs, _) = load_workspace_at(
             root.as_path().as_ref(),
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs
index bdca800a0d6..c03688e8009 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs
@@ -13,6 +13,7 @@ impl flags::Ssr {
         let cargo_config = CargoConfig {
             sysroot: Some(RustLibSource::Discover),
             all_targets: true,
+            set_test: true,
             ..Default::default()
         };
         let load_cargo_config = LoadCargoConfig {
@@ -50,7 +51,8 @@ impl flags::Search {
     pub fn run(self) -> anyhow::Result<()> {
         use ide_db::base_db::SourceRootDatabase;
         use ide_db::symbol_index::SymbolsDatabase;
-        let cargo_config = CargoConfig::default();
+        let cargo_config =
+            CargoConfig { all_targets: true, set_test: true, ..CargoConfig::default() };
         let load_cargo_config = LoadCargoConfig {
             load_out_dirs_from_check: true,
             with_proc_macro_server: ProcMacroServerChoice::Sysroot,
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
index 4cc60695fe6..ef2e542cf22 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -12,10 +12,10 @@ use std::{
 use cfg::{CfgAtom, CfgDiff};
 use hir::Symbol;
 use ide::{
-    AssistConfig, CallableSnippets, CompletionConfig, DiagnosticsConfig, ExprFillDefaultMode,
-    GenericParameterHints, HighlightConfig, HighlightRelatedConfig, HoverConfig, HoverDocFormat,
-    InlayFieldsToResolve, InlayHintsConfig, JoinLinesConfig, MemoryLayoutHoverConfig,
-    MemoryLayoutHoverRenderKind, Snippet, SnippetScope, SourceRootId,
+    AssistConfig, CallableSnippets, CompletionConfig, CompletionFieldsToResolve, DiagnosticsConfig,
+    ExprFillDefaultMode, GenericParameterHints, HighlightConfig, HighlightRelatedConfig,
+    HoverConfig, HoverDocFormat, InlayFieldsToResolve, InlayHintsConfig, JoinLinesConfig,
+    MemoryLayoutHoverConfig, MemoryLayoutHoverRenderKind, Snippet, SnippetScope, SourceRootId,
 };
 use ide_db::{
     imports::insert_use::{ImportGranularity, InsertUseConfig, PrefixKind},
@@ -574,6 +574,9 @@ config_data! {
         /// set to a path relative to the workspace to use that path.
         cargo_targetDir | rust_analyzerTargetDir: Option<TargetDirectory> = None,
 
+        /// Set `cfg(test)` for local crates. Defaults to true.
+        cfg_setTest: bool = true,
+
         /// Run the check command for diagnostics on save.
         checkOnSave | checkOnSave_enable: bool                         = true,
 
@@ -695,7 +698,6 @@ config_data! {
         workspace_symbol_search_limit: usize = 128,
         /// Workspace symbol search scope.
         workspace_symbol_search_scope: WorkspaceSymbolSearchScopeDef = WorkspaceSymbolSearchScopeDef::Workspace,
-
     }
 }
 
@@ -1391,6 +1393,7 @@ impl Config {
     }
 
     pub fn completion(&self, source_root: Option<SourceRootId>) -> CompletionConfig {
+        let client_capability_fields = self.completion_resolve_support_properties();
         CompletionConfig {
             enable_postfix_completions: self.completion_postfix_enable(source_root).to_owned(),
             enable_imports_on_the_fly: self.completion_autoimport_enable(source_root).to_owned()
@@ -1415,6 +1418,15 @@ impl Config {
             limit: self.completion_limit(source_root).to_owned(),
             enable_term_search: self.completion_termSearch_enable(source_root).to_owned(),
             term_search_fuel: self.completion_termSearch_fuel(source_root).to_owned() as u64,
+            fields_to_resolve: CompletionFieldsToResolve {
+                resolve_label_details: client_capability_fields.contains("labelDetails"),
+                resolve_tags: client_capability_fields.contains("tags"),
+                resolve_detail: client_capability_fields.contains("detail"),
+                resolve_documentation: client_capability_fields.contains("documentation"),
+                resolve_filter_text: client_capability_fields.contains("filterText"),
+                resolve_text_edit: client_capability_fields.contains("textEdit"),
+                resolve_command: client_capability_fields.contains("command"),
+            },
         }
     }
 
@@ -1859,9 +1871,14 @@ impl Config {
             extra_args: self.cargo_extraArgs(source_root).clone(),
             extra_env: self.cargo_extraEnv(source_root).clone(),
             target_dir: self.target_dir_from_config(source_root),
+            set_test: *self.cfg_setTest(source_root),
         }
     }
 
+    pub fn cfg_set_test(&self, source_root: Option<SourceRootId>) -> bool {
+        *self.cfg_setTest(source_root)
+    }
+
     pub(crate) fn completion_snippets_default() -> FxHashMap<String, SnippetDef> {
         serde_json::from_str(
             r#"{
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs
index 7e9162eee6e..96b164228ef 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs
@@ -7,6 +7,7 @@ use paths::{AbsPathBuf, Utf8Path, Utf8PathBuf};
 use project_model::ProjectJsonData;
 use serde::{Deserialize, Serialize};
 use serde_json::Value;
+use tracing::{info_span, span::EnteredSpan};
 
 use crate::command::{CommandHandle, ParseFromLine};
 
@@ -60,7 +61,10 @@ impl DiscoverCommand {
         let mut cmd = Command::new(command);
         cmd.args(args);
 
-        Ok(DiscoverHandle { _handle: CommandHandle::spawn(cmd, self.sender.clone())? })
+        Ok(DiscoverHandle {
+            _handle: CommandHandle::spawn(cmd, self.sender.clone())?,
+            span: info_span!("discover_command").entered(),
+        })
     }
 }
 
@@ -68,6 +72,8 @@ impl DiscoverCommand {
 #[derive(Debug)]
 pub(crate) struct DiscoverHandle {
     _handle: CommandHandle<DiscoverProjectMessage>,
+    #[allow(dead_code)] // not accessed, but used to log on drop.
+    span: EnteredSpan,
 }
 
 /// An enum containing either progress messages, an error,
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
index 89487aa673b..c3142c9cfca 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs
@@ -46,6 +46,11 @@ pub(crate) struct FetchWorkspaceRequest {
     pub(crate) force_crate_graph_reload: bool,
 }
 
+pub(crate) struct FetchWorkspaceResponse {
+    pub(crate) workspaces: Vec<anyhow::Result<ProjectWorkspace>>,
+    pub(crate) force_crate_graph_reload: bool,
+}
+
 // Enforces drop order
 pub(crate) struct Handle<H, C> {
     pub(crate) handle: H,
@@ -111,6 +116,9 @@ pub(crate) struct GlobalState {
     pub(crate) vfs_config_version: u32,
     pub(crate) vfs_progress_config_version: u32,
     pub(crate) vfs_done: bool,
+    // used to track how long VFS loading takes. this can't be on `vfs::loader::Handle`,
+    // as that handle's lifetime is the same as `GlobalState` itself.
+    pub(crate) vfs_span: Option<tracing::span::EnteredSpan>,
     pub(crate) wants_to_switch: Option<Cause>,
 
     /// `workspaces` field stores the data we actually use, while the `OpQueue`
@@ -143,8 +151,7 @@ pub(crate) struct GlobalState {
     pub(crate) detached_files: FxHashSet<ManifestPath>,
 
     // op queues
-    pub(crate) fetch_workspaces_queue:
-        OpQueue<FetchWorkspaceRequest, Option<(Vec<anyhow::Result<ProjectWorkspace>>, bool)>>,
+    pub(crate) fetch_workspaces_queue: OpQueue<FetchWorkspaceRequest, FetchWorkspaceResponse>,
     pub(crate) fetch_build_data_queue:
         OpQueue<(), (Arc<Vec<ProjectWorkspace>>, Vec<anyhow::Result<WorkspaceBuildScripts>>)>,
     pub(crate) fetch_proc_macros_queue: OpQueue<Vec<ProcMacroPaths>, bool>,
@@ -253,6 +260,7 @@ impl GlobalState {
             vfs: Arc::new(RwLock::new((vfs::Vfs::default(), IntMap::default()))),
             vfs_config_version: 0,
             vfs_progress_config_version: 0,
+            vfs_span: None,
             vfs_done: true,
             wants_to_switch: None,
 
@@ -498,7 +506,7 @@ impl GlobalState {
             mem_docs: self.mem_docs.clone(),
             semantic_tokens_cache: Arc::clone(&self.semantic_tokens_cache),
             proc_macros_loaded: !self.config.expand_proc_macros()
-                || *self.fetch_proc_macros_queue.last_op_result(),
+                || self.fetch_proc_macros_queue.last_op_result().copied().unwrap_or(false),
             flycheck: self.flycheck.clone(),
         }
     }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
index 49b1ba32a79..bb03eb3c89b 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
@@ -380,7 +380,7 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool {
                     if id == flycheck.id() {
                         updated = true;
                         match package.filter(|_| {
-                            !world.config.flycheck_workspace(source_root_id) || target.is_some()
+                            !world.config.flycheck_workspace(source_root_id) && target.is_some()
                         }) {
                             Some(package) => flycheck
                                 .restart_for_package(package, target.clone().map(TupleExt::head)),
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
index bcbd970a0d2..9773d8dbce0 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/request.rs
@@ -10,9 +10,9 @@ use std::{
 use anyhow::Context;
 
 use ide::{
-    AnnotationConfig, AssistKind, AssistResolveStrategy, Cancellable, FilePosition, FileRange,
-    HoverAction, HoverGotoTypeData, InlayFieldsToResolve, Query, RangeInfo, ReferenceCategory,
-    Runnable, RunnableKind, SingleResolve, SourceChange, TextEdit,
+    AnnotationConfig, AssistKind, AssistResolveStrategy, Cancellable, CompletionFieldsToResolve,
+    FilePosition, FileRange, HoverAction, HoverGotoTypeData, InlayFieldsToResolve, Query,
+    RangeInfo, ReferenceCategory, Runnable, RunnableKind, SingleResolve, SourceChange, TextEdit,
 };
 use ide_db::SymbolKind;
 use itertools::Itertools;
@@ -1019,9 +1019,11 @@ pub(crate) fn handle_completion(
 
     let items = to_proto::completion_items(
         &snap.config,
+        &completion_config.fields_to_resolve,
         &line_index,
         snap.file_version(position.file_id),
         text_document_position,
+        completion_trigger_character,
         items,
     );
 
@@ -1054,36 +1056,70 @@ pub(crate) fn handle_completion_resolve(
     };
     let source_root = snap.analysis.source_root_id(file_id)?;
 
-    let additional_edits = snap
-        .analysis
-        .resolve_completion_edits(
-            &snap.config.completion(Some(source_root)),
-            FilePosition { file_id, offset },
-            resolve_data
-                .imports
-                .into_iter()
-                .map(|import| (import.full_import_path, import.imported_name)),
-        )?
-        .into_iter()
-        .flat_map(|edit| edit.into_iter().map(|indel| to_proto::text_edit(&line_index, indel)))
-        .collect::<Vec<_>>();
+    let mut forced_resolve_completions_config = snap.config.completion(Some(source_root));
+    forced_resolve_completions_config.fields_to_resolve = CompletionFieldsToResolve::empty();
 
-    if !all_edits_are_disjoint(&original_completion, &additional_edits) {
-        return Err(LspError::new(
-            ErrorCode::InternalError as i32,
-            "Import edit overlaps with the original completion edits, this is not LSP-compliant"
-                .into(),
-        )
-        .into());
-    }
+    let position = FilePosition { file_id, offset };
+    let Some(resolved_completions) = snap.analysis.completions(
+        &forced_resolve_completions_config,
+        position,
+        resolve_data.trigger_character,
+    )?
+    else {
+        return Ok(original_completion);
+    };
+    let resolved_completions = to_proto::completion_items(
+        &snap.config,
+        &forced_resolve_completions_config.fields_to_resolve,
+        &line_index,
+        snap.file_version(position.file_id),
+        resolve_data.position,
+        resolve_data.trigger_character,
+        resolved_completions,
+    );
+    let Some(mut resolved_completion) = resolved_completions.into_iter().find(|completion| {
+        completion.label == original_completion.label
+            && completion.kind == original_completion.kind
+            && completion.deprecated == original_completion.deprecated
+            && completion.preselect == original_completion.preselect
+            && completion.sort_text == original_completion.sort_text
+    }) else {
+        return Ok(original_completion);
+    };
 
-    if let Some(original_additional_edits) = original_completion.additional_text_edits.as_mut() {
-        original_additional_edits.extend(additional_edits)
-    } else {
-        original_completion.additional_text_edits = Some(additional_edits);
+    if !resolve_data.imports.is_empty() {
+        let additional_edits = snap
+            .analysis
+            .resolve_completion_edits(
+                &forced_resolve_completions_config,
+                position,
+                resolve_data
+                    .imports
+                    .into_iter()
+                    .map(|import| (import.full_import_path, import.imported_name)),
+            )?
+            .into_iter()
+            .flat_map(|edit| edit.into_iter().map(|indel| to_proto::text_edit(&line_index, indel)))
+            .collect::<Vec<_>>();
+
+        if !all_edits_are_disjoint(&resolved_completion, &additional_edits) {
+            return Err(LspError::new(
+                ErrorCode::InternalError as i32,
+                "Import edit overlaps with the original completion edits, this is not LSP-compliant"
+                    .into(),
+            )
+            .into());
+        }
+
+        if let Some(original_additional_edits) = resolved_completion.additional_text_edits.as_mut()
+        {
+            original_additional_edits.extend(additional_edits)
+        } else {
+            resolved_completion.additional_text_edits = Some(additional_edits);
+        }
     }
 
-    Ok(original_completion)
+    Ok(resolved_completion)
 }
 
 pub(crate) fn handle_folding_range(
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
index 118469df730..8946c7acb93 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
@@ -12,7 +12,8 @@
 
 use hir::ChangeWithProcMacros;
 use ide::{
-    AnalysisHost, CallableSnippets, CompletionConfig, DiagnosticsConfig, FilePosition, TextSize,
+    AnalysisHost, CallableSnippets, CompletionConfig, CompletionFieldsToResolve, DiagnosticsConfig,
+    FilePosition, TextSize,
 };
 use ide_db::{
     imports::insert_use::{ImportGranularity, InsertUseConfig},
@@ -36,6 +37,8 @@ fn integrated_highlighting_benchmark() {
 
     let cargo_config = CargoConfig {
         sysroot: Some(project_model::RustLibSource::Discover),
+        all_targets: true,
+        set_test: true,
         ..CargoConfig::default()
     };
     let load_cargo_config = LoadCargoConfig {
@@ -102,6 +105,8 @@ fn integrated_completion_benchmark() {
 
     let cargo_config = CargoConfig {
         sysroot: Some(project_model::RustLibSource::Discover),
+        all_targets: true,
+        set_test: true,
         ..CargoConfig::default()
     };
     let load_cargo_config = LoadCargoConfig {
@@ -168,6 +173,7 @@ fn integrated_completion_benchmark() {
             snippets: Vec::new(),
             limit: None,
             add_semicolon_to_unit: true,
+            fields_to_resolve: CompletionFieldsToResolve::empty(),
         };
         let position =
             FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() };
@@ -215,6 +221,7 @@ fn integrated_completion_benchmark() {
             snippets: Vec::new(),
             limit: None,
             add_semicolon_to_unit: true,
+            fields_to_resolve: CompletionFieldsToResolve::empty(),
         };
         let position =
             FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() };
@@ -260,6 +267,7 @@ fn integrated_completion_benchmark() {
             snippets: Vec::new(),
             limit: None,
             add_semicolon_to_unit: true,
+            fields_to_resolve: CompletionFieldsToResolve::empty(),
         };
         let position =
             FilePosition { file_id, offset: TextSize::try_from(completion_offset).unwrap() };
@@ -279,6 +287,8 @@ fn integrated_diagnostics_benchmark() {
 
     let cargo_config = CargoConfig {
         sysroot: Some(project_model::RustLibSource::Discover),
+        all_targets: true,
+        set_test: true,
         ..CargoConfig::default()
     };
     let load_cargo_config = LoadCargoConfig {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs
index 714991e8116..234204695cb 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lib.rs
@@ -34,6 +34,7 @@ mod handlers {
 
 pub mod tracing {
     pub mod config;
+    pub mod json;
     pub use config::Config;
     pub mod hprof;
 }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/capabilities.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/capabilities.rs
index 9610808c27e..3b19284f241 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/capabilities.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/capabilities.rs
@@ -448,7 +448,7 @@ impl ClientCapabilities {
             .unwrap_or_default()
     }
 
-    pub fn inlay_hint_resolve_support_properties(&self) -> FxHashSet<String> {
+    pub fn inlay_hint_resolve_support_properties(&self) -> FxHashSet<&str> {
         self.0
             .text_document
             .as_ref()
@@ -457,8 +457,22 @@ impl ClientCapabilities {
             .map(|inlay_resolve| inlay_resolve.properties.iter())
             .into_iter()
             .flatten()
-            .cloned()
-            .collect::<FxHashSet<_>>()
+            .map(|s| s.as_str())
+            .collect()
+    }
+
+    pub fn completion_resolve_support_properties(&self) -> FxHashSet<&str> {
+        self.0
+            .text_document
+            .as_ref()
+            .and_then(|text| text.completion.as_ref())
+            .and_then(|completion_caps| completion_caps.completion_item.as_ref())
+            .and_then(|completion_item_caps| completion_item_caps.resolve_support.as_ref())
+            .map(|resolve_support| resolve_support.properties.iter())
+            .into_iter()
+            .flatten()
+            .map(|s| s.as_str())
+            .collect()
     }
 
     pub fn hover_markdown_support(&self) -> bool {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs
index 618481bbc66..8039f0644ee 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/ext.rs
@@ -825,6 +825,7 @@ pub struct CompletionResolveData {
     pub position: lsp_types::TextDocumentPositionParams,
     pub imports: Vec<CompletionImport>,
     pub version: Option<i32>,
+    pub trigger_character: Option<char>,
 }
 
 #[derive(Debug, Serialize, Deserialize)]
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 4902c9f88c1..375b7428c2d 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
@@ -6,9 +6,9 @@ use std::{
 };
 
 use ide::{
-    Annotation, AnnotationKind, Assist, AssistKind, Cancellable, CompletionItem,
-    CompletionItemKind, CompletionRelevance, Documentation, FileId, FileRange, FileSystemEdit,
-    Fold, FoldKind, Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel,
+    Annotation, AnnotationKind, Assist, AssistKind, Cancellable, CompletionFieldsToResolve,
+    CompletionItem, CompletionItemKind, CompletionRelevance, Documentation, FileId, FileRange,
+    FileSystemEdit, Fold, FoldKind, Highlight, HlMod, HlOperator, HlPunct, HlRange, HlTag, Indel,
     InlayFieldsToResolve, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayKind, Markup,
     NavigationTarget, ReferenceCategory, RenameError, Runnable, Severity, SignatureHelp,
     SnippetEdit, SourceChange, StructureNodeKind, SymbolKind, TextEdit, TextRange, TextSize,
@@ -227,9 +227,11 @@ pub(crate) fn snippet_text_edit_vec(
 
 pub(crate) fn completion_items(
     config: &Config,
+    fields_to_resolve: &CompletionFieldsToResolve,
     line_index: &LineIndex,
     version: Option<i32>,
     tdpp: lsp_types::TextDocumentPositionParams,
+    completion_trigger_character: Option<char>,
     mut items: Vec<CompletionItem>,
 ) -> Vec<lsp_types::CompletionItem> {
     if config.completion_hide_deprecated() {
@@ -239,7 +241,17 @@ pub(crate) fn completion_items(
     let max_relevance = items.iter().map(|it| it.relevance.score()).max().unwrap_or_default();
     let mut res = Vec::with_capacity(items.len());
     for item in items {
-        completion_item(&mut res, config, line_index, version, &tdpp, max_relevance, item);
+        completion_item(
+            &mut res,
+            config,
+            fields_to_resolve,
+            line_index,
+            version,
+            &tdpp,
+            max_relevance,
+            completion_trigger_character,
+            item,
+        );
     }
 
     if let Some(limit) = config.completion(None).limit {
@@ -253,21 +265,33 @@ pub(crate) fn completion_items(
 fn completion_item(
     acc: &mut Vec<lsp_types::CompletionItem>,
     config: &Config,
+    fields_to_resolve: &CompletionFieldsToResolve,
     line_index: &LineIndex,
     version: Option<i32>,
     tdpp: &lsp_types::TextDocumentPositionParams,
     max_relevance: u32,
+    completion_trigger_character: Option<char>,
     item: CompletionItem,
 ) {
     let insert_replace_support = config.insert_replace_support().then_some(tdpp.position);
     let ref_match = item.ref_match();
-    let lookup = item.lookup().to_owned();
 
     let mut additional_text_edits = Vec::new();
+    let mut something_to_resolve = false;
 
-    // LSP does not allow arbitrary edits in completion, so we have to do a
-    // non-trivial mapping here.
-    let text_edit = {
+    let filter_text = if fields_to_resolve.resolve_filter_text {
+        something_to_resolve = !item.lookup().is_empty();
+        None
+    } else {
+        Some(item.lookup().to_owned())
+    };
+
+    let text_edit = if fields_to_resolve.resolve_text_edit {
+        something_to_resolve = true;
+        None
+    } else {
+        // LSP does not allow arbitrary edits in completion, so we have to do a
+        // non-trivial mapping here.
         let mut text_edit = None;
         let source_range = item.source_range;
         for indel in item.text_edit {
@@ -290,25 +314,49 @@ fn completion_item(
                 additional_text_edits.push(text_edit);
             }
         }
-        text_edit.unwrap()
+        Some(text_edit.unwrap())
     };
 
     let insert_text_format = item.is_snippet.then_some(lsp_types::InsertTextFormat::SNIPPET);
-    let tags = item.deprecated.then(|| vec![lsp_types::CompletionItemTag::DEPRECATED]);
+    let tags = if fields_to_resolve.resolve_tags {
+        something_to_resolve = item.deprecated;
+        None
+    } else {
+        item.deprecated.then(|| vec![lsp_types::CompletionItemTag::DEPRECATED])
+    };
     let command = if item.trigger_call_info && config.client_commands().trigger_parameter_hints {
-        Some(command::trigger_parameter_hints())
+        if fields_to_resolve.resolve_command {
+            something_to_resolve = true;
+            None
+        } else {
+            Some(command::trigger_parameter_hints())
+        }
+    } else {
+        None
+    };
+
+    let detail = if fields_to_resolve.resolve_detail {
+        something_to_resolve = item.detail.is_some();
+        None
     } else {
+        item.detail
+    };
+
+    let documentation = if fields_to_resolve.resolve_documentation {
+        something_to_resolve = item.documentation.is_some();
         None
+    } else {
+        item.documentation.map(documentation)
     };
 
     let mut lsp_item = lsp_types::CompletionItem {
         label: item.label.to_string(),
-        detail: item.detail,
-        filter_text: Some(lookup),
+        detail,
+        filter_text,
         kind: Some(completion_item_kind(item.kind)),
-        text_edit: Some(text_edit),
+        text_edit,
         additional_text_edits: Some(additional_text_edits),
-        documentation: item.documentation.map(documentation),
+        documentation,
         deprecated: Some(item.deprecated),
         tags,
         command,
@@ -317,29 +365,40 @@ fn completion_item(
     };
 
     if config.completion_label_details_support() {
-        lsp_item.label_details = Some(lsp_types::CompletionItemLabelDetails {
-            detail: item.label_detail.as_ref().map(ToString::to_string),
-            description: lsp_item.detail.clone(),
-        });
+        if fields_to_resolve.resolve_label_details {
+            something_to_resolve = true;
+        } else {
+            lsp_item.label_details = Some(lsp_types::CompletionItemLabelDetails {
+                detail: item.label_detail.as_ref().map(ToString::to_string),
+                description: lsp_item.detail.clone(),
+            });
+        }
     } else if let Some(label_detail) = item.label_detail {
         lsp_item.label.push_str(label_detail.as_str());
     }
 
     set_score(&mut lsp_item, max_relevance, item.relevance);
 
-    if config.completion(None).enable_imports_on_the_fly && !item.import_to_add.is_empty() {
-        let imports = item
-            .import_to_add
-            .into_iter()
-            .map(|(import_path, import_name)| lsp_ext::CompletionImport {
-                full_import_path: import_path,
-                imported_name: import_name,
-            })
-            .collect::<Vec<_>>();
-        if !imports.is_empty() {
-            let data = lsp_ext::CompletionResolveData { position: tdpp.clone(), imports, version };
-            lsp_item.data = Some(to_value(data).unwrap());
-        }
+    let imports =
+        if config.completion(None).enable_imports_on_the_fly && !item.import_to_add.is_empty() {
+            item.import_to_add
+                .into_iter()
+                .map(|(import_path, import_name)| lsp_ext::CompletionImport {
+                    full_import_path: import_path,
+                    imported_name: import_name,
+                })
+                .collect()
+        } else {
+            Vec::new()
+        };
+    if something_to_resolve || !imports.is_empty() {
+        let data = lsp_ext::CompletionResolveData {
+            position: tdpp.clone(),
+            imports,
+            version,
+            trigger_character: completion_trigger_character,
+        };
+        lsp_item.data = Some(to_value(data).unwrap());
     }
 
     if let Some((label, indel, relevance)) = ref_match {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
index 83559230257..ef289720568 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
@@ -22,7 +22,9 @@ use crate::{
     diagnostics::{fetch_native_diagnostics, DiagnosticsGeneration, NativeDiagnosticsFetchKind},
     discover::{DiscoverArgument, DiscoverCommand, DiscoverProjectMessage},
     flycheck::{self, FlycheckMessage},
-    global_state::{file_id_to_url, url_to_file_id, FetchWorkspaceRequest, GlobalState},
+    global_state::{
+        file_id_to_url, url_to_file_id, FetchWorkspaceRequest, FetchWorkspaceResponse, GlobalState,
+    },
     hack_recover_crate_name,
     handlers::dispatch::{NotificationDispatcher, RequestDispatcher},
     lsp::{
@@ -695,9 +697,9 @@ impl GlobalState {
                 let (state, msg) = match progress {
                     ProjectWorkspaceProgress::Begin => (Progress::Begin, None),
                     ProjectWorkspaceProgress::Report(msg) => (Progress::Report, Some(msg)),
-                    ProjectWorkspaceProgress::End(workspaces, force_reload_crate_graph) => {
-                        self.fetch_workspaces_queue
-                            .op_completed(Some((workspaces, force_reload_crate_graph)));
+                    ProjectWorkspaceProgress::End(workspaces, force_crate_graph_reload) => {
+                        let resp = FetchWorkspaceResponse { workspaces, force_crate_graph_reload };
+                        self.fetch_workspaces_queue.op_completed(resp);
                         if let Err(e) = self.fetch_workspace_error() {
                             error!("FetchWorkspaceError: {e}");
                         }
@@ -794,13 +796,20 @@ impl GlobalState {
                 }
             }
             vfs::loader::Message::Progress { n_total, n_done, dir, config_version } => {
-                let _p = tracing::info_span!("GlobalState::handle_vfs_mgs/progress").entered();
+                let _p = span!(Level::INFO, "GlobalState::handle_vfs_mgs/progress").entered();
                 always!(config_version <= self.vfs_config_version);
 
                 let (n_done, state) = match n_done {
-                    LoadingProgress::Started => (0, Progress::Begin),
+                    LoadingProgress::Started => {
+                        self.vfs_span =
+                            Some(span!(Level::INFO, "vfs_load", total = n_total).entered());
+                        (0, Progress::Begin)
+                    }
                     LoadingProgress::Progress(n_done) => (n_done.min(n_total), Progress::Report),
-                    LoadingProgress::Finished => (n_total, Progress::End),
+                    LoadingProgress::Finished => {
+                        self.vfs_span = None;
+                        (n_total, Progress::End)
+                    }
                 };
 
                 self.vfs_progress_config_version = config_version;
@@ -881,6 +890,7 @@ impl GlobalState {
             .expect("No title could be found; this is a bug");
         match message {
             DiscoverProjectMessage::Finished { project, buildfile } => {
+                self.discover_handle = None;
                 self.report_progress(&title, Progress::End, None, None, None);
                 self.discover_workspace_queue.op_completed(());
 
@@ -892,6 +902,7 @@ impl GlobalState {
                 self.report_progress(&title, Progress::Report, Some(message), None, None)
             }
             DiscoverProjectMessage::Error { error, source } => {
+                self.discover_handle = None;
                 let message = format!("Project discovery failed: {error}");
                 self.discover_workspace_queue.op_completed(());
                 self.show_and_log_error(message.clone(), source);
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs
index 5c4c858e150..123f20605ab 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/op_queue.rs
@@ -27,12 +27,12 @@ pub(crate) type Cause = String;
 pub(crate) struct OpQueue<Args = (), Output = ()> {
     op_requested: Option<(Cause, Args)>,
     op_in_progress: bool,
-    last_op_result: Output,
+    last_op_result: Option<Output>,
 }
 
-impl<Args, Output: Default> Default for OpQueue<Args, Output> {
+impl<Args, Output> Default for OpQueue<Args, Output> {
     fn default() -> Self {
-        Self { op_requested: None, op_in_progress: false, last_op_result: Default::default() }
+        Self { op_requested: None, op_in_progress: false, last_op_result: None }
     }
 }
 
@@ -56,12 +56,12 @@ impl<Args, Output> OpQueue<Args, Output> {
     pub(crate) fn op_completed(&mut self, result: Output) {
         assert!(self.op_in_progress);
         self.op_in_progress = false;
-        self.last_op_result = result;
+        self.last_op_result = Some(result);
     }
 
     /// Get the result of the last operation.
-    pub(crate) fn last_op_result(&self) -> &Output {
-        &self.last_op_result
+    pub(crate) fn last_op_result(&self) -> Option<&Output> {
+        self.last_op_result.as_ref()
     }
 
     // Is there an operation that has started, but hasn't yet finished?
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
index f6765715c5a..60ee0295a3a 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
@@ -33,7 +33,7 @@ use vfs::{AbsPath, AbsPathBuf, ChangeKind};
 use crate::{
     config::{Config, FilesWatcher, LinkedProject},
     flycheck::{FlycheckConfig, FlycheckHandle},
-    global_state::{FetchWorkspaceRequest, GlobalState},
+    global_state::{FetchWorkspaceRequest, FetchWorkspaceResponse, GlobalState},
     lsp_ext,
     main_loop::{DiscoverProjectParam, Task},
     op_queue::Cause,
@@ -448,15 +448,15 @@ impl GlobalState {
         let _p = tracing::info_span!("GlobalState::switch_workspaces").entered();
         tracing::info!(%cause, "will switch workspaces");
 
-        let Some((workspaces, force_reload_crate_graph)) =
+        let Some(FetchWorkspaceResponse { workspaces, force_crate_graph_reload }) =
             self.fetch_workspaces_queue.last_op_result()
         else {
             return;
         };
 
-        info!(%cause, ?force_reload_crate_graph);
+        info!(%cause, ?force_crate_graph_reload);
         if self.fetch_workspace_error().is_err() && !self.workspaces.is_empty() {
-            if *force_reload_crate_graph {
+            if *force_crate_graph_reload {
                 self.recreate_crate_graph(cause);
             }
             // It only makes sense to switch to a partially broken workspace
@@ -474,8 +474,12 @@ impl GlobalState {
                 .all(|(l, r)| l.eq_ignore_build_data(r));
 
         if same_workspaces {
-            let (workspaces, build_scripts) = self.fetch_build_data_queue.last_op_result();
-            if Arc::ptr_eq(workspaces, &self.workspaces) {
+            let (workspaces, build_scripts) = match self.fetch_build_data_queue.last_op_result() {
+                Some((workspaces, build_scripts)) => (workspaces.clone(), build_scripts.as_slice()),
+                None => (Default::default(), Default::default()),
+            };
+
+            if Arc::ptr_eq(&workspaces, &self.workspaces) {
                 info!("set build scripts to workspaces");
 
                 let workspaces = workspaces
@@ -492,7 +496,7 @@ impl GlobalState {
                 self.workspaces = Arc::new(workspaces);
             } else {
                 info!("build scripts do not match the version of the active workspace");
-                if *force_reload_crate_graph {
+                if *force_crate_graph_reload {
                     self.recreate_crate_graph(cause);
                 }
 
@@ -739,22 +743,18 @@ impl GlobalState {
     pub(super) fn fetch_workspace_error(&self) -> Result<(), String> {
         let mut buf = String::new();
 
-        let Some((last_op_result, _)) = self.fetch_workspaces_queue.last_op_result() else {
+        let Some(FetchWorkspaceResponse { workspaces, .. }) =
+            self.fetch_workspaces_queue.last_op_result()
+        else {
             return Ok(());
         };
 
-        if !self.discover_workspace_queue.op_in_progress() {
-            if last_op_result.is_empty() {
-                stdx::format_to!(buf, "rust-analyzer failed to discover workspace");
-            } else {
-                for ws in last_op_result {
-                    if let Err(err) = ws {
-                        stdx::format_to!(
-                            buf,
-                            "rust-analyzer failed to load workspace: {:#}\n",
-                            err
-                        );
-                    }
+        if workspaces.is_empty() && self.config.discover_workspace_config().is_none() {
+            stdx::format_to!(buf, "rust-analyzer failed to fetch workspace");
+        } else {
+            for ws in workspaces {
+                if let Err(err) = ws {
+                    stdx::format_to!(buf, "rust-analyzer failed to load workspace: {:#}\n", err);
                 }
             }
         }
@@ -769,7 +769,11 @@ impl GlobalState {
     pub(super) fn fetch_build_data_error(&self) -> Result<(), String> {
         let mut buf = String::new();
 
-        for ws in &self.fetch_build_data_queue.last_op_result().1 {
+        let Some((_, ws)) = &self.fetch_build_data_queue.last_op_result() else {
+            return Ok(());
+        };
+
+        for ws in ws {
             match ws {
                 Ok(data) => {
                     if let Some(stderr) = data.error() {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/tracing/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/tracing/config.rs
index f330754f19a..b73f6e77514 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/tracing/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/tracing/config.rs
@@ -1,17 +1,20 @@
 //! Simple logger that logs either to stderr or to a file, using `tracing_subscriber`
 //! filter syntax and `tracing_appender` for non blocking output.
 
-use std::io;
+use std::io::{self};
 
 use anyhow::Context;
 use tracing::level_filters::LevelFilter;
 use tracing_subscriber::{
-    filter::Targets, fmt::MakeWriter, layer::SubscriberExt, util::SubscriberInitExt, Layer,
-    Registry,
+    filter::{filter_fn, Targets},
+    fmt::MakeWriter,
+    layer::SubscriberExt,
+    Layer, Registry,
 };
 use tracing_tree::HierarchicalLayer;
 
 use crate::tracing::hprof;
+use crate::tracing::json;
 
 #[derive(Debug)]
 pub struct Config<T> {
@@ -34,6 +37,12 @@ pub struct Config<T> {
     /// env RA_PROFILE=*@3>10        // dump everything, up to depth 3, if it takes more than 10
     /// ```
     pub profile_filter: Option<String>,
+
+    /// Filtering syntax, set in a shell:
+    /// ```
+    /// env RA_PROFILE_JSON=foo|bar|baz
+    /// ```
+    pub json_profile_filter: Option<String>,
 }
 
 impl<T> Config<T>
@@ -41,7 +50,7 @@ where
     T: for<'writer> MakeWriter<'writer> + Send + Sync + 'static,
 {
     pub fn init(self) -> anyhow::Result<()> {
-        let filter: Targets = self
+        let targets_filter: Targets = self
             .filter
             .parse()
             .with_context(|| format!("invalid log filter: `{}`", self.filter))?;
@@ -50,31 +59,60 @@ where
 
         let ra_fmt_layer = tracing_subscriber::fmt::layer()
             .with_target(false)
+            .with_ansi(false)
             .with_writer(writer)
-            .with_filter(filter);
-
-        let mut chalk_layer = None;
-        if let Some(chalk_filter) = self.chalk_filter {
-            let level: LevelFilter =
-                chalk_filter.parse().with_context(|| "invalid chalk log filter")?;
-
-            let chalk_filter = Targets::new()
-                .with_target("chalk_solve", level)
-                .with_target("chalk_ir", level)
-                .with_target("chalk_recursive", level);
-            chalk_layer = Some(
+            .with_filter(targets_filter);
+
+        let chalk_layer = match self.chalk_filter {
+            Some(chalk_filter) => {
+                let level: LevelFilter =
+                    chalk_filter.parse().with_context(|| "invalid chalk log filter")?;
+
+                let chalk_filter = Targets::new()
+                    .with_target("chalk_solve", level)
+                    .with_target("chalk_ir", level)
+                    .with_target("chalk_recursive", level);
+                // TODO: remove `.with_filter(LevelFilter::OFF)` on the `None` branch.
                 HierarchicalLayer::default()
                     .with_indent_lines(true)
                     .with_ansi(false)
                     .with_indent_amount(2)
                     .with_writer(io::stderr)
-                    .with_filter(chalk_filter),
-            );
+                    .with_filter(chalk_filter)
+                    .boxed()
+            }
+            None => None::<HierarchicalLayer>.with_filter(LevelFilter::OFF).boxed(),
+        };
+
+        // TODO: remove `.with_filter(LevelFilter::OFF)` on the `None` branch.
+        let profiler_layer = match self.profile_filter {
+            Some(spec) => Some(hprof::SpanTree::new(&spec)).with_filter(LevelFilter::INFO),
+            None => None.with_filter(LevelFilter::OFF),
+        };
+
+        let json_profiler_layer = match self.json_profile_filter {
+            Some(spec) => {
+                let filter = json::JsonFilter::from_spec(&spec);
+                let filter = filter_fn(move |metadata| {
+                    let allowed = match &filter.allowed_names {
+                        Some(names) => names.contains(metadata.name()),
+                        None => true,
+                    };
+
+                    allowed && metadata.is_span()
+                });
+                Some(json::TimingLayer::new(std::io::stderr).with_filter(filter))
+            }
+            None => None,
         };
 
-        let profiler_layer = self.profile_filter.map(|spec| hprof::layer(&spec));
+        let subscriber = Registry::default()
+            .with(ra_fmt_layer)
+            .with(json_profiler_layer)
+            .with(profiler_layer)
+            .with(chalk_layer);
 
-        Registry::default().with(ra_fmt_layer).with(chalk_layer).with(profiler_layer).try_init()?;
+        tracing::subscriber::set_global_default(subscriber)?;
 
         Ok(())
     }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/tracing/hprof.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/tracing/hprof.rs
index 2d1604e70be..cad92962f34 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/tracing/hprof.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/tracing/hprof.rs
@@ -33,6 +33,7 @@
 
 use std::{
     fmt::Write,
+    marker::PhantomData,
     mem,
     time::{Duration, Instant},
 };
@@ -50,53 +51,42 @@ use tracing_subscriber::{
     Layer, Registry,
 };
 
-use crate::tracing::hprof;
-
 pub fn init(spec: &str) -> tracing::subscriber::DefaultGuard {
-    let subscriber = Registry::default().with(layer(spec));
+    let subscriber = Registry::default().with(SpanTree::new(spec));
     tracing::subscriber::set_default(subscriber)
 }
 
-pub fn layer<S>(spec: &str) -> impl Layer<S>
-where
-    S: Subscriber + for<'span> tracing_subscriber::registry::LookupSpan<'span>,
-{
-    let (write_filter, allowed_names) = WriteFilter::from_spec(spec);
-
-    // this filter the first pass for `tracing`: these are all the "profiling" spans, but things like
-    // span depth or duration are not filtered here: that only occurs at write time.
-    let profile_filter = filter::filter_fn(move |metadata| {
-        let allowed = match &allowed_names {
-            Some(names) => names.contains(metadata.name()),
-            None => true,
-        };
-
-        allowed
-            && metadata.is_span()
-            && metadata.level() >= &Level::INFO
-            && !metadata.target().starts_with("salsa")
-            && metadata.name() != "compute_exhaustiveness_and_usefulness"
-            && !metadata.target().starts_with("chalk")
-    });
-
-    hprof::SpanTree::default().aggregate(true).spec_filter(write_filter).with_filter(profile_filter)
-}
-
-#[derive(Default, Debug)]
-pub(crate) struct SpanTree {
+#[derive(Debug)]
+pub(crate) struct SpanTree<S> {
     aggregate: bool,
     write_filter: WriteFilter,
+    _inner: PhantomData<fn(S)>,
 }
 
-impl SpanTree {
-    /// Merge identical sibling spans together.
-    pub(crate) fn aggregate(self, yes: bool) -> SpanTree {
-        SpanTree { aggregate: yes, ..self }
-    }
-
-    /// Add a write-time filter for span duration or tree depth.
-    pub(crate) fn spec_filter(self, write_filter: WriteFilter) -> SpanTree {
-        SpanTree { write_filter, ..self }
+impl<S> SpanTree<S>
+where
+    S: Subscriber + for<'span> tracing_subscriber::registry::LookupSpan<'span>,
+{
+    pub(crate) fn new(spec: &str) -> impl Layer<S> {
+        let (write_filter, allowed_names) = WriteFilter::from_spec(spec);
+
+        // this filter the first pass for `tracing`: these are all the "profiling" spans, but things like
+        // span depth or duration are not filtered here: that only occurs at write time.
+        let profile_filter = filter::filter_fn(move |metadata| {
+            let allowed = match &allowed_names {
+                Some(names) => names.contains(metadata.name()),
+                None => true,
+            };
+
+            allowed
+                && metadata.is_span()
+                && metadata.level() >= &Level::INFO
+                && !metadata.target().starts_with("salsa")
+                && metadata.name() != "compute_exhaustiveness_and_usefulness"
+                && !metadata.target().starts_with("chalk")
+        });
+
+        Self { aggregate: true, write_filter, _inner: PhantomData }.with_filter(profile_filter)
     }
 }
 
@@ -136,7 +126,7 @@ impl<'a> Visit for DataVisitor<'a> {
     }
 }
 
-impl<S> Layer<S> for SpanTree
+impl<S> Layer<S> for SpanTree<S>
 where
     S: Subscriber + for<'span> LookupSpan<'span>,
 {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/tracing/json.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/tracing/json.rs
new file mode 100644
index 00000000000..f540a33b451
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/tracing/json.rs
@@ -0,0 +1,90 @@
+//! A [tracing_subscriber::layer::Layer] that exports new-line delinated JSON.
+//!
+//! Usage:
+//!
+//! ```rust
+//! let layer = json::TimingLayer::new(std::io::stderr);
+//! Registry::default().with(layer).init();
+//! ```
+
+use std::{io::Write as _, marker::PhantomData, time::Instant};
+
+use ide_db::FxHashSet;
+use tracing::{
+    span::{Attributes, Id},
+    Event, Subscriber,
+};
+use tracing_subscriber::{fmt::MakeWriter, layer::Context, registry::LookupSpan, Layer};
+
+struct JsonData {
+    name: &'static str,
+    start: std::time::Instant,
+}
+
+impl JsonData {
+    fn new(name: &'static str) -> Self {
+        Self { name, start: Instant::now() }
+    }
+}
+
+#[derive(Debug)]
+pub(crate) struct TimingLayer<S, W> {
+    writer: W,
+    _inner: PhantomData<fn(S)>,
+}
+
+impl<S, W> TimingLayer<S, W> {
+    pub(crate) fn new(writer: W) -> Self {
+        Self { writer, _inner: PhantomData }
+    }
+}
+
+impl<S, W> Layer<S> for TimingLayer<S, W>
+where
+    S: Subscriber + for<'span> LookupSpan<'span>,
+    W: for<'writer> MakeWriter<'writer> + Send + Sync + 'static,
+{
+    fn on_new_span(&self, attrs: &Attributes<'_>, id: &Id, ctx: Context<'_, S>) {
+        let span = ctx.span(id).unwrap();
+
+        let data = JsonData::new(attrs.metadata().name());
+        span.extensions_mut().insert(data);
+    }
+
+    fn on_event(&self, _event: &Event<'_>, _ctx: Context<'_, S>) {}
+
+    fn on_close(&self, id: Id, ctx: Context<'_, S>) {
+        #[derive(serde::Serialize)]
+        struct JsonDataInner {
+            name: &'static str,
+            elapsed_ms: u128,
+        }
+
+        let span = ctx.span(&id).unwrap();
+        let Some(data) = span.extensions_mut().remove::<JsonData>() else {
+            return;
+        };
+
+        let data = JsonDataInner { name: data.name, elapsed_ms: data.start.elapsed().as_millis() };
+        let mut out = serde_json::to_string(&data).expect("Unable to serialize data");
+        out.push('\n');
+        self.writer.make_writer().write_all(out.as_bytes()).expect("Unable to write data");
+    }
+}
+
+#[derive(Default, Clone, Debug)]
+pub(crate) struct JsonFilter {
+    pub(crate) allowed_names: Option<FxHashSet<String>>,
+}
+
+impl JsonFilter {
+    pub(crate) fn from_spec(spec: &str) -> Self {
+        let allowed_names = if spec == "*" {
+            None
+        } else {
+            Some(FxHashSet::from_iter(spec.split('|').map(String::from)))
+        };
+
+        Self { allowed_names }
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs
index 06ce9846818..18aface632d 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/support.rs
@@ -103,6 +103,7 @@ impl Project<'_> {
                 filter: std::env::var("RA_LOG").ok().unwrap_or_else(|| "error".to_owned()),
                 chalk_filter: std::env::var("CHALK_DEBUG").ok(),
                 profile_filter: std::env::var("RA_PROFILE").ok(),
+                json_profile_filter: std::env::var("RA_PROFILE_JSON").ok(),
             };
         });
 
diff --git a/src/tools/rust-analyzer/crates/span/src/hygiene.rs b/src/tools/rust-analyzer/crates/span/src/hygiene.rs
index cb9c092f5fc..3863b3e809c 100644
--- a/src/tools/rust-analyzer/crates/span/src/hygiene.rs
+++ b/src/tools/rust-analyzer/crates/span/src/hygiene.rs
@@ -142,3 +142,12 @@ pub enum Transparency {
     /// Def-site spans in procedural macros, identifiers from `macro` by default use this.
     Opaque,
 }
+
+impl Transparency {
+    /// Returns `true` if the transparency is [`Opaque`].
+    ///
+    /// [`Opaque`]: Transparency::Opaque
+    pub fn is_opaque(&self) -> bool {
+        matches!(self, Self::Opaque)
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/span/src/map.rs b/src/tools/rust-analyzer/crates/span/src/map.rs
index f80de05ec65..66bbce18594 100644
--- a/src/tools/rust-analyzer/crates/span/src/map.rs
+++ b/src/tools/rust-analyzer/crates/span/src/map.rs
@@ -55,7 +55,10 @@ where
     /// Returns all [`TextRange`]s that correspond to the given span.
     ///
     /// Note this does a linear search through the entire backing vector.
-    pub fn ranges_with_span_exact(&self, span: SpanData<S>) -> impl Iterator<Item = TextRange> + '_
+    pub fn ranges_with_span_exact(
+        &self,
+        span: SpanData<S>,
+    ) -> impl Iterator<Item = (TextRange, S)> + '_
     where
         S: Copy,
     {
@@ -64,14 +67,14 @@ where
                 return None;
             }
             let start = idx.checked_sub(1).map_or(TextSize::new(0), |prev| self.spans[prev].0);
-            Some(TextRange::new(start, end))
+            Some((TextRange::new(start, end), s.ctx))
         })
     }
 
     /// Returns all [`TextRange`]s whose spans contain the given span.
     ///
     /// Note this does a linear search through the entire backing vector.
-    pub fn ranges_with_span(&self, span: SpanData<S>) -> impl Iterator<Item = TextRange> + '_
+    pub fn ranges_with_span(&self, span: SpanData<S>) -> impl Iterator<Item = (TextRange, S)> + '_
     where
         S: Copy,
     {
@@ -83,7 +86,7 @@ where
                 return None;
             }
             let start = idx.checked_sub(1).map_or(TextSize::new(0), |prev| self.spans[prev].0);
-            Some(TextRange::new(start, end))
+            Some((TextRange::new(start, end), s.ctx))
         })
     }
 
diff --git a/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs b/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs
index 0ccd0886760..3a05b83e497 100644
--- a/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/syntax-bridge/src/lib.rs
@@ -148,6 +148,7 @@ pub fn token_tree_to_syntax_node<Ctx>(
 ) -> (Parse<SyntaxNode>, SpanMap<Ctx>)
 where
     SpanData<Ctx>: Copy + fmt::Debug,
+    Ctx: PartialEq,
 {
     let buffer = match tt {
         tt::Subtree {
@@ -892,6 +893,7 @@ fn delim_to_str(d: tt::DelimiterKind, closing: bool) -> Option<&'static str> {
 impl<Ctx> TtTreeSink<'_, Ctx>
 where
     SpanData<Ctx>: Copy + fmt::Debug,
+    Ctx: PartialEq,
 {
     /// Parses a float literal as if it was a one to two name ref nodes with a dot inbetween.
     /// This occurs when a float literal is used as a field access.
@@ -949,6 +951,7 @@ where
         }
 
         let mut last = self.cursor;
+        let mut combined_span = None;
         'tokens: for _ in 0..n_tokens {
             let tmp: u8;
             if self.cursor.eof() {
@@ -982,7 +985,10 @@ where
                             format_to!(self.buf, "{lit}");
                             debug_assert_ne!(self.buf.len() - buf_l, 0);
                             self.text_pos += TextSize::new((self.buf.len() - buf_l) as u32);
-                            self.token_map.push(self.text_pos, lit.span);
+                            combined_span = match combined_span {
+                                None => Some(lit.span),
+                                Some(prev_span) => Some(Self::merge_spans(prev_span, lit.span)),
+                            };
                             self.cursor = self.cursor.bump();
                             continue 'tokens;
                         }
@@ -1006,9 +1012,13 @@ where
             };
             self.buf += text;
             self.text_pos += TextSize::of(text);
-            self.token_map.push(self.text_pos, span);
+            combined_span = match combined_span {
+                None => Some(span),
+                Some(prev_span) => Some(Self::merge_spans(prev_span, span)),
+            }
         }
 
+        self.token_map.push(self.text_pos, combined_span.expect("expected at least one token"));
         self.inner.token(kind, self.buf.as_str());
         self.buf.clear();
         // FIXME: Emitting whitespace for this is really just a hack, we should get rid of it.
@@ -1043,4 +1053,22 @@ where
     fn error(&mut self, error: String) {
         self.inner.error(error, self.text_pos)
     }
+
+    fn merge_spans(a: SpanData<Ctx>, b: SpanData<Ctx>) -> SpanData<Ctx> {
+        // We don't do what rustc does exactly, rustc does something clever when the spans have different syntax contexts
+        // but this runs afoul of our separation between `span` and `hir-expand`.
+        SpanData {
+            range: if a.ctx == b.ctx {
+                TextRange::new(
+                    std::cmp::min(a.range.start(), b.range.start()),
+                    std::cmp::max(a.range.end(), b.range.end()),
+                )
+            } else {
+                // Combining ranges make no sense when they come from different syntax contexts.
+                a.range
+            },
+            anchor: a.anchor,
+            ctx: a.ctx,
+        }
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast.rs b/src/tools/rust-analyzer/crates/syntax/src/ast.rs
index 3282bd6eff2..32b1f5f7544 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast.rs
@@ -8,6 +8,7 @@ pub mod make;
 mod node_ext;
 mod operators;
 pub mod prec;
+pub mod syntax_factory;
 mod token_ext;
 mod traits;
 
@@ -166,7 +167,7 @@ mod support {
 }
 
 #[test]
-fn assert_ast_is_object_safe() {
+fn assert_ast_is_dyn_compatible() {
     fn _f(_: &dyn AstNode, _: &dyn HasName) {}
 }
 
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory.rs
new file mode 100644
index 00000000000..73bbe49105d
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory.rs
@@ -0,0 +1,45 @@
+//! Builds upon [`crate::ast::make`] constructors to create ast fragments with
+//! optional syntax mappings.
+//!
+//! Instead of forcing make constructors to perform syntax mapping, we instead
+//! let [`SyntaxFactory`] handle constructing the mappings. Care must be taken
+//! to remember to feed the syntax mappings into a [`SyntaxEditor`](crate::syntax_editor::SyntaxEditor),
+//! if applicable.
+
+mod constructors;
+
+use std::cell::{RefCell, RefMut};
+
+use crate::syntax_editor::SyntaxMapping;
+
+pub struct SyntaxFactory {
+    // Stored in a refcell so that the factory methods can be &self
+    mappings: Option<RefCell<SyntaxMapping>>,
+}
+
+impl SyntaxFactory {
+    /// Creates a new [`SyntaxFactory`], generating mappings between input nodes and generated nodes.
+    pub fn new() -> Self {
+        Self { mappings: Some(RefCell::new(SyntaxMapping::new())) }
+    }
+
+    /// Creates a [`SyntaxFactory`] without generating mappings.
+    pub fn without_mappings() -> Self {
+        Self { mappings: None }
+    }
+
+    /// Gets all of the tracked syntax mappings, if any.
+    pub fn finish_with_mappings(self) -> SyntaxMapping {
+        self.mappings.unwrap_or_default().into_inner()
+    }
+
+    fn mappings(&self) -> Option<RefMut<'_, SyntaxMapping>> {
+        self.mappings.as_ref().map(|it| it.borrow_mut())
+    }
+}
+
+impl Default for SyntaxFactory {
+    fn default() -> Self {
+        Self::without_mappings()
+    }
+}
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
new file mode 100644
index 00000000000..9f88add0f78
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/syntax_factory/constructors.rs
@@ -0,0 +1,110 @@
+//! Wrappers over [`make`] constructors
+use itertools::Itertools;
+
+use crate::{
+    ast::{self, make, HasName},
+    syntax_editor::SyntaxMappingBuilder,
+    AstNode,
+};
+
+use super::SyntaxFactory;
+
+impl SyntaxFactory {
+    pub fn name(&self, name: &str) -> ast::Name {
+        make::name(name).clone_for_update()
+    }
+
+    pub fn ident_pat(&self, ref_: bool, mut_: bool, name: ast::Name) -> ast::IdentPat {
+        let ast = make::ident_pat(ref_, mut_, name.clone()).clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_node(name.syntax().clone(), ast.name().unwrap().syntax().clone());
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
+    pub fn block_expr(
+        &self,
+        stmts: impl IntoIterator<Item = ast::Stmt>,
+        tail_expr: Option<ast::Expr>,
+    ) -> ast::BlockExpr {
+        let stmts = stmts.into_iter().collect_vec();
+        let input = stmts.iter().map(|it| it.syntax().clone()).collect_vec();
+
+        let ast = make::block_expr(stmts, tail_expr.clone()).clone_for_update();
+
+        if let Some((mut mapping, stmt_list)) = self.mappings().zip(ast.stmt_list()) {
+            let mut builder = SyntaxMappingBuilder::new(stmt_list.syntax().clone());
+
+            builder.map_children(
+                input.into_iter(),
+                stmt_list.statements().map(|it| it.syntax().clone()),
+            );
+
+            if let Some((input, output)) = tail_expr.zip(stmt_list.tail_expr()) {
+                builder.map_node(input.syntax().clone(), output.syntax().clone());
+            }
+
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+
+    pub fn expr_path(&self, path: ast::Path) -> ast::Expr {
+        let ast::Expr::PathExpr(ast) = make::expr_path(path.clone()).clone_for_update() else {
+            unreachable!()
+        };
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_node(path.syntax().clone(), ast.path().unwrap().syntax().clone());
+            builder.finish(&mut mapping);
+        }
+
+        ast.into()
+    }
+
+    pub fn expr_ref(&self, expr: ast::Expr, exclusive: bool) -> ast::Expr {
+        let ast::Expr::RefExpr(ast) = make::expr_ref(expr.clone(), exclusive).clone_for_update()
+        else {
+            unreachable!()
+        };
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_node(expr.syntax().clone(), ast.expr().unwrap().syntax().clone());
+            builder.finish(&mut mapping);
+        }
+
+        ast.into()
+    }
+
+    pub fn let_stmt(
+        &self,
+        pattern: ast::Pat,
+        ty: Option<ast::Type>,
+        initializer: Option<ast::Expr>,
+    ) -> ast::LetStmt {
+        let ast =
+            make::let_stmt(pattern.clone(), ty.clone(), initializer.clone()).clone_for_update();
+
+        if let Some(mut mapping) = self.mappings() {
+            let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
+            builder.map_node(pattern.syntax().clone(), ast.pat().unwrap().syntax().clone());
+            if let Some(input) = ty {
+                builder.map_node(input.syntax().clone(), ast.ty().unwrap().syntax().clone());
+            }
+            if let Some(input) = initializer {
+                builder
+                    .map_node(input.syntax().clone(), ast.initializer().unwrap().syntax().clone());
+            }
+            builder.finish(&mut mapping);
+        }
+
+        ast
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs
index eb114f5e5f1..714f5a99111 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor.rs
@@ -100,6 +100,10 @@ impl SyntaxEditor {
     pub fn finish(self) -> SyntaxEdit {
         edit_algo::apply_edits(self)
     }
+
+    pub fn add_mappings(&mut self, other: SyntaxMapping) {
+        self.mappings.merge(other);
+    }
 }
 
 /// Represents a completed [`SyntaxEditor`] operation.
@@ -319,85 +323,14 @@ fn is_ancestor_or_self_of_element(node: &SyntaxElement, ancestor: &SyntaxNode) -
 #[cfg(test)]
 mod tests {
     use expect_test::expect;
-    use itertools::Itertools;
 
     use crate::{
-        ast::{self, make, HasName},
+        ast::{self, make, syntax_factory::SyntaxFactory},
         AstNode,
     };
 
     use super::*;
 
-    fn make_ident_pat(
-        editor: Option<&mut SyntaxEditor>,
-        ref_: bool,
-        mut_: bool,
-        name: ast::Name,
-    ) -> ast::IdentPat {
-        let ast = make::ident_pat(ref_, mut_, name.clone()).clone_for_update();
-
-        if let Some(editor) = editor {
-            let mut mapping = SyntaxMappingBuilder::new(ast.syntax().clone());
-            mapping.map_node(name.syntax().clone(), ast.name().unwrap().syntax().clone());
-            mapping.finish(editor);
-        }
-
-        ast
-    }
-
-    fn make_let_stmt(
-        editor: Option<&mut SyntaxEditor>,
-        pattern: ast::Pat,
-        ty: Option<ast::Type>,
-        initializer: Option<ast::Expr>,
-    ) -> ast::LetStmt {
-        let ast =
-            make::let_stmt(pattern.clone(), ty.clone(), initializer.clone()).clone_for_update();
-
-        if let Some(editor) = editor {
-            let mut mapping = SyntaxMappingBuilder::new(ast.syntax().clone());
-            mapping.map_node(pattern.syntax().clone(), ast.pat().unwrap().syntax().clone());
-            if let Some(input) = ty {
-                mapping.map_node(input.syntax().clone(), ast.ty().unwrap().syntax().clone());
-            }
-            if let Some(input) = initializer {
-                mapping
-                    .map_node(input.syntax().clone(), ast.initializer().unwrap().syntax().clone());
-            }
-            mapping.finish(editor);
-        }
-
-        ast
-    }
-
-    fn make_block_expr(
-        editor: Option<&mut SyntaxEditor>,
-        stmts: impl IntoIterator<Item = ast::Stmt>,
-        tail_expr: Option<ast::Expr>,
-    ) -> ast::BlockExpr {
-        let stmts = stmts.into_iter().collect_vec();
-        let input = stmts.iter().map(|it| it.syntax().clone()).collect_vec();
-
-        let ast = make::block_expr(stmts, tail_expr.clone()).clone_for_update();
-
-        if let Some((editor, stmt_list)) = editor.zip(ast.stmt_list()) {
-            let mut mapping = SyntaxMappingBuilder::new(stmt_list.syntax().clone());
-
-            mapping.map_children(
-                input.into_iter(),
-                stmt_list.statements().map(|it| it.syntax().clone()),
-            );
-
-            if let Some((input, output)) = tail_expr.zip(stmt_list.tail_expr()) {
-                mapping.map_node(input.syntax().clone(), output.syntax().clone());
-            }
-
-            mapping.finish(editor);
-        }
-
-        ast
-    }
-
     #[test]
     fn basic_usage() {
         let root = make::match_arm(
@@ -417,6 +350,7 @@ mod tests {
         let to_replace = root.syntax().descendants().find_map(ast::BinExpr::cast).unwrap();
 
         let mut editor = SyntaxEditor::new(root.syntax().clone());
+        let make = SyntaxFactory::new();
 
         let name = make::name("var_name");
         let name_ref = make::name_ref("var_name").clone_for_update();
@@ -425,21 +359,20 @@ mod tests {
         editor.add_annotation(name.syntax(), placeholder_snippet);
         editor.add_annotation(name_ref.syntax(), placeholder_snippet);
 
-        let make_ident_pat = make_ident_pat(Some(&mut editor), false, false, name);
-        let make_let_stmt = make_let_stmt(
-            Some(&mut editor),
-            make_ident_pat.into(),
-            None,
-            Some(to_replace.clone().into()),
-        );
-        let new_block = make_block_expr(
-            Some(&mut editor),
-            [make_let_stmt.into()],
+        let new_block = make.block_expr(
+            [make
+                .let_stmt(
+                    make.ident_pat(false, false, name.clone()).into(),
+                    None,
+                    Some(to_replace.clone().into()),
+                )
+                .into()],
             Some(to_wrap.clone().into()),
         );
 
         editor.replace(to_replace.syntax(), name_ref.syntax());
         editor.replace(to_wrap.syntax(), new_block.syntax());
+        editor.add_mappings(make.finish_with_mappings());
 
         let edit = editor.finish();
 
@@ -473,11 +406,11 @@ mod tests {
         let second_let = root.syntax().descendants().find_map(ast::LetStmt::cast).unwrap();
 
         let mut editor = SyntaxEditor::new(root.syntax().clone());
+        let make = SyntaxFactory::without_mappings();
 
         editor.insert(
             Position::first_child_of(root.stmt_list().unwrap().syntax()),
-            make_let_stmt(
-                None,
+            make.let_stmt(
                 make::ext::simple_ident_pat(make::name("first")).into(),
                 None,
                 Some(make::expr_literal("1").into()),
@@ -487,8 +420,7 @@ mod tests {
 
         editor.insert(
             Position::after(second_let.syntax()),
-            make_let_stmt(
-                None,
+            make.let_stmt(
                 make::ext::simple_ident_pat(make::name("third")).into(),
                 None,
                 Some(make::expr_literal("3").into()),
@@ -528,19 +460,17 @@ mod tests {
         let second_let = root.syntax().descendants().find_map(ast::LetStmt::cast).unwrap();
 
         let mut editor = SyntaxEditor::new(root.syntax().clone());
+        let make = SyntaxFactory::new();
 
-        let new_block_expr =
-            make_block_expr(Some(&mut editor), [], Some(ast::Expr::BlockExpr(inner_block.clone())));
+        let new_block_expr = make.block_expr([], Some(ast::Expr::BlockExpr(inner_block.clone())));
 
-        let first_let = make_let_stmt(
-            Some(&mut editor),
+        let first_let = make.let_stmt(
             make::ext::simple_ident_pat(make::name("first")).into(),
             None,
             Some(make::expr_literal("1").into()),
         );
 
-        let third_let = make_let_stmt(
-            Some(&mut editor),
+        let third_let = make.let_stmt(
             make::ext::simple_ident_pat(make::name("third")).into(),
             None,
             Some(make::expr_literal("3").into()),
@@ -552,6 +482,7 @@ mod tests {
         );
         editor.insert(Position::after(second_let.syntax()), third_let.syntax());
         editor.replace(inner_block.syntax(), new_block_expr.syntax());
+        editor.add_mappings(make.finish_with_mappings());
 
         let edit = editor.finish();
 
@@ -581,12 +512,11 @@ mod tests {
         let inner_block = root.clone();
 
         let mut editor = SyntaxEditor::new(root.syntax().clone());
+        let make = SyntaxFactory::new();
 
-        let new_block_expr =
-            make_block_expr(Some(&mut editor), [], Some(ast::Expr::BlockExpr(inner_block.clone())));
+        let new_block_expr = make.block_expr([], Some(ast::Expr::BlockExpr(inner_block.clone())));
 
-        let first_let = make_let_stmt(
-            Some(&mut editor),
+        let first_let = make.let_stmt(
             make::ext::simple_ident_pat(make::name("first")).into(),
             None,
             Some(make::expr_literal("1").into()),
@@ -597,6 +527,7 @@ mod tests {
             first_let.syntax(),
         );
         editor.replace(inner_block.syntax(), new_block_expr.syntax());
+        editor.add_mappings(make.finish_with_mappings());
 
         let edit = editor.finish();
 
diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs
index 9bb5e6d9338..16bc55ed2d4 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs
@@ -7,8 +7,6 @@ use rustc_hash::FxHashMap;
 
 use crate::{SyntaxElement, SyntaxNode};
 
-use super::SyntaxEditor;
-
 #[derive(Debug, Default)]
 pub struct SyntaxMapping {
     // important information to keep track of:
@@ -209,7 +207,7 @@ impl SyntaxMapping {
         Some(output)
     }
 
-    fn add_mapping(&mut self, syntax_mapping: SyntaxMappingBuilder) {
+    pub fn add_mapping(&mut self, syntax_mapping: SyntaxMappingBuilder) {
         let SyntaxMappingBuilder { parent_node, node_mappings } = syntax_mapping;
 
         let parent_entry: u32 = self.entry_parents.len().try_into().unwrap();
@@ -257,8 +255,8 @@ impl SyntaxMappingBuilder {
         }
     }
 
-    pub fn finish(self, editor: &mut SyntaxEditor) {
-        editor.mappings.add_mapping(self);
+    pub fn finish(self, mappings: &mut SyntaxMapping) {
+        mappings.add_mapping(self);
     }
 }
 
diff --git a/src/tools/rust-analyzer/crates/vfs/src/loader.rs b/src/tools/rust-analyzer/crates/vfs/src/loader.rs
index f24354cb493..c49e4c4322d 100644
--- a/src/tools/rust-analyzer/crates/vfs/src/loader.rs
+++ b/src/tools/rust-analyzer/crates/vfs/src/loader.rs
@@ -1,4 +1,4 @@
-//! Object safe interface for file watching and reading.
+//! Dynamically compatible interface for file watching and reading.
 use std::fmt;
 
 use paths::{AbsPath, AbsPathBuf};
@@ -232,6 +232,6 @@ impl fmt::Debug for Message {
 }
 
 #[test]
-fn handle_is_object_safe() {
+fn handle_is_dyn_compatible() {
     fn _assert(_: &dyn Handle) {}
 }
diff --git a/src/tools/rust-analyzer/docs/dev/README.md b/src/tools/rust-analyzer/docs/dev/README.md
index 002b8ba2a66..12e6d829a08 100644
--- a/src/tools/rust-analyzer/docs/dev/README.md
+++ b/src/tools/rust-analyzer/docs/dev/README.md
@@ -178,7 +178,15 @@ RA_PROFILE=foo|bar|baz   // enabled only selected entries
 RA_PROFILE=*@3>10        // dump everything, up to depth 3, if it takes more than 10 ms
 ```
 
-In particular, I have `export RA_PROFILE='*>10'` in my shell profile.
+Some rust-analyzer contributors have `export RA_PROFILE='*>10'` in my shell profile.
+
+For machine-readable JSON output, we have the `RA_PROFILE_JSON` env variable. We support
+filtering only by span name:
+
+```
+RA_PROFILE=* // dump everything
+RA_PROFILE_JSON="vfs_load|parallel_prime_caches|discover_command" // dump selected spans
+```
 
 We also have a "counting" profiler which counts number of instances of popular structs.
 It is enabled by `RA_COUNT=1`.
diff --git a/src/tools/rust-analyzer/docs/dev/architecture.md b/src/tools/rust-analyzer/docs/dev/architecture.md
index 4f8723a9368..6aa57b2f9be 100644
--- a/src/tools/rust-analyzer/docs/dev/architecture.md
+++ b/src/tools/rust-analyzer/docs/dev/architecture.md
@@ -42,7 +42,7 @@ The underlying engine makes sure that model is computed lazily (on-demand) and c
 `crates/rust-analyzer/src/bin/main.rs` contains the main function which spawns LSP.
 This is *the* entry point, but it front-loads a lot of complexity, so it's fine to just skim through it.
 
-`crates/rust-analyzer/src/handlers.rs` implements all LSP requests and is a great place to start if you are already familiar with LSP.
+`crates/rust-analyzer/src/handlers/requests.rs` implements all LSP requests and is a great place to start if you are already familiar with LSP.
 
 `Analysis` and `AnalysisHost` types define the main API for consumers of IDE services.
 
diff --git a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md
index b7bac4d29fa..7764f7843a0 100644
--- a/src/tools/rust-analyzer/docs/dev/lsp-extensions.md
+++ b/src/tools/rust-analyzer/docs/dev/lsp-extensions.md
@@ -1,5 +1,5 @@
 <!---
-lsp/ext.rs hash: 6292ee8d88d4c9ec
+lsp/ext.rs hash: 90cf7718d54fe3c2
 
 If you need to change the above hash to make the test pass, please check if you
 need to adjust this doc as well and ping this issue:
diff --git a/src/tools/rust-analyzer/docs/dev/syntax.md b/src/tools/rust-analyzer/docs/dev/syntax.md
index 6c4daecc58f..3dcd430cea5 100644
--- a/src/tools/rust-analyzer/docs/dev/syntax.md
+++ b/src/tools/rust-analyzer/docs/dev/syntax.md
@@ -378,7 +378,7 @@ impl AstNode for AssocItem {
 }
 ```
 
-Shared AST substructures are modeled via (object safe) traits:
+Shared AST substructures are modeled via (dynamically compatible) traits:
 
 ```rust
 trait HasVisibility: AstNode {
diff --git a/src/tools/rust-analyzer/docs/user/generated_config.adoc b/src/tools/rust-analyzer/docs/user/generated_config.adoc
index f37fd7f4ab3..708fc2b7891 100644
--- a/src/tools/rust-analyzer/docs/user/generated_config.adoc
+++ b/src/tools/rust-analyzer/docs/user/generated_config.adoc
@@ -158,6 +158,11 @@ building from locking the `Cargo.lock` at the expense of duplicating build artif
 Set to `true` to use a subdirectory of the existing target directory or
 set to a path relative to the workspace to use that path.
 --
+[[rust-analyzer.cfg.setTest]]rust-analyzer.cfg.setTest (default: `true`)::
++
+--
+Set `cfg(test)` for local crates. Defaults to true.
+--
 [[rust-analyzer.checkOnSave]]rust-analyzer.checkOnSave (default: `true`)::
 +
 --
diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json
index b66f0a64d57..a823e5bb96c 100644
--- a/src/tools/rust-analyzer/editors/code/package.json
+++ b/src/tools/rust-analyzer/editors/code/package.json
@@ -512,6 +512,11 @@
                         "type": "boolean",
                         "default": false
                     },
+                    "rust-analyzer.debug.buildBeforeRestart": {
+                        "markdownDescription": "Whether to rebuild the project modules before debugging the same test again",
+                        "type": "boolean",
+                        "default": false
+                    },
                     "rust-analyzer.debug.engineSettings": {
                         "type": "object",
                         "default": {},
@@ -849,6 +854,16 @@
                 }
             },
             {
+                "title": "cfg",
+                "properties": {
+                    "rust-analyzer.cfg.setTest": {
+                        "markdownDescription": "Set `cfg(test)` for local crates. Defaults to true.",
+                        "default": true,
+                        "type": "boolean"
+                    }
+                }
+            },
+            {
                 "title": "general",
                 "properties": {
                     "rust-analyzer.checkOnSave": {
diff --git a/src/tools/rust-analyzer/editors/code/src/bootstrap.ts b/src/tools/rust-analyzer/editors/code/src/bootstrap.ts
index daead47e942..35867f710d5 100644
--- a/src/tools/rust-analyzer/editors/code/src/bootstrap.ts
+++ b/src/tools/rust-analyzer/editors/code/src/bootstrap.ts
@@ -23,10 +23,11 @@ export async function bootstrap(
 
     if (!isValidExecutable(path, config.serverExtraEnv)) {
         throw new Error(
-            `Failed to execute ${path} --version.` + config.serverPath
-                ? `\`config.server.path\` or \`config.serverPath\` has been set explicitly.\
+            `Failed to execute ${path} --version.` +
+                (config.serverPath
+                    ? `\`config.server.path\` or \`config.serverPath\` has been set explicitly.\
             Consider removing this config or making a valid server binary available at that path.`
-                : "",
+                    : ""),
         );
     }
 
diff --git a/src/tools/rust-analyzer/editors/code/src/config.ts b/src/tools/rust-analyzer/editors/code/src/config.ts
index 1e3dc608095..abb4099f9f5 100644
--- a/src/tools/rust-analyzer/editors/code/src/config.ts
+++ b/src/tools/rust-analyzer/editors/code/src/config.ts
@@ -24,6 +24,7 @@ export class Config {
         "serverPath",
         "server",
         "files",
+        "cfg",
     ].map((opt) => `${this.rootSection}.${opt}`);
 
     private readonly requiresWindowReloadOpts = ["testExplorer"].map(
@@ -299,6 +300,7 @@ export class Config {
             engine: this.get<string>("debug.engine"),
             engineSettings: this.get<object>("debug.engineSettings") ?? {},
             openDebugPane: this.get<boolean>("debug.openDebugPane"),
+            buildBeforeRestart: this.get<boolean>("debug.buildBeforeRestart"),
             sourceFileMap: sourceFileMap,
         };
     }
diff --git a/src/tools/rust-analyzer/editors/code/src/debug.ts b/src/tools/rust-analyzer/editors/code/src/debug.ts
index 3aae0f9ce6e..fb7e340e517 100644
--- a/src/tools/rust-analyzer/editors/code/src/debug.ts
+++ b/src/tools/rust-analyzer/editors/code/src/debug.ts
@@ -5,12 +5,15 @@ import type * as ra from "./lsp_ext";
 
 import { Cargo } from "./toolchain";
 import type { Ctx } from "./ctx";
-import { prepareEnv } from "./run";
+import { createTaskFromRunnable, prepareEnv } from "./run";
 import { execute, isCargoRunnableArgs, unwrapUndefinable } from "./util";
 import type { Config } from "./config";
 
 const debugOutput = vscode.window.createOutputChannel("Debug");
 
+// Here we want to keep track on everything that's currently running
+const activeDebugSessionIds: string[] = [];
+
 export async function makeDebugConfig(ctx: Ctx, runnable: ra.Runnable): Promise<void> {
     const scope = ctx.activeRustEditor?.document.uri;
     if (!scope) return;
@@ -45,6 +48,8 @@ export async function startDebugSession(ctx: Ctx, runnable: ra.Runnable): Promis
     const wsLaunchSection = vscode.workspace.getConfiguration("launch");
     const configurations = wsLaunchSection.get<any[]>("configurations") || [];
 
+    // The runnable label is the name of the test with the "test prefix"
+    // e.g. test test_feature_x
     const index = configurations.findIndex((c) => c.name === runnable.label);
     if (-1 !== index) {
         debugConfig = configurations[index];
@@ -168,6 +173,8 @@ async function getDebugConfiguration(
     if (debugConfig.name === "run binary") {
         // The LSP side: crates\rust-analyzer\src\main_loop\handlers.rs,
         // fn to_lsp_runnable(...) with RunnableKind::Bin
+        // FIXME: Neither crates\rust-analyzer\src\main_loop\handlers.rs
+        // nor to_lsp_runnable exist anymore
         debugConfig.name = `run ${path.basename(executable)}`;
     }
 
@@ -359,3 +366,49 @@ function quote(xs: string[]) {
         })
         .join(" ");
 }
+
+async function recompileTestFromDebuggingSession(session: vscode.DebugSession, ctx: Ctx) {
+    const { cwd, args: sessionArgs }: vscode.DebugConfiguration = session.configuration;
+
+    const args: ra.CargoRunnableArgs = {
+        cwd: cwd,
+        cargoArgs: ["test", "--no-run", "--test", "lib"],
+
+        // The first element of the debug configuration args is the test path e.g. "test_bar::foo::test_a::test_b"
+        executableArgs: sessionArgs,
+    };
+    const runnable: ra.Runnable = {
+        kind: "cargo",
+        label: "compile-test",
+        args,
+    };
+    const task: vscode.Task = await createTaskFromRunnable(runnable, ctx.config);
+
+    // It is not needed to call the language server, since the test path is already resolved in the
+    // configuration option. We can simply call a debug configuration with the --no-run option to compile
+    await vscode.tasks.executeTask(task);
+}
+
+export function initializeDebugSessionTrackingAndRebuild(ctx: Ctx) {
+    vscode.debug.onDidStartDebugSession((session: vscode.DebugSession) => {
+        if (!activeDebugSessionIds.includes(session.id)) {
+            activeDebugSessionIds.push(session.id);
+        }
+    });
+
+    vscode.debug.onDidTerminateDebugSession(async (session: vscode.DebugSession) => {
+        // The id of the session will be the same when pressing restart the restart button
+        if (activeDebugSessionIds.find((s) => s === session.id)) {
+            await recompileTestFromDebuggingSession(session, ctx);
+        }
+        removeActiveSession(session);
+    });
+}
+
+function removeActiveSession(session: vscode.DebugSession) {
+    const activeSessionId = activeDebugSessionIds.findIndex((id) => id === session.id);
+
+    if (activeSessionId !== -1) {
+        activeDebugSessionIds.splice(activeSessionId, 1);
+    }
+}
diff --git a/src/tools/rust-analyzer/editors/code/src/main.ts b/src/tools/rust-analyzer/editors/code/src/main.ts
index 4769fdd864a..0ddc5619e99 100644
--- a/src/tools/rust-analyzer/editors/code/src/main.ts
+++ b/src/tools/rust-analyzer/editors/code/src/main.ts
@@ -6,6 +6,7 @@ import { type CommandFactory, Ctx, fetchWorkspace } from "./ctx";
 import * as diagnostics from "./diagnostics";
 import { activateTaskProvider } from "./tasks";
 import { setContextValue } from "./util";
+import { initializeDebugSessionTrackingAndRebuild } from "./debug";
 
 const RUST_PROJECT_CONTEXT_NAME = "inRustProject";
 
@@ -102,6 +103,10 @@ async function activateServer(ctx: Ctx): Promise<RustAnalyzerExtensionApi> {
         ctx.subscriptions,
     );
 
+    if (ctx.config.debug.buildBeforeRestart) {
+        initializeDebugSessionTrackingAndRebuild(ctx);
+    }
+
     await ctx.start();
     return ctx;
 }
diff --git a/src/tools/rust-analyzer/editors/code/src/run.ts b/src/tools/rust-analyzer/editors/code/src/run.ts
index dd0da6b62c8..8a82a5a58cf 100644
--- a/src/tools/rust-analyzer/editors/code/src/run.ts
+++ b/src/tools/rust-analyzer/editors/code/src/run.ts
@@ -36,7 +36,7 @@ export async function selectRunnable(
 
     if (runnables.length === 0) {
         // it is the debug case, run always has at least 'cargo check ...'
-        // see crates\rust-analyzer\src\main_loop\handlers.rs, handle_runnables
+        // see crates\rust-analyzer\src\handlers\request.rs, handle_runnables
         await vscode.window.showErrorMessage("There's no debug target!");
         quickPick.dispose();
         return;
diff --git a/src/tools/rust-analyzer/editors/code/src/toolchain.ts b/src/tools/rust-analyzer/editors/code/src/toolchain.ts
index 850a6a55616..e8bab9c3d84 100644
--- a/src/tools/rust-analyzer/editors/code/src/toolchain.ts
+++ b/src/tools/rust-analyzer/editors/code/src/toolchain.ts
@@ -29,7 +29,7 @@ export class Cargo {
     static artifactSpec(cargoArgs: string[], executableArgs?: string[]): ArtifactSpec {
         cargoArgs = [...cargoArgs, "--message-format=json"];
         // arguments for a runnable from the quick pick should be updated.
-        // see crates\rust-analyzer\src\main_loop\handlers.rs, handle_code_lens
+        // see crates\rust-analyzer\src\handlers\request.rs, handle_code_lens
         switch (cargoArgs[0]) {
             case "run":
                 cargoArgs[0] = "build";
diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version
index 79ed6cc7d74..f217c6a19cb 100644
--- a/src/tools/rust-analyzer/rust-version
+++ b/src/tools/rust-analyzer/rust-version
@@ -1 +1 @@
-1b5aa96d6016bafe50e071b45d4d2e3c90fd766f
+cf24c73141a77db730f4b7fda69dcd7e8b113b51
diff --git a/src/tools/rustdoc-gui/tester.js b/src/tools/rustdoc-gui/tester.js
index 8f6626f6296..c5874b4ee02 100644
--- a/src/tools/rustdoc-gui/tester.js
+++ b/src/tools/rustdoc-gui/tester.js
@@ -19,7 +19,6 @@ function showHelp() {
     console.log("  --debug                    : show extra information about script run");
     console.log("  --show-text                : render font in pages");
     console.log("  --no-headless              : disable headless mode");
-    console.log("  --no-sandbox               : disable sandbox mode");
     console.log("  --help                     : show this message then quit");
     console.log("  --tests-folder [PATH]      : location of the .GOML tests folder");
     console.log("  --jobs [NUMBER]            : number of threads to run tests on");
@@ -40,7 +39,6 @@ function parseOptions(args) {
         "no_headless": false,
         "jobs": -1,
         "executable_path": null,
-        "no_sandbox": false,
     };
     const correspondences = {
         "--doc-folder": "doc_folder",
@@ -49,7 +47,6 @@ function parseOptions(args) {
         "--show-text": "show_text",
         "--no-headless": "no_headless",
         "--executable-path": "executable_path",
-        "--no-sandbox": "no_sandbox",
     };
 
     for (let i = 0; i < args.length; ++i) {
@@ -80,9 +77,6 @@ function parseOptions(args) {
         } else if (arg === "--help") {
             showHelp();
             process.exit(0);
-        } else if (arg === "--no-sandbox") {
-            console.log("`--no-sandbox` is being used. Be very careful!");
-            opts[correspondences[arg]] = true;
         } else if (correspondences[arg]) {
             opts[correspondences[arg]] = true;
         } else {
@@ -203,6 +197,7 @@ async function main(argv) {
         const args = [
             "--variable", "DOC_PATH", opts["doc_folder"].split("\\").join("/"),
             "--enable-fail-on-js-error", "--allow-file-access-from-files",
+            "--no-sandbox",
         ];
         if (opts["debug"]) {
             debug = true;
@@ -211,9 +206,6 @@ async function main(argv) {
         if (opts["show_text"]) {
             args.push("--show-text");
         }
-        if (opts["no_sandbox"]) {
-            args.push("--no-sandbox");
-        }
         if (opts["no_headless"]) {
             args.push("--no-headless");
             headless = false;
@@ -262,19 +254,6 @@ async function main(argv) {
         console.log(`Running ${files.length} rustdoc-gui ...`);
     }
 
-    // We catch this "event" to display a nicer message in case of unexpected exit (because of a
-    // missing `--no-sandbox`).
-    const exitHandling = () => {
-        if (!opts["no_sandbox"]) {
-            console.log("");
-            console.log(
-                "`browser-ui-test` crashed unexpectedly. Please try again with adding `--test-args \
---no-sandbox` at the end. For example: `x.py test tests/rustdoc-gui --test-args --no-sandbox`");
-            console.log("");
-        }
-    };
-    process.on("exit", exitHandling);
-
     const originalFilesLen = files.length;
     const results = createEmptyResults();
     const status_bar = char_printer(files.length);
@@ -299,9 +278,6 @@ async function main(argv) {
     Array.prototype.push.apply(results.failed, new_results.failed);
     Array.prototype.push.apply(results.errored, new_results.errored);
 
-    // We don't need this listener anymore.
-    process.removeListener("exit", exitHandling);
-
     if (debug) {
         results.successful.sort(by_filename);
         results.successful.forEach(r => {
diff --git a/src/tools/rustfmt/src/attr.rs b/src/tools/rustfmt/src/attr.rs
index 5c2068b6a22..381c938ae80 100644
--- a/src/tools/rustfmt/src/attr.rs
+++ b/src/tools/rustfmt/src/attr.rs
@@ -90,7 +90,7 @@ fn format_derive(
             let item_spans = attr.meta_item_list().map(|meta_item_list| {
                 meta_item_list
                     .into_iter()
-                    .map(|nested_meta_item| nested_meta_item.span())
+                    .map(|meta_item_inner| meta_item_inner.span())
             })?;
 
             let items = itemize_list(
@@ -243,17 +243,15 @@ fn rewrite_initial_doc_comments(
     Ok((0, None))
 }
 
-impl Rewrite for ast::NestedMetaItem {
+impl Rewrite for ast::MetaItemInner {
     fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
         self.rewrite_result(context, shape).ok()
     }
 
     fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
         match self {
-            ast::NestedMetaItem::MetaItem(ref meta_item) => {
-                meta_item.rewrite_result(context, shape)
-            }
-            ast::NestedMetaItem::Lit(ref l) => {
+            ast::MetaItemInner::MetaItem(ref meta_item) => meta_item.rewrite_result(context, shape),
+            ast::MetaItemInner::Lit(ref l) => {
                 rewrite_literal(context, l.as_token_lit(), l.span, shape)
             }
         }
@@ -535,10 +533,10 @@ pub(crate) trait MetaVisitor<'ast> {
     fn visit_meta_list(
         &mut self,
         _meta_item: &'ast ast::MetaItem,
-        list: &'ast [ast::NestedMetaItem],
+        list: &'ast [ast::MetaItemInner],
     ) {
         for nm in list {
-            self.visit_nested_meta_item(nm);
+            self.visit_meta_item_inner(nm);
         }
     }
 
@@ -551,10 +549,10 @@ pub(crate) trait MetaVisitor<'ast> {
     ) {
     }
 
-    fn visit_nested_meta_item(&mut self, nm: &'ast ast::NestedMetaItem) {
+    fn visit_meta_item_inner(&mut self, nm: &'ast ast::MetaItemInner) {
         match nm {
-            ast::NestedMetaItem::MetaItem(ref meta_item) => self.visit_meta_item(meta_item),
-            ast::NestedMetaItem::Lit(ref lit) => self.visit_meta_item_lit(lit),
+            ast::MetaItemInner::MetaItem(ref meta_item) => self.visit_meta_item(meta_item),
+            ast::MetaItemInner::Lit(ref lit) => self.visit_meta_item_lit(lit),
         }
     }
 
diff --git a/src/tools/rustfmt/src/overflow.rs b/src/tools/rustfmt/src/overflow.rs
index fdc8e23e72b..728adff2c7d 100644
--- a/src/tools/rustfmt/src/overflow.rs
+++ b/src/tools/rustfmt/src/overflow.rs
@@ -78,7 +78,7 @@ pub(crate) enum OverflowableItem<'a> {
     Expr(&'a ast::Expr),
     GenericParam(&'a ast::GenericParam),
     MacroArg(&'a MacroArg),
-    NestedMetaItem(&'a ast::NestedMetaItem),
+    MetaItemInner(&'a ast::MetaItemInner),
     SegmentParam(&'a SegmentParam<'a>),
     FieldDef(&'a ast::FieldDef),
     TuplePatField(&'a TuplePatField<'a>),
@@ -123,7 +123,7 @@ impl<'a> OverflowableItem<'a> {
             OverflowableItem::Expr(expr) => f(*expr),
             OverflowableItem::GenericParam(gp) => f(*gp),
             OverflowableItem::MacroArg(macro_arg) => f(*macro_arg),
-            OverflowableItem::NestedMetaItem(nmi) => f(*nmi),
+            OverflowableItem::MetaItemInner(nmi) => f(*nmi),
             OverflowableItem::SegmentParam(sp) => f(*sp),
             OverflowableItem::FieldDef(sf) => f(*sf),
             OverflowableItem::TuplePatField(pat) => f(*pat),
@@ -138,9 +138,9 @@ impl<'a> OverflowableItem<'a> {
             OverflowableItem::Expr(expr) => is_simple_expr(expr),
             OverflowableItem::MacroArg(MacroArg::Keyword(..)) => true,
             OverflowableItem::MacroArg(MacroArg::Expr(expr)) => is_simple_expr(expr),
-            OverflowableItem::NestedMetaItem(nested_meta_item) => match nested_meta_item {
-                ast::NestedMetaItem::Lit(..) => true,
-                ast::NestedMetaItem::MetaItem(ref meta_item) => {
+            OverflowableItem::MetaItemInner(meta_item_inner) => match meta_item_inner {
+                ast::MetaItemInner::Lit(..) => true,
+                ast::MetaItemInner::MetaItem(ref meta_item) => {
                     matches!(meta_item.kind, ast::MetaItemKind::Word)
                 }
             },
@@ -184,12 +184,10 @@ impl<'a> OverflowableItem<'a> {
                 MacroArg::Item(..) => len == 1,
                 MacroArg::Keyword(..) => false,
             },
-            OverflowableItem::NestedMetaItem(nested_meta_item) if len == 1 => {
-                match nested_meta_item {
-                    ast::NestedMetaItem::Lit(..) => false,
-                    ast::NestedMetaItem::MetaItem(..) => true,
-                }
-            }
+            OverflowableItem::MetaItemInner(meta_item_inner) if len == 1 => match meta_item_inner {
+                ast::MetaItemInner::Lit(..) => false,
+                ast::MetaItemInner::MetaItem(..) => true,
+            },
             OverflowableItem::SegmentParam(SegmentParam::Type(ty)) => {
                 can_be_overflowed_type(context, ty, len)
             }
@@ -202,7 +200,7 @@ impl<'a> OverflowableItem<'a> {
     fn special_cases(&self, config: &Config) -> impl Iterator<Item = &(&'static str, usize)> {
         let base_cases = match self {
             OverflowableItem::MacroArg(..) => SPECIAL_CASE_MACROS,
-            OverflowableItem::NestedMetaItem(..) => SPECIAL_CASE_ATTR,
+            OverflowableItem::MetaItemInner(..) => SPECIAL_CASE_ATTR,
             _ => &[],
         };
         let additional_cases = match self {
@@ -261,7 +259,7 @@ macro_rules! impl_into_overflowable_item_for_rustfmt_types {
 impl_into_overflowable_item_for_ast_node!(
     Expr,
     GenericParam,
-    NestedMetaItem,
+    MetaItemInner,
     FieldDef,
     Ty,
     Pat,
diff --git a/src/tools/rustfmt/src/parse/macros/asm.rs b/src/tools/rustfmt/src/parse/macros/asm.rs
index 5ee083da539..58c8d21bd7a 100644
--- a/src/tools/rustfmt/src/parse/macros/asm.rs
+++ b/src/tools/rustfmt/src/parse/macros/asm.rs
@@ -7,5 +7,5 @@ use crate::rewrite::RewriteContext;
 pub(crate) fn parse_asm(context: &RewriteContext<'_>, mac: &ast::MacCall) -> Option<AsmArgs> {
     let ts = mac.args.tokens.clone();
     let mut parser = super::build_parser(context, ts);
-    parse_asm_args(&mut parser, mac.span(), false).ok()
+    parse_asm_args(&mut parser, mac.span(), ast::AsmMacro::Asm).ok()
 }
diff --git a/src/tools/rustfmt/src/parse/session.rs b/src/tools/rustfmt/src/parse/session.rs
index 9f27a05939e..54ad56ed3f3 100644
--- a/src/tools/rustfmt/src/parse/session.rs
+++ b/src/tools/rustfmt/src/parse/session.rs
@@ -46,7 +46,7 @@ impl SilentOnIgnoredFilesEmitter {
 }
 
 impl Translate for SilentOnIgnoredFilesEmitter {
-    fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> {
+    fn fluent_bundle(&self) -> Option<&rustc_errors::FluentBundle> {
         self.emitter.fluent_bundle()
     }
 
@@ -56,7 +56,7 @@ impl Translate for SilentOnIgnoredFilesEmitter {
 }
 
 impl Emitter for SilentOnIgnoredFilesEmitter {
-    fn source_map(&self) -> Option<&Lrc<SourceMap>> {
+    fn source_map(&self) -> Option<&SourceMap> {
         None
     }
 
@@ -344,7 +344,7 @@ mod tests {
         }
 
         impl Translate for TestEmitter {
-            fn fluent_bundle(&self) -> Option<&Lrc<rustc_errors::FluentBundle>> {
+            fn fluent_bundle(&self) -> Option<&rustc_errors::FluentBundle> {
                 None
             }
 
@@ -354,7 +354,7 @@ mod tests {
         }
 
         impl Emitter for TestEmitter {
-            fn source_map(&self) -> Option<&Lrc<SourceMap>> {
+            fn source_map(&self) -> Option<&SourceMap> {
                 None
             }
 
diff --git a/src/tools/rustfmt/src/skip.rs b/src/tools/rustfmt/src/skip.rs
index d733f7068fd..cd3860d3d7f 100644
--- a/src/tools/rustfmt/src/skip.rs
+++ b/src/tools/rustfmt/src/skip.rs
@@ -116,8 +116,8 @@ fn get_skip_names(kind: &str, attrs: &[ast::Attribute]) -> Vec<String> {
         }
 
         if let Some(list) = attr.meta_item_list() {
-            for nested_meta_item in list {
-                if let Some(name) = nested_meta_item.ident() {
+            for meta_item_inner in list {
+                if let Some(name) = meta_item_inner.ident() {
                     skip_names.push(name.to_string());
                 }
             }
diff --git a/src/tools/rustfmt/src/spanned.rs b/src/tools/rustfmt/src/spanned.rs
index b4424e476ee..555a9240798 100644
--- a/src/tools/rustfmt/src/spanned.rs
+++ b/src/tools/rustfmt/src/spanned.rs
@@ -199,7 +199,7 @@ impl Spanned for MacroArg {
     }
 }
 
-impl Spanned for ast::NestedMetaItem {
+impl Spanned for ast::MetaItemInner {
     fn span(&self) -> Span {
         self.span()
     }
diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs
index 07b483b2b37..f75c4f0fad7 100644
--- a/src/tools/rustfmt/src/types.rs
+++ b/src/tools/rustfmt/src/types.rs
@@ -951,8 +951,6 @@ impl Rewrite for ast::Ty {
             ast::TyKind::Tup(ref items) => {
                 rewrite_tuple(context, items.iter(), self.span, shape, items.len() == 1)
             }
-            ast::TyKind::AnonStruct(..) => Ok(context.snippet(self.span).to_owned()),
-            ast::TyKind::AnonUnion(..) => Ok(context.snippet(self.span).to_owned()),
             ast::TyKind::Path(ref q_self, ref path) => {
                 rewrite_path(context, PathContext::Type, q_self, path, shape)
             }
diff --git a/src/tools/rustfmt/src/utils.rs b/src/tools/rustfmt/src/utils.rs
index d1cfc6acc49..0ca34a79491 100644
--- a/src/tools/rustfmt/src/utils.rs
+++ b/src/tools/rustfmt/src/utils.rs
@@ -1,7 +1,7 @@
 use std::borrow::Cow;
 
 use rustc_ast::ast::{
-    self, Attribute, MetaItem, MetaItemKind, NestedMetaItem, NodeId, Path, Visibility,
+    self, Attribute, MetaItem, MetaItemInner, MetaItemKind, NodeId, Path, Visibility,
     VisibilityKind,
 };
 use rustc_ast::ptr;
@@ -257,10 +257,10 @@ fn is_skip(meta_item: &MetaItem) -> bool {
 }
 
 #[inline]
-fn is_skip_nested(meta_item: &NestedMetaItem) -> bool {
+fn is_skip_nested(meta_item: &MetaItemInner) -> bool {
     match meta_item {
-        NestedMetaItem::MetaItem(ref mi) => is_skip(mi),
-        NestedMetaItem::Lit(_) => false,
+        MetaItemInner::MetaItem(ref mi) => is_skip(mi),
+        MetaItemInner::Lit(_) => false,
     }
 }
 
diff --git a/src/tools/rustfmt/tests/target/anonymous-types.rs b/src/tools/rustfmt/tests/target/anonymous-types.rs
deleted file mode 100644
index e8c2d83878c..00000000000
--- a/src/tools/rustfmt/tests/target/anonymous-types.rs
+++ /dev/null
@@ -1,31 +0,0 @@
-// Test for issue 85480
-// Pretty print anonymous struct and union types
-
-// pp-exact
-// pretty-compare-only
-
-struct Foo {
-    _: union  {
-        _: struct  {
-            a: u8,
-            b: u16,
-        },
-        c: u32,
-    },
-    d: u64,
-    e: f32,
-}
-
-// Test for https://github.com/rust-lang/rust/issues/117942
-struct Foo {
-    _: union  {
-        #[rustfmt::skip]
-    f: String,
-    },
-    #[rustfmt::skip]
-    _: struct {
-    g: i32,
-    },
-}
-
-fn main() {}
diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt
index 50d21c7ed4c..7a0f98d59f0 100644
--- a/src/tools/tidy/src/allowed_run_make_makefiles.txt
+++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt
@@ -1,6 +1,5 @@
 run-make/branch-protection-check-IBT/Makefile
 run-make/cat-and-grep-sanity-check/Makefile
-run-make/emit-to-stdout/Makefile
 run-make/extern-fn-reachable/Makefile
 run-make/incr-add-rust-src-component/Makefile
 run-make/issue-84395-lto-embed-bitcode/Makefile
diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs
index 1ffad06457f..5163f039a23 100644
--- a/src/tools/tidy/src/deps.rs
+++ b/src/tools/tidy/src/deps.rs
@@ -129,11 +129,14 @@ const EXCEPTIONS_STDARCH: ExceptionList = &[
 
 const EXCEPTIONS_CARGO: ExceptionList = &[
     // tidy-alphabetical-start
+    ("arrayref", "BSD-2-Clause"),
     ("bitmaps", "MPL-2.0+"),
+    ("blake3", "CC0-1.0 OR Apache-2.0 OR Apache-2.0 WITH LLVM-exception"),
     ("bytesize", "Apache-2.0"),
     ("ciborium", "Apache-2.0"),
     ("ciborium-io", "Apache-2.0"),
     ("ciborium-ll", "Apache-2.0"),
+    ("constant_time_eq", "CC0-1.0 OR MIT-0 OR Apache-2.0"),
     ("dunce", "CC0-1.0 OR MIT-0 OR Apache-2.0"),
     ("encoding_rs", "(Apache-2.0 OR MIT) AND BSD-3-Clause"),
     ("fiat-crypto", "MIT OR Apache-2.0 OR BSD-1-Clause"),
diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt
index 86cae849b97..66c176c6f44 100644
--- a/src/tools/tidy/src/issues.txt
+++ b/src/tools/tidy/src/issues.txt
@@ -802,7 +802,6 @@ ui/consts/issue-70942-trait-vs-impl-mismatch.rs
 ui/consts/issue-73976-monomorphic.rs
 ui/consts/issue-73976-polymorphic.rs
 ui/consts/issue-76064.rs
-ui/consts/issue-77062-large-zst-array.rs
 ui/consts/issue-78655.rs
 ui/consts/issue-79137-monomorphic.rs
 ui/consts/issue-79137-toogeneric.rs
@@ -3173,10 +3172,6 @@ ui/nll/user-annotations/issue-55241.rs
 ui/nll/user-annotations/issue-55748-pat-types-constrain-bindings.rs
 ui/nll/user-annotations/issue-57731-ascibed-coupled-types.rs
 ui/numbers-arithmetic/issue-8460.rs
-ui/object-safety/issue-102762.rs
-ui/object-safety/issue-102933.rs
-ui/object-safety/issue-106247.rs
-ui/object-safety/issue-19538.rs
 ui/on-unimplemented/issue-104140.rs
 ui/or-patterns/issue-64879-trailing-before-guard.rs
 ui/or-patterns/issue-67514-irrefutable-param.rs
diff --git a/src/tools/unicode-table-generator/src/raw_emitter.rs b/src/tools/unicode-table-generator/src/raw_emitter.rs
index bfffa1eee60..8097d6a8caf 100644
--- a/src/tools/unicode-table-generator/src/raw_emitter.rs
+++ b/src/tools/unicode-table-generator/src/raw_emitter.rs
@@ -77,7 +77,7 @@ impl RawEmitter {
 
         writeln!(
             &mut self.file,
-            "const BITSET_CANONICAL: &'static [u64; {}] = &[{}];",
+            "static BITSET_CANONICAL: [u64; {}] = [{}];",
             canonicalized.canonical_words.len(),
             fmt_list(canonicalized.canonical_words.iter().map(|v| Bits(*v))),
         )
@@ -85,7 +85,7 @@ impl RawEmitter {
         self.bytes_used += 8 * canonicalized.canonical_words.len();
         writeln!(
             &mut self.file,
-            "const BITSET_MAPPING: &'static [(u8, u8); {}] = &[{}];",
+            "static BITSET_MAPPING: [(u8, u8); {}] = [{}];",
             canonicalized.canonicalized_words.len(),
             fmt_list(&canonicalized.canonicalized_words),
         )
@@ -139,7 +139,7 @@ impl RawEmitter {
 
         writeln!(
             &mut self.file,
-            "const BITSET_CHUNKS_MAP: &'static [u8; {}] = &[{}];",
+            "static BITSET_CHUNKS_MAP: [u8; {}] = [{}];",
             chunk_indices.len(),
             fmt_list(&chunk_indices),
         )
@@ -147,7 +147,7 @@ impl RawEmitter {
         self.bytes_used += chunk_indices.len();
         writeln!(
             &mut self.file,
-            "const BITSET_INDEX_CHUNKS: &'static [[u8; {}]; {}] = &[{}];",
+            "static BITSET_INDEX_CHUNKS: [[u8; {}]; {}] = [{}];",
             chunk_length,
             chunks.len(),
             fmt_list(chunks.iter()),
diff --git a/src/tools/wasm-component-ld/Cargo.toml b/src/tools/wasm-component-ld/Cargo.toml
index 49f4e59650e..acdb1aa1ab7 100644
--- a/src/tools/wasm-component-ld/Cargo.toml
+++ b/src/tools/wasm-component-ld/Cargo.toml
@@ -10,4 +10,4 @@ name = "wasm-component-ld"
 path = "src/main.rs"
 
 [dependencies]
-wasm-component-ld = "0.5.9"
+wasm-component-ld = "0.5.10"
diff --git a/src/version b/src/version
index 6b4de0a42b0..bd0f9e6c28f 100644
--- a/src/version
+++ b/src/version
@@ -1 +1 @@
-1.83.0
+1.84.0