about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorRalf Jung <post@ralfj.de>2025-07-20 06:05:35 +0000
committerGitHub <noreply@github.com>2025-07-20 06:05:35 +0000
commitc9f96487ceb4fa156cea42b4f9aaa368924adbf1 (patch)
treef13dfba697a518dcfb1b15150736c73eb4b04b03 /src
parent627c031ce765934667e564fe3baf5c511ff1237a (diff)
parentc3175802c51fceae995640b1c1e50b648834f9af (diff)
downloadrust-c9f96487ceb4fa156cea42b4f9aaa368924adbf1.tar.gz
rust-c9f96487ceb4fa156cea42b4f9aaa368924adbf1.zip
Merge pull request #4485 from rust-lang/rustup-2025-07-20
Automatic Rustup
Diffstat (limited to 'src')
-rw-r--r--src/bootstrap/README.md2
-rw-r--r--src/bootstrap/bootstrap.py7
-rw-r--r--src/bootstrap/src/core/build_steps/check.rs7
-rw-r--r--src/bootstrap/src/core/build_steps/compile.rs52
-rw-r--r--src/bootstrap/src/core/build_steps/dist.rs23
-rw-r--r--src/bootstrap/src/core/build_steps/install.rs2
-rw-r--r--src/bootstrap/src/core/build_steps/test.rs3
-rw-r--r--src/bootstrap/src/core/build_steps/tool.rs330
-rw-r--r--src/bootstrap/src/core/builder/cargo.rs55
-rw-r--r--src/bootstrap/src/core/builder/mod.rs3
-rw-r--r--src/bootstrap/src/core/builder/tests.rs29
-rw-r--r--src/bootstrap/src/lib.rs68
-rw-r--r--src/ci/github-actions/jobs.yml4
-rw-r--r--src/doc/rustc-dev-guide/src/tests/directives.md6
-rw-r--r--src/doc/rustc-dev-guide/src/tests/ui.md20
-rw-r--r--src/doc/rustc/src/exploit-mitigations.md22
-rw-r--r--src/doc/rustc/src/platform-support/xtensa.md2
-rw-r--r--src/librustdoc/clean/types.rs4
-rw-r--r--src/librustdoc/clean/utils.rs6
-rw-r--r--src/librustdoc/doctest.rs4
-rw-r--r--src/librustdoc/doctest/rust.rs2
-rw-r--r--src/librustdoc/formats/renderer.rs4
-rw-r--r--src/librustdoc/html/format.rs12
-rw-r--r--src/librustdoc/html/highlight.rs4
-rw-r--r--src/librustdoc/html/layout.rs1
-rw-r--r--src/librustdoc/html/markdown.rs4
-rw-r--r--src/librustdoc/html/render/context.rs84
-rw-r--r--src/librustdoc/html/render/mod.rs39
-rw-r--r--src/librustdoc/html/render/ordered_json.rs2
-rw-r--r--src/librustdoc/html/render/print_item.rs80
-rw-r--r--src/librustdoc/html/render/sidebar.rs2
-rw-r--r--src/librustdoc/html/render/sorted_template.rs3
-rw-r--r--src/librustdoc/json/conversions.rs20
-rw-r--r--src/librustdoc/json/mod.rs6
-rw-r--r--src/librustdoc/passes/collect_intra_doc_links.rs25
-rw-r--r--src/librustdoc/passes/lint/redundant_explicit_links.rs16
-rw-r--r--src/librustdoc/passes/strip_aliased_non_local.rs10
-rw-r--r--src/tools/compiletest/src/common.rs28
-rw-r--r--src/tools/compiletest/src/directives.rs12
-rw-r--r--src/tools/compiletest/src/runtest.rs7
-rw-r--r--src/tools/compiletest/src/runtest/ui.rs53
-rw-r--r--src/tools/miri/rust-version2
-rw-r--r--src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.rs4
-rw-r--r--src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.stderr5
-rw-r--r--src/tools/miri/tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.rs4
-rw-r--r--src/tools/miri/tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.stderr5
-rw-r--r--src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.rs4
-rw-r--r--src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.stderr5
-rw-r--r--src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.rs4
-rw-r--r--src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.stderr5
-rw-r--r--src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.rs4
-rw-r--r--src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.stderr5
-rw-r--r--src/tools/miri/tests/fail/function_calls/arg_inplace_observe_after.stderr7
-rw-r--r--src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.none.stderr7
-rw-r--r--src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_read.none.stderr7
-rw-r--r--src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr15
-rw-r--r--src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_data.rs5
-rw-r--r--src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_data.stderr5
-rw-r--r--src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_len.rs5
-rw-r--r--src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_len.stderr5
-rw-r--r--src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_thin.rs5
-rw-r--r--src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_thin.stderr5
-rw-r--r--src/tools/miri/tests/fail/read_from_trivial_switch.rs5
-rw-r--r--src/tools/miri/tests/fail/read_from_trivial_switch.stderr5
-rw-r--r--src/tools/miri/tests/fail/uninit/padding-enum.rs3
-rw-r--r--src/tools/miri/tests/fail/uninit/padding-enum.stderr5
-rw-r--r--src/tools/miri/tests/fail/uninit/padding-pair.rs3
-rw-r--r--src/tools/miri/tests/fail/uninit/padding-pair.stderr5
-rw-r--r--src/tools/miri/tests/fail/uninit/padding-struct.stderr7
-rw-r--r--src/tools/miri/tests/fail/uninit/padding-wide-ptr.rs3
-rw-r--r--src/tools/miri/tests/fail/uninit/padding-wide-ptr.stderr5
-rw-r--r--src/tools/miri/tests/fail/uninit/transmute-pair-uninit.rs3
-rw-r--r--src/tools/miri/tests/fail/uninit/transmute-pair-uninit.stderr5
-rw-r--r--src/tools/miri/tests/fail/uninit/uninit_byte_read.stderr7
-rw-r--r--src/tools/miri/tests/fail/validity/invalid_int_op.stderr7
-rw-r--r--src/tools/miri/tests/native-lib/fail/tracing/partial_init.rs2
-rw-r--r--src/tools/miri/tests/native-lib/fail/tracing/partial_init.stderr7
-rw-r--r--src/tools/miri/tests/pass-dep/libc/libc-time.rs6
-rw-r--r--src/tools/opt-dist/src/environment.rs5
-rw-r--r--src/tools/opt-dist/src/exec.rs12
-rw-r--r--src/tools/opt-dist/src/main.rs122
-rw-r--r--src/tools/opt-dist/src/training.rs4
-rw-r--r--src/tools/tidy/src/features.rs35
-rw-r--r--src/tools/tidy/src/fluent_period.rs29
-rw-r--r--src/tools/tidy/src/fluent_used.rs2
-rw-r--r--src/tools/tidy/src/lib.rs7
-rw-r--r--src/tools/tidy/src/mir_opt_tests.rs31
-rw-r--r--src/tools/tidy/src/rustdoc_templates.rs2
-rw-r--r--src/tools/tidy/src/style.rs46
-rw-r--r--src/tools/tidy/src/target_specific_tests.rs22
-rw-r--r--src/tools/tidy/src/ui_tests.rs43
-rw-r--r--src/tools/tidy/src/x_version.rs12
92 files changed, 1046 insertions, 580 deletions
diff --git a/src/bootstrap/README.md b/src/bootstrap/README.md
index 6ce4c6d62fa..2965174b45b 100644
--- a/src/bootstrap/README.md
+++ b/src/bootstrap/README.md
@@ -105,7 +105,7 @@ build/
       debuginfo/
       ...
 
-    # Bootstrap host tools (which are always compiled with the stage0 compiler)
+    # Host tools (which are always compiled with the stage0 compiler)
     # are stored here.
     bootstrap-tools/
 
diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py
index d8c6be78247..40e08361a0f 100644
--- a/src/bootstrap/bootstrap.py
+++ b/src/bootstrap/bootstrap.py
@@ -8,6 +8,7 @@ import re
 import shutil
 import subprocess
 import sys
+import sysconfig
 import tarfile
 import tempfile
 
@@ -333,7 +334,11 @@ def default_build_triple(verbose):
         if ostype == "Android":
             kernel = "linux-android"
         else:
-            kernel = "unknown-linux-gnu"
+            python_soabi = sysconfig.get_config_var("SOABI")
+            if python_soabi is not None and "musl" in python_soabi:
+                kernel = "unknown-linux-musl"
+            else:
+                kernel = "unknown-linux-gnu"
     elif kernel == "SunOS":
         kernel = "pc-solaris"
         # On Solaris, uname -m will return a machine classification instead
diff --git a/src/bootstrap/src/core/build_steps/check.rs b/src/bootstrap/src/core/build_steps/check.rs
index 3278b55305c..1d30886a86a 100644
--- a/src/bootstrap/src/core/build_steps/check.rs
+++ b/src/bootstrap/src/core/build_steps/check.rs
@@ -4,7 +4,10 @@ use crate::core::build_steps::compile::{
     add_to_sysroot, run_cargo, rustc_cargo, rustc_cargo_env, std_cargo, std_crates_for_run_make,
 };
 use crate::core::build_steps::tool;
-use crate::core::build_steps::tool::{COMPILETEST_ALLOW_FEATURES, SourceType, prepare_tool_cargo};
+use crate::core::build_steps::tool::{
+    COMPILETEST_ALLOW_FEATURES, SourceType, ToolTargetBuildMode, get_tool_target_compiler,
+    prepare_tool_cargo,
+};
 use crate::core::builder::{
     self, Alias, Builder, Kind, RunConfig, ShouldRun, Step, StepMetadata, crate_description,
 };
@@ -252,8 +255,10 @@ fn prepare_compiler_for_check(
     mode: Mode,
 ) -> Compiler {
     let host = builder.host_target;
+
     match mode {
         Mode::ToolBootstrap => builder.compiler(0, host),
+        Mode::ToolTarget => get_tool_target_compiler(builder, ToolTargetBuildMode::Build(target)),
         Mode::ToolStd => {
             if builder.config.compile_time_deps {
                 // When --compile-time-deps is passed, we can't use any rustc
diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs
index 09bb2e35bda..85847cac446 100644
--- a/src/bootstrap/src/core/build_steps/compile.rs
+++ b/src/bootstrap/src/core/build_steps/compile.rs
@@ -19,7 +19,7 @@ use serde_derive::Deserialize;
 use tracing::{instrument, span};
 
 use crate::core::build_steps::gcc::{Gcc, add_cg_gcc_cargo_flags};
-use crate::core::build_steps::tool::SourceType;
+use crate::core::build_steps::tool::{SourceType, copy_lld_artifacts};
 use crate::core::build_steps::{dist, llvm};
 use crate::core::builder;
 use crate::core::builder::{
@@ -2050,19 +2050,20 @@ impl Step for Assemble {
             }
         }
 
-        let maybe_install_llvm_bitcode_linker = |compiler| {
+        let maybe_install_llvm_bitcode_linker = || {
             if builder.config.llvm_bitcode_linker_enabled {
                 trace!("llvm-bitcode-linker enabled, installing");
-                let llvm_bitcode_linker =
-                    builder.ensure(crate::core::build_steps::tool::LlvmBitcodeLinker {
-                        build_compiler: compiler,
-                        target: target_compiler.host,
-                    });
+                let llvm_bitcode_linker = builder.ensure(
+                    crate::core::build_steps::tool::LlvmBitcodeLinker::from_target_compiler(
+                        builder,
+                        target_compiler,
+                    ),
+                );
 
                 // Copy the llvm-bitcode-linker to the self-contained binary directory
                 let bindir_self_contained = builder
-                    .sysroot(compiler)
-                    .join(format!("lib/rustlib/{}/bin/self-contained", compiler.host));
+                    .sysroot(target_compiler)
+                    .join(format!("lib/rustlib/{}/bin/self-contained", target_compiler.host));
                 let tool_exe = exe("llvm-bitcode-linker", target_compiler.host);
 
                 t!(fs::create_dir_all(&bindir_self_contained));
@@ -2089,9 +2090,9 @@ impl Step for Assemble {
                 builder.info(&format!("Creating a sysroot for stage{stage} compiler (use `rustup toolchain link 'name' build/host/stage{stage}`)", stage = target_compiler.stage));
             }
 
-            let mut precompiled_compiler = target_compiler;
-            precompiled_compiler.forced_compiler(true);
-            maybe_install_llvm_bitcode_linker(precompiled_compiler);
+            // FIXME: this is incomplete, we do not copy a bunch of other stuff to the downloaded
+            // sysroot...
+            maybe_install_llvm_bitcode_linker();
 
             return target_compiler;
         }
@@ -2256,10 +2257,12 @@ impl Step for Assemble {
         copy_codegen_backends_to_sysroot(builder, build_compiler, target_compiler);
 
         if builder.config.lld_enabled {
-            builder.ensure(crate::core::build_steps::tool::LldWrapper {
-                build_compiler,
-                target_compiler,
-            });
+            let lld_wrapper =
+                builder.ensure(crate::core::build_steps::tool::LldWrapper::for_use_by_compiler(
+                    builder,
+                    target_compiler,
+                ));
+            copy_lld_artifacts(builder, lld_wrapper, target_compiler);
         }
 
         if builder.config.llvm_enabled(target_compiler.host) && builder.config.llvm_tools_enabled {
@@ -2284,15 +2287,14 @@ impl Step for Assemble {
         }
 
         // In addition to `rust-lld` also install `wasm-component-ld` when
-        // LLD is enabled. This is a relatively small binary that primarily
-        // delegates to the `rust-lld` binary for linking and then runs
-        // logic to create the final binary. This is used by the
-        // `wasm32-wasip2` target of Rust.
+        // is enabled. This is used by the `wasm32-wasip2` target of Rust.
         if builder.tool_enabled("wasm-component-ld") {
-            let wasm_component = builder.ensure(crate::core::build_steps::tool::WasmComponentLd {
-                compiler: build_compiler,
-                target: target_compiler.host,
-            });
+            let wasm_component = builder.ensure(
+                crate::core::build_steps::tool::WasmComponentLd::for_use_by_compiler(
+                    builder,
+                    target_compiler,
+                ),
+            );
             builder.copy_link(
                 &wasm_component.tool_path,
                 &libdir_bin.join(wasm_component.tool_path.file_name().unwrap()),
@@ -2300,7 +2302,7 @@ impl Step for Assemble {
             );
         }
 
-        maybe_install_llvm_bitcode_linker(target_compiler);
+        maybe_install_llvm_bitcode_linker();
 
         // Ensure that `libLLVM.so` ends up in the newly build compiler directory,
         // so that it can be found when the newly built `rustc` is run.
diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs
index 8b2d65ace50..39e4fb2ac01 100644
--- a/src/bootstrap/src/core/build_steps/dist.rs
+++ b/src/bootstrap/src/core/build_steps/dist.rs
@@ -1575,7 +1575,10 @@ impl Step for Extended {
             compiler: builder.compiler(stage, target),
             backend: "cranelift".to_string(),
         });
-        add_component!("llvm-bitcode-linker" => LlvmBitcodeLinker {compiler, target});
+        add_component!("llvm-bitcode-linker" => LlvmBitcodeLinker {
+            build_compiler: compiler,
+            target
+        });
 
         let etc = builder.src.join("src/etc/installer");
 
@@ -2341,9 +2344,13 @@ impl Step for LlvmTools {
     }
 }
 
+/// Distributes the `llvm-bitcode-linker` tool so that it can be used by a compiler whose host
+/// is `target`.
 #[derive(Debug, PartialOrd, Ord, Clone, Hash, PartialEq, Eq)]
 pub struct LlvmBitcodeLinker {
-    pub compiler: Compiler,
+    /// The linker will be compiled by this compiler.
+    pub build_compiler: Compiler,
+    /// The linker will by usable by rustc on this host.
     pub target: TargetSelection,
 }
 
@@ -2359,9 +2366,8 @@ impl Step for LlvmBitcodeLinker {
 
     fn make_run(run: RunConfig<'_>) {
         run.builder.ensure(LlvmBitcodeLinker {
-            compiler: run.builder.compiler_for(
-                run.builder.top_stage,
-                run.builder.config.host_target,
+            build_compiler: tool::LlvmBitcodeLinker::get_build_compiler_for_target(
+                run.builder,
                 run.target,
             ),
             target: run.target,
@@ -2369,13 +2375,10 @@ impl Step for LlvmBitcodeLinker {
     }
 
     fn run(self, builder: &Builder<'_>) -> Option<GeneratedTarball> {
-        let compiler = self.compiler;
         let target = self.target;
 
-        builder.ensure(compile::Rustc::new(compiler, target));
-
-        let llbc_linker =
-            builder.ensure(tool::LlvmBitcodeLinker { build_compiler: compiler, target });
+        let llbc_linker = builder
+            .ensure(tool::LlvmBitcodeLinker::from_build_compiler(self.build_compiler, target));
 
         let self_contained_bin_dir = format!("lib/rustlib/{}/bin/self-contained", target.triple);
 
diff --git a/src/bootstrap/src/core/build_steps/install.rs b/src/bootstrap/src/core/build_steps/install.rs
index 4434d6658eb..4156b49a8b3 100644
--- a/src/bootstrap/src/core/build_steps/install.rs
+++ b/src/bootstrap/src/core/build_steps/install.rs
@@ -287,7 +287,7 @@ install!((self, builder, _config),
         }
     };
     LlvmBitcodeLinker, alias = "llvm-bitcode-linker", Self::should_build(_config), only_hosts: true, {
-        if let Some(tarball) = builder.ensure(dist::LlvmBitcodeLinker { compiler: self.compiler, target: self.target }) {
+        if let Some(tarball) = builder.ensure(dist::LlvmBitcodeLinker { build_compiler: self.compiler, target: self.target }) {
             install_sh(builder, "llvm-bitcode-linker", self.compiler.stage, Some(self.target), &tarball);
         } else {
             builder.info(
diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs
index 9e7ea5c115f..98f11116279 100644
--- a/src/bootstrap/src/core/build_steps/test.rs
+++ b/src/bootstrap/src/core/build_steps/test.rs
@@ -2945,7 +2945,8 @@ impl Step for RemoteCopyLibs {
 
         builder.info(&format!("REMOTE copy libs to emulator ({target})"));
 
-        let remote_test_server = builder.ensure(tool::RemoteTestServer { compiler, target });
+        let remote_test_server =
+            builder.ensure(tool::RemoteTestServer { build_compiler: compiler, target });
 
         // Spawn the emulator and wait for it to come online
         let tool = builder.tool_exe(Tool::RemoteTestClient);
diff --git a/src/bootstrap/src/core/build_steps/tool.rs b/src/bootstrap/src/core/build_steps/tool.rs
index 1c994b0ccfc..f5fa33b98f3 100644
--- a/src/bootstrap/src/core/build_steps/tool.rs
+++ b/src/bootstrap/src/core/build_steps/tool.rs
@@ -42,7 +42,8 @@ pub enum ToolArtifactKind {
 
 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
 struct ToolBuild {
-    compiler: Compiler,
+    /// Compiler that will build this tool.
+    build_compiler: Compiler,
     target: TargetSelection,
     tool: &'static str,
     path: &'static str,
@@ -112,34 +113,34 @@ impl Step for ToolBuild {
         let mut tool = self.tool;
         let path = self.path;
 
-        let target_compiler = self.compiler;
-        self.compiler = if self.mode == Mode::ToolRustc {
-            get_tool_rustc_compiler(builder, self.compiler)
+        let target_compiler = self.build_compiler;
+        self.build_compiler = if self.mode == Mode::ToolRustc {
+            get_tool_rustc_compiler(builder, self.build_compiler)
         } else {
-            self.compiler
+            self.build_compiler
         };
 
         match self.mode {
             Mode::ToolRustc => {
                 // If compiler was forced, its artifacts should have been prepared earlier.
-                if !self.compiler.is_forced_compiler() {
-                    builder.std(self.compiler, self.compiler.host);
-                    builder.ensure(compile::Rustc::new(self.compiler, target));
+                if !self.build_compiler.is_forced_compiler() {
+                    builder.std(self.build_compiler, self.build_compiler.host);
+                    builder.ensure(compile::Rustc::new(self.build_compiler, target));
                 }
             }
             Mode::ToolStd => {
                 // If compiler was forced, its artifacts should have been prepared earlier.
-                if !self.compiler.is_forced_compiler() {
-                    builder.std(self.compiler, target)
+                if !self.build_compiler.is_forced_compiler() {
+                    builder.std(self.build_compiler, target);
                 }
             }
-            Mode::ToolBootstrap => {} // uses downloaded stage0 compiler libs
+            Mode::ToolBootstrap | Mode::ToolTarget => {} // uses downloaded stage0 compiler libs
             _ => panic!("unexpected Mode for tool build"),
         }
 
         let mut cargo = prepare_tool_cargo(
             builder,
-            self.compiler,
+            self.build_compiler,
             self.mode,
             target,
             Kind::Build,
@@ -161,7 +162,7 @@ impl Step for ToolBuild {
 
         // Rustc tools (miri, clippy, cargo, rustfmt, rust-analyzer)
         // could use the additional optimizations.
-        if self.mode == Mode::ToolRustc && is_lto_stage(&self.compiler) {
+        if self.mode == Mode::ToolRustc && is_lto_stage(&self.build_compiler) {
             let lto = match builder.config.rust_lto {
                 RustcLto::Off => Some("off"),
                 RustcLto::Thin => Some("thin"),
@@ -183,8 +184,9 @@ impl Step for ToolBuild {
             Kind::Build,
             self.mode,
             self.tool,
-            self.compiler.stage,
-            &self.compiler.host,
+            // A stage N tool is built with the stage N-1 compiler.
+            self.build_compiler.stage + 1,
+            &self.build_compiler.host,
             &self.target,
         );
 
@@ -207,14 +209,14 @@ impl Step for ToolBuild {
             }
             let tool_path = match self.artifact_kind {
                 ToolArtifactKind::Binary => {
-                    copy_link_tool_bin(builder, self.compiler, self.target, self.mode, tool)
+                    copy_link_tool_bin(builder, self.build_compiler, self.target, self.mode, tool)
                 }
                 ToolArtifactKind::Library => builder
-                    .cargo_out(self.compiler, self.mode, self.target)
+                    .cargo_out(self.build_compiler, self.mode, self.target)
                     .join(format!("lib{tool}.rlib")),
             };
 
-            ToolBuildResult { tool_path, build_compiler: self.compiler, target_compiler }
+            ToolBuildResult { tool_path, build_compiler: self.build_compiler, target_compiler }
         }
     }
 }
@@ -365,6 +367,47 @@ pub(crate) fn get_tool_rustc_compiler(
     builder.compiler(target_compiler.stage.saturating_sub(1), builder.config.host_target)
 }
 
+/// Determines how to build a `ToolTarget`, i.e. which compiler should be used to compile it.
+/// The compiler stage is automatically bumped if we need to cross-compile a stage 1 tool.
+pub enum ToolTargetBuildMode {
+    /// Build the tool using rustc that corresponds to the selected CLI stage.
+    Build(TargetSelection),
+    /// Build the tool so that it can be attached to the sysroot of the passed compiler.
+    /// Since we always dist stage 2+, the compiler that builds the tool in this case has to be
+    /// stage 1+.
+    Dist(Compiler),
+}
+
+/// Returns compiler that is able to compile a `ToolTarget` tool with the given `mode`.
+pub(crate) fn get_tool_target_compiler(
+    builder: &Builder<'_>,
+    mode: ToolTargetBuildMode,
+) -> Compiler {
+    let (target, build_compiler_stage) = match mode {
+        ToolTargetBuildMode::Build(target) => {
+            assert!(builder.top_stage > 0);
+            // If we want to build a stage N tool, we need to compile it with stage N-1 rustc
+            (target, builder.top_stage - 1)
+        }
+        ToolTargetBuildMode::Dist(target_compiler) => {
+            assert!(target_compiler.stage > 0);
+            // If we want to dist a stage N rustc, we want to attach stage N tool to it.
+            // And to build that tool, we need to compile it with stage N-1 rustc
+            (target_compiler.host, target_compiler.stage - 1)
+        }
+    };
+
+    let compiler = if builder.host_target == target {
+        builder.compiler(build_compiler_stage, builder.host_target)
+    } else {
+        // If we are cross-compiling a stage 1 tool, we cannot do that with a stage 0 compiler,
+        // so we auto-bump the tool's stage to 2, which means we need a stage 1 compiler.
+        builder.compiler(build_compiler_stage.max(1), builder.host_target)
+    };
+    builder.std(compiler, target);
+    compiler
+}
+
 /// Links a built tool binary with the given `name` from the build directory to the
 /// tools directory.
 fn copy_link_tool_bin(
@@ -451,7 +494,7 @@ macro_rules! bootstrap_tool {
                 let compiletest_wants_stage0 = $tool_name == "compiletest" && builder.config.compiletest_use_stage0_libtest;
 
                 builder.ensure(ToolBuild {
-                    compiler: self.compiler,
+                    build_compiler: self.compiler,
                     target: self.target,
                     tool: $tool_name,
                     mode: if is_unstable && !compiletest_wants_stage0 {
@@ -521,7 +564,6 @@ bootstrap_tool!(
     // rustdoc-gui-test has a crate dependency on compiletest, so it needs the same unstable features.
     RustdocGUITest, "src/tools/rustdoc-gui-test", "rustdoc-gui-test", is_unstable_tool = true, allow_features = COMPILETEST_ALLOW_FEATURES;
     CoverageDump, "src/tools/coverage-dump", "coverage-dump";
-    WasmComponentLd, "src/tools/wasm-component-ld", "wasm-component-ld", is_unstable_tool = true, allow_features = "min_specialization";
     UnicodeTableGenerator, "src/tools/unicode-table-generator", "unicode-table-generator";
     FeaturesStatusDump, "src/tools/features-status-dump", "features-status-dump";
     OptimizedDist, "src/tools/opt-dist", "opt-dist", submodules = &["src/tools/rustc-perf"];
@@ -560,7 +602,7 @@ impl Step for RustcPerf {
         builder.require_submodule("src/tools/rustc-perf", None);
 
         let tool = ToolBuild {
-            compiler: self.compiler,
+            build_compiler: self.compiler,
             target: self.target,
             tool: "collector",
             mode: Mode::ToolBootstrap,
@@ -576,7 +618,7 @@ impl Step for RustcPerf {
         let res = builder.ensure(tool.clone());
         // We also need to symlink the `rustc-fake` binary to the corresponding directory,
         // because `collector` expects it in the same directory.
-        copy_link_tool_bin(builder, tool.compiler, tool.target, tool.mode, "rustc-fake");
+        copy_link_tool_bin(builder, tool.build_compiler, tool.target, tool.mode, "rustc-fake");
 
         res
     }
@@ -620,7 +662,7 @@ impl Step for ErrorIndex {
 
     fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
         builder.ensure(ToolBuild {
-            compiler: self.compiler,
+            build_compiler: self.compiler,
             target: self.compiler.host,
             tool: "error_index_generator",
             mode: Mode::ToolRustc,
@@ -636,7 +678,7 @@ impl Step for ErrorIndex {
 
 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct RemoteTestServer {
-    pub compiler: Compiler,
+    pub build_compiler: Compiler,
     pub target: TargetSelection,
 }
 
@@ -649,17 +691,20 @@ impl Step for RemoteTestServer {
 
     fn make_run(run: RunConfig<'_>) {
         run.builder.ensure(RemoteTestServer {
-            compiler: run.builder.compiler(run.builder.top_stage, run.builder.config.host_target),
+            build_compiler: get_tool_target_compiler(
+                run.builder,
+                ToolTargetBuildMode::Build(run.target),
+            ),
             target: run.target,
         });
     }
 
     fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
         builder.ensure(ToolBuild {
-            compiler: self.compiler,
+            build_compiler: self.build_compiler,
             target: self.target,
             tool: "remote-test-server",
-            mode: Mode::ToolStd,
+            mode: Mode::ToolTarget,
             path: "src/tools/remote-test-server",
             source_type: SourceType::InTree,
             extra_features: Vec::new(),
@@ -668,6 +713,10 @@ impl Step for RemoteTestServer {
             artifact_kind: ToolArtifactKind::Binary,
         })
     }
+
+    fn metadata(&self) -> Option<StepMetadata> {
+        Some(StepMetadata::build("remote-test-server", self.target).built_by(self.build_compiler))
+    }
 }
 
 #[derive(Debug, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]
@@ -757,7 +806,7 @@ impl Step for Rustdoc {
 
         let ToolBuildResult { tool_path, build_compiler, target_compiler } =
             builder.ensure(ToolBuild {
-                compiler: target_compiler,
+                build_compiler: target_compiler,
                 target,
                 // Cargo adds a number of paths to the dylib search path on windows, which results in
                 // the wrong rustdoc being executed. To avoid the conflicting rustdocs, we name the "tool"
@@ -825,7 +874,7 @@ impl Step for Cargo {
         builder.build.require_submodule("src/tools/cargo", None);
 
         builder.ensure(ToolBuild {
-            compiler: self.compiler,
+            build_compiler: self.compiler,
             target: self.target,
             tool: "cargo",
             mode: Mode::ToolRustc,
@@ -839,17 +888,50 @@ impl Step for Cargo {
     }
 }
 
+/// Represents a built LldWrapper, the `lld-wrapper` tool itself, and a directory
+/// containing a build of LLD.
+#[derive(Clone)]
+pub struct BuiltLldWrapper {
+    tool: ToolBuildResult,
+    lld_dir: PathBuf,
+}
+
 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct LldWrapper {
     pub build_compiler: Compiler,
-    pub target_compiler: Compiler,
+    pub target: TargetSelection,
+}
+
+impl LldWrapper {
+    /// Returns `LldWrapper` that should be **used** by the passed compiler.
+    pub fn for_use_by_compiler(builder: &Builder<'_>, target_compiler: Compiler) -> Self {
+        Self {
+            build_compiler: get_tool_target_compiler(
+                builder,
+                ToolTargetBuildMode::Dist(target_compiler),
+            ),
+            target: target_compiler.host,
+        }
+    }
 }
 
 impl Step for LldWrapper {
-    type Output = ToolBuildResult;
+    type Output = BuiltLldWrapper;
+
+    const ONLY_HOSTS: bool = true;
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
-        run.never()
+        run.path("src/tools/lld-wrapper")
+    }
+
+    fn make_run(run: RunConfig<'_>) {
+        run.builder.ensure(LldWrapper {
+            build_compiler: get_tool_target_compiler(
+                run.builder,
+                ToolTargetBuildMode::Build(run.target),
+            ),
+            target: run.target,
+        });
     }
 
     #[cfg_attr(
@@ -858,25 +940,16 @@ impl Step for LldWrapper {
             level = "debug",
             name = "LldWrapper::run",
             skip_all,
-            fields(build_compiler = ?self.build_compiler, target_compiler = ?self.target_compiler),
+            fields(build_compiler = ?self.build_compiler),
         ),
     )]
-    fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
-        if builder.config.dry_run() {
-            return ToolBuildResult {
-                tool_path: Default::default(),
-                build_compiler: self.build_compiler,
-                target_compiler: self.target_compiler,
-            };
-        }
-
-        let target = self.target_compiler.host;
-
-        let tool_result = builder.ensure(ToolBuild {
-            compiler: self.build_compiler,
-            target,
+    fn run(self, builder: &Builder<'_>) -> Self::Output {
+        let lld_dir = builder.ensure(llvm::Lld { target: self.target });
+        let tool = builder.ensure(ToolBuild {
+            build_compiler: self.build_compiler,
+            target: self.target,
             tool: "lld-wrapper",
-            mode: Mode::ToolStd,
+            mode: Mode::ToolTarget,
             path: "src/tools/lld-wrapper",
             source_type: SourceType::InTree,
             extra_features: Vec::new(),
@@ -884,38 +957,110 @@ impl Step for LldWrapper {
             cargo_args: Vec::new(),
             artifact_kind: ToolArtifactKind::Binary,
         });
+        BuiltLldWrapper { tool, lld_dir }
+    }
 
-        let libdir_bin = builder.sysroot_target_bindir(self.target_compiler, target);
-        t!(fs::create_dir_all(&libdir_bin));
+    fn metadata(&self) -> Option<StepMetadata> {
+        Some(StepMetadata::build("LldWrapper", self.target).built_by(self.build_compiler))
+    }
+}
 
-        let lld_install = builder.ensure(llvm::Lld { target });
-        let src_exe = exe("lld", target);
-        let dst_exe = exe("rust-lld", target);
+pub(crate) fn copy_lld_artifacts(
+    builder: &Builder<'_>,
+    lld_wrapper: BuiltLldWrapper,
+    target_compiler: Compiler,
+) {
+    let target = target_compiler.host;
 
+    let libdir_bin = builder.sysroot_target_bindir(target_compiler, target);
+    t!(fs::create_dir_all(&libdir_bin));
+
+    let src_exe = exe("lld", target);
+    let dst_exe = exe("rust-lld", target);
+
+    builder.copy_link(
+        &lld_wrapper.lld_dir.join("bin").join(src_exe),
+        &libdir_bin.join(dst_exe),
+        FileType::Executable,
+    );
+    let self_contained_lld_dir = libdir_bin.join("gcc-ld");
+    t!(fs::create_dir_all(&self_contained_lld_dir));
+
+    for name in crate::LLD_FILE_NAMES {
         builder.copy_link(
-            &lld_install.join("bin").join(src_exe),
-            &libdir_bin.join(dst_exe),
+            &lld_wrapper.tool.tool_path,
+            &self_contained_lld_dir.join(exe(name, target)),
             FileType::Executable,
         );
-        let self_contained_lld_dir = libdir_bin.join("gcc-ld");
-        t!(fs::create_dir_all(&self_contained_lld_dir));
-
-        for name in crate::LLD_FILE_NAMES {
-            builder.copy_link(
-                &tool_result.tool_path,
-                &self_contained_lld_dir.join(exe(name, target)),
-                FileType::Executable,
-            );
+    }
+}
+
+/// Builds the `wasm-component-ld` linker wrapper, which is shipped with rustc to be executed on the
+/// host platform where rustc runs.
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
+pub struct WasmComponentLd {
+    build_compiler: Compiler,
+    target: TargetSelection,
+}
+
+impl WasmComponentLd {
+    /// Returns `WasmComponentLd` that should be **used** by the passed compiler.
+    pub fn for_use_by_compiler(builder: &Builder<'_>, target_compiler: Compiler) -> Self {
+        Self {
+            build_compiler: get_tool_target_compiler(
+                builder,
+                ToolTargetBuildMode::Dist(target_compiler),
+            ),
+            target: target_compiler.host,
         }
+    }
+}
+
+impl Step for WasmComponentLd {
+    type Output = ToolBuildResult;
+
+    const ONLY_HOSTS: bool = true;
+
+    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
+        run.path("src/tools/wasm-component-ld")
+    }
+
+    fn make_run(run: RunConfig<'_>) {
+        run.builder.ensure(WasmComponentLd {
+            build_compiler: get_tool_target_compiler(
+                run.builder,
+                ToolTargetBuildMode::Build(run.target),
+            ),
+            target: run.target,
+        });
+    }
 
-        tool_result
+    #[cfg_attr(
+        feature = "tracing",
+        instrument(
+            level = "debug",
+            name = "WasmComponentLd::run",
+            skip_all,
+            fields(build_compiler = ?self.build_compiler),
+        ),
+    )]
+    fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
+        builder.ensure(ToolBuild {
+            build_compiler: self.build_compiler,
+            target: self.target,
+            tool: "wasm-component-ld",
+            mode: Mode::ToolTarget,
+            path: "src/tools/wasm-component-ld",
+            source_type: SourceType::InTree,
+            extra_features: vec![],
+            allow_features: "",
+            cargo_args: vec![],
+            artifact_kind: ToolArtifactKind::Binary,
+        })
     }
 
     fn metadata(&self) -> Option<StepMetadata> {
-        Some(
-            StepMetadata::build("LldWrapper", self.target_compiler.host)
-                .built_by(self.build_compiler),
-        )
+        Some(StepMetadata::build("WasmComponentLd", self.target).built_by(self.build_compiler))
     }
 }
 
@@ -948,7 +1093,7 @@ impl Step for RustAnalyzer {
 
     fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
         builder.ensure(ToolBuild {
-            compiler: self.compiler,
+            build_compiler: self.compiler,
             target: self.target,
             tool: "rust-analyzer",
             mode: Mode::ToolRustc,
@@ -993,7 +1138,7 @@ impl Step for RustAnalyzerProcMacroSrv {
 
     fn run(self, builder: &Builder<'_>) -> Option<ToolBuildResult> {
         let tool_result = builder.ensure(ToolBuild {
-            compiler: self.compiler,
+            build_compiler: self.compiler,
             target: self.target,
             tool: "rust-analyzer-proc-macro-srv",
             mode: Mode::ToolRustc,
@@ -1021,8 +1166,35 @@ impl Step for RustAnalyzerProcMacroSrv {
 
 #[derive(Debug, Clone, Hash, PartialEq, Eq)]
 pub struct LlvmBitcodeLinker {
-    pub build_compiler: Compiler,
-    pub target: TargetSelection,
+    build_compiler: Compiler,
+    target: TargetSelection,
+}
+
+impl LlvmBitcodeLinker {
+    /// Returns `LlvmBitcodeLinker` that will be **compiled** by the passed compiler, for the given
+    /// `target`.
+    pub fn from_build_compiler(build_compiler: Compiler, target: TargetSelection) -> Self {
+        Self { build_compiler, target }
+    }
+
+    /// Returns `LlvmBitcodeLinker` that should be **used** by the passed compiler.
+    pub fn from_target_compiler(builder: &Builder<'_>, target_compiler: Compiler) -> Self {
+        Self {
+            build_compiler: get_tool_target_compiler(
+                builder,
+                ToolTargetBuildMode::Dist(target_compiler),
+            ),
+            target: target_compiler.host,
+        }
+    }
+
+    /// Return a compiler that is able to build this tool for the given `target`.
+    pub fn get_build_compiler_for_target(
+        builder: &Builder<'_>,
+        target: TargetSelection,
+    ) -> Compiler {
+        get_tool_target_compiler(builder, ToolTargetBuildMode::Build(target))
+    }
 }
 
 impl Step for LlvmBitcodeLinker {
@@ -1038,9 +1210,7 @@ impl Step for LlvmBitcodeLinker {
 
     fn make_run(run: RunConfig<'_>) {
         run.builder.ensure(LlvmBitcodeLinker {
-            build_compiler: run
-                .builder
-                .compiler(run.builder.top_stage, run.builder.config.host_target),
+            build_compiler: Self::get_build_compiler_for_target(run.builder, run.target),
             target: run.target,
         });
     }
@@ -1051,10 +1221,10 @@ impl Step for LlvmBitcodeLinker {
     )]
     fn run(self, builder: &Builder<'_>) -> ToolBuildResult {
         builder.ensure(ToolBuild {
-            compiler: self.build_compiler,
+            build_compiler: self.build_compiler,
             target: self.target,
             tool: "llvm-bitcode-linker",
-            mode: Mode::ToolRustc,
+            mode: Mode::ToolTarget,
             path: "src/tools/llvm-bitcode-linker",
             source_type: SourceType::InTree,
             extra_features: vec![],
@@ -1239,7 +1409,7 @@ fn run_tool_build_step(
 
     let ToolBuildResult { tool_path, build_compiler, target_compiler } =
         builder.ensure(ToolBuild {
-            compiler,
+            build_compiler: compiler,
             target,
             tool: tool_name,
             mode: Mode::ToolRustc,
@@ -1338,7 +1508,7 @@ impl Step for TestFloatParse {
         let compiler = builder.compiler(builder.top_stage, bootstrap_host);
 
         builder.ensure(ToolBuild {
-            compiler,
+            build_compiler: compiler,
             target: bootstrap_host,
             tool: "test-float-parse",
             mode: Mode::ToolStd,
diff --git a/src/bootstrap/src/core/builder/cargo.rs b/src/bootstrap/src/core/builder/cargo.rs
index a3b471ca56e..badd5f24dba 100644
--- a/src/bootstrap/src/core/builder/cargo.rs
+++ b/src/bootstrap/src/core/builder/cargo.rs
@@ -537,7 +537,7 @@ impl Builder<'_> {
             }
         }
 
-        let stage = if compiler.stage == 0 && self.local_rebuild {
+        let build_compiler_stage = if compiler.stage == 0 && self.local_rebuild {
             // Assume the local-rebuild rustc already has stage1 features.
             1
         } else {
@@ -545,15 +545,17 @@ impl Builder<'_> {
         };
 
         // We synthetically interpret a stage0 compiler used to build tools as a
-        // "raw" compiler in that it's the exact snapshot we download. Normally
-        // the stage0 build means it uses libraries build by the stage0
-        // compiler, but for tools we just use the precompiled libraries that
-        // we've downloaded
-        let use_snapshot = mode == Mode::ToolBootstrap;
-        assert!(!use_snapshot || stage == 0 || self.local_rebuild);
-
-        let maybe_sysroot = self.sysroot(compiler);
-        let sysroot = if use_snapshot { self.rustc_snapshot_sysroot() } else { &maybe_sysroot };
+        // "raw" compiler in that it's the exact snapshot we download. For things like
+        // ToolRustc, we would have to use the artificial stage0-sysroot compiler instead.
+        let use_snapshot =
+            mode == Mode::ToolBootstrap || (mode == Mode::ToolTarget && build_compiler_stage == 0);
+        assert!(!use_snapshot || build_compiler_stage == 0 || self.local_rebuild);
+
+        let sysroot = if use_snapshot {
+            self.rustc_snapshot_sysroot().to_path_buf()
+        } else {
+            self.sysroot(compiler)
+        };
         let libdir = self.rustc_libdir(compiler);
 
         let sysroot_str = sysroot.as_os_str().to_str().expect("sysroot should be UTF-8");
@@ -562,7 +564,7 @@ impl Builder<'_> {
         }
 
         let mut rustflags = Rustflags::new(target);
-        if stage != 0 {
+        if build_compiler_stage != 0 {
             if let Ok(s) = env::var("CARGOFLAGS_NOT_BOOTSTRAP") {
                 cargo.args(s.split_whitespace());
             }
@@ -604,7 +606,7 @@ impl Builder<'_> {
         // sysroot. Passing this cfg enables raw-dylib support instead, which makes the native
         // library unnecessary. This can be removed when windows-rs enables raw-dylib
         // unconditionally.
-        if let Mode::Rustc | Mode::ToolRustc | Mode::ToolBootstrap = mode {
+        if let Mode::Rustc | Mode::ToolRustc | Mode::ToolBootstrap | Mode::ToolTarget = mode {
             rustflags.arg("--cfg=windows_raw_dylib");
         }
 
@@ -657,7 +659,7 @@ impl Builder<'_> {
         // FIXME(rust-lang/cargo#5754) we shouldn't be using special command arguments
         // to the host invocation here, but rather Cargo should know what flags to pass rustc
         // itself.
-        if stage == 0 {
+        if build_compiler_stage == 0 {
             hostflags.arg("--cfg=bootstrap");
         }
 
@@ -666,7 +668,7 @@ impl Builder<'_> {
         // #71458.
         let mut rustdocflags = rustflags.clone();
         rustdocflags.propagate_cargo_env("RUSTDOCFLAGS");
-        if stage == 0 {
+        if build_compiler_stage == 0 {
             rustdocflags.env("RUSTDOCFLAGS_BOOTSTRAP");
         } else {
             rustdocflags.env("RUSTDOCFLAGS_NOT_BOOTSTRAP");
@@ -677,7 +679,7 @@ impl Builder<'_> {
         }
 
         match mode {
-            Mode::Std | Mode::ToolBootstrap | Mode::ToolStd => {}
+            Mode::Std | Mode::ToolBootstrap | Mode::ToolStd | Mode::ToolTarget => {}
             Mode::Rustc | Mode::Codegen | Mode::ToolRustc => {
                 // Build proc macros both for the host and the target unless proc-macros are not
                 // supported by the target.
@@ -719,7 +721,7 @@ impl Builder<'_> {
         // feature on the rustc side.
         cargo.arg("-Zbinary-dep-depinfo");
         let allow_features = match mode {
-            Mode::ToolBootstrap | Mode::ToolStd => {
+            Mode::ToolBootstrap | Mode::ToolStd | Mode::ToolTarget => {
                 // Restrict the allowed features so we don't depend on nightly
                 // accidentally.
                 //
@@ -833,7 +835,7 @@ impl Builder<'_> {
         cargo
             .env("RUSTBUILD_NATIVE_DIR", self.native_dir(target))
             .env("RUSTC_REAL", self.rustc(compiler))
-            .env("RUSTC_STAGE", stage.to_string())
+            .env("RUSTC_STAGE", build_compiler_stage.to_string())
             .env("RUSTC_SYSROOT", sysroot)
             .env("RUSTC_LIBDIR", libdir)
             .env("RUSTDOC", self.bootstrap_out.join("rustdoc"))
@@ -878,7 +880,7 @@ impl Builder<'_> {
         let debuginfo_level = match mode {
             Mode::Rustc | Mode::Codegen => self.config.rust_debuginfo_level_rustc,
             Mode::Std => self.config.rust_debuginfo_level_std,
-            Mode::ToolBootstrap | Mode::ToolStd | Mode::ToolRustc => {
+            Mode::ToolBootstrap | Mode::ToolStd | Mode::ToolRustc | Mode::ToolTarget => {
                 self.config.rust_debuginfo_level_tools
             }
         };
@@ -890,11 +892,10 @@ impl Builder<'_> {
             profile_var("DEBUG_ASSERTIONS"),
             match mode {
                 Mode::Std => self.config.std_debug_assertions,
-                Mode::Rustc => self.config.rustc_debug_assertions,
-                Mode::Codegen => self.config.rustc_debug_assertions,
-                Mode::ToolBootstrap => self.config.tools_debug_assertions,
-                Mode::ToolStd => self.config.tools_debug_assertions,
-                Mode::ToolRustc => self.config.tools_debug_assertions,
+                Mode::Rustc | Mode::Codegen => self.config.rustc_debug_assertions,
+                Mode::ToolBootstrap | Mode::ToolStd | Mode::ToolRustc | Mode::ToolTarget => {
+                    self.config.tools_debug_assertions
+                }
             }
             .to_string(),
         );
@@ -965,7 +966,11 @@ impl Builder<'_> {
                     cargo.env("CFG_VIRTUAL_RUSTC_DEV_SOURCE_BASE_DIR", map_to);
                 }
             }
-            Mode::Std | Mode::ToolBootstrap | Mode::ToolRustc | Mode::ToolStd => {
+            Mode::Std
+            | Mode::ToolBootstrap
+            | Mode::ToolRustc
+            | Mode::ToolStd
+            | Mode::ToolTarget => {
                 if let Some(ref map_to) =
                     self.build.debuginfo_map_to(GitRepo::Rustc, RemapScheme::NonCompiler)
                 {
@@ -1280,7 +1285,7 @@ impl Builder<'_> {
             };
 
             if let Some(limit) = limit
-                && (stage == 0
+                && (build_compiler_stage == 0
                     || self.config.default_codegen_backend(target).unwrap_or_default() == "llvm")
             {
                 rustflags.arg(&format!("-Cllvm-args=-import-instr-limit={limit}"));
diff --git a/src/bootstrap/src/core/builder/mod.rs b/src/bootstrap/src/core/builder/mod.rs
index 1b75d00b30e..d73e2bce25b 100644
--- a/src/bootstrap/src/core/builder/mod.rs
+++ b/src/bootstrap/src/core/builder/mod.rs
@@ -963,6 +963,7 @@ impl<'a> Builder<'a> {
                 tool::RemoteTestServer,
                 tool::RemoteTestClient,
                 tool::RustInstaller,
+                tool::FeaturesStatusDump,
                 tool::Cargo,
                 tool::RustAnalyzer,
                 tool::RustAnalyzerProcMacroSrv,
@@ -984,6 +985,8 @@ impl<'a> Builder<'a> {
                 tool::CoverageDump,
                 tool::LlvmBitcodeLinker,
                 tool::RustcPerf,
+                tool::WasmComponentLd,
+                tool::LldWrapper
             ),
             Kind::Clippy => describe!(
                 clippy::Std,
diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs
index 51a90649692..2f8cdd2f8c8 100644
--- a/src/bootstrap/src/core/builder/tests.rs
+++ b/src/bootstrap/src/core/builder/tests.rs
@@ -769,11 +769,11 @@ mod snapshot {
         [build] llvm <host>
         [build] rustc 0 <host> -> rustc 1 <host>
         [build] rustc 0 <host> -> LldWrapper 1 <host>
-        [build] rustc 1 <host> -> LlvmBitcodeLinker 2 <host>
+        [build] rustc 0 <host> -> LlvmBitcodeLinker 1 <host>
         [build] rustc 1 <host> -> std 1 <host>
         [build] rustc 1 <host> -> rustc 2 <host>
         [build] rustc 1 <host> -> LldWrapper 2 <host>
-        [build] rustc 2 <host> -> LlvmBitcodeLinker 3 <host>
+        [build] rustc 1 <host> -> LlvmBitcodeLinker 2 <host>
         [build] rustc 2 <host> -> std 2 <host>
         [build] rustdoc 1 <host>
         "
@@ -793,17 +793,17 @@ mod snapshot {
         [build] llvm <host>
         [build] rustc 0 <host> -> rustc 1 <host>
         [build] rustc 0 <host> -> LldWrapper 1 <host>
-        [build] rustc 1 <host> -> LlvmBitcodeLinker 2 <host>
+        [build] rustc 0 <host> -> LlvmBitcodeLinker 1 <host>
         [build] rustc 1 <host> -> std 1 <host>
         [build] rustc 1 <host> -> rustc 2 <host>
         [build] rustc 1 <host> -> LldWrapper 2 <host>
-        [build] rustc 2 <host> -> LlvmBitcodeLinker 3 <host>
+        [build] rustc 1 <host> -> LlvmBitcodeLinker 2 <host>
         [build] rustc 1 <host> -> std 1 <target1>
         [build] rustc 2 <host> -> std 2 <target1>
         [build] llvm <target1>
         [build] rustc 1 <host> -> rustc 2 <target1>
         [build] rustc 1 <host> -> LldWrapper 2 <target1>
-        [build] rustc 2 <target1> -> LlvmBitcodeLinker 3 <target1>
+        [build] rustc 1 <host> -> LlvmBitcodeLinker 2 <target1>
         [build] rustdoc 1 <target1>
         "
         );
@@ -1062,18 +1062,28 @@ mod snapshot {
     fn dist_extended() {
         let ctx = TestCtx::new();
         insta::assert_snapshot!(
-            ctx
-                .config("dist")
-                .args(&["--set", "build.extended=true"])
-                .render_steps(), @r"
+            ctx.config("dist")
+            .args(&[
+                "--set",
+                "build.extended=true",
+                "--set",
+                "rust.llvm-bitcode-linker=true",
+                "--set",
+                "rust.lld=true",
+            ])
+            .render_steps(), @r"
         [build] rustc 0 <host> -> UnstableBookGen 1 <host>
         [build] rustc 0 <host> -> Rustbook 1 <host>
         [build] llvm <host>
         [build] rustc 0 <host> -> rustc 1 <host>
+        [build] rustc 0 <host> -> LldWrapper 1 <host>
         [build] rustc 0 <host> -> WasmComponentLd 1 <host>
+        [build] rustc 0 <host> -> LlvmBitcodeLinker 1 <host>
         [build] rustc 1 <host> -> std 1 <host>
         [build] rustc 1 <host> -> rustc 2 <host>
+        [build] rustc 1 <host> -> LldWrapper 2 <host>
         [build] rustc 1 <host> -> WasmComponentLd 2 <host>
+        [build] rustc 1 <host> -> LlvmBitcodeLinker 2 <host>
         [build] rustdoc 1 <host>
         [doc] std 2 <host> crates=[alloc,compiler_builtins,core,panic_abort,panic_unwind,proc_macro,std,sysroot,test,unwind]
         [build] rustc 2 <host> -> std 2 <host>
@@ -1092,7 +1102,6 @@ mod snapshot {
         [build] rustc 0 <host> -> cargo-clippy 1 <host>
         [build] rustc 0 <host> -> miri 1 <host>
         [build] rustc 0 <host> -> cargo-miri 1 <host>
-        [build] rustc 1 <host> -> LlvmBitcodeLinker 2 <host>
         ");
     }
 
diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs
index 44be51815c7..63aab4d116a 100644
--- a/src/bootstrap/src/lib.rs
+++ b/src/bootstrap/src/lib.rs
@@ -253,12 +253,24 @@ pub enum Mode {
     /// These tools are intended to be only executed on the host system that
     /// invokes bootstrap, and they thus cannot be cross-compiled.
     ///
-    /// They are always built using the stage0 compiler, and typically they
+    /// They are always built using the stage0 compiler, and they
     /// can be compiled with stable Rust.
     ///
     /// These tools also essentially do not participate in staging.
     ToolBootstrap,
 
+    /// Build a cross-compilable helper tool. These tools do not depend on unstable features or
+    /// compiler internals, but they might be cross-compilable (so we cannot build them using the
+    /// stage0 compiler, unlike `ToolBootstrap`).
+    ///
+    /// Some of these tools are also shipped in our `dist` archives.
+    /// While we could compile them using the stage0 compiler when not cross-compiling, we instead
+    /// use the in-tree compiler (and std) to build them, so that we can ship e.g. std security
+    /// fixes and avoid depending fully on stage0 for the artifacts that we ship.
+    ///
+    /// This mode is used e.g. for linkers and linker tools invoked by rustc on its host target.
+    ToolTarget,
+
     /// Build a tool which uses the locally built std, placing output in the
     /// "stageN-tools" directory. Its usage is quite rare, mainly used by
     /// compiletest which needs libtest.
@@ -273,11 +285,21 @@ pub enum Mode {
 
 impl Mode {
     pub fn is_tool(&self) -> bool {
-        matches!(self, Mode::ToolBootstrap | Mode::ToolRustc | Mode::ToolStd)
+        match self {
+            Mode::ToolBootstrap | Mode::ToolRustc | Mode::ToolStd | Mode::ToolTarget => true,
+            Mode::Std | Mode::Codegen | Mode::Rustc => false,
+        }
     }
 
     pub fn must_support_dlopen(&self) -> bool {
-        matches!(self, Mode::Std | Mode::Codegen)
+        match self {
+            Mode::Std | Mode::Codegen => true,
+            Mode::ToolBootstrap
+            | Mode::ToolRustc
+            | Mode::ToolStd
+            | Mode::ToolTarget
+            | Mode::Rustc => false,
+        }
     }
 }
 
@@ -802,17 +824,39 @@ impl Build {
     /// stage when running with a particular host compiler.
     ///
     /// The mode indicates what the root directory is for.
-    fn stage_out(&self, compiler: Compiler, mode: Mode) -> PathBuf {
-        let suffix = match mode {
-            Mode::Std => "-std",
-            Mode::Rustc => "-rustc",
-            Mode::Codegen => "-codegen",
-            Mode::ToolBootstrap => {
-                return self.out.join(compiler.host).join("bootstrap-tools");
+    fn stage_out(&self, build_compiler: Compiler, mode: Mode) -> PathBuf {
+        use std::fmt::Write;
+
+        fn bootstrap_tool() -> (Option<u32>, &'static str) {
+            (None, "bootstrap-tools")
+        }
+        fn staged_tool(build_compiler: Compiler) -> (Option<u32>, &'static str) {
+            (Some(build_compiler.stage), "tools")
+        }
+
+        let (stage, suffix) = match mode {
+            Mode::Std => (Some(build_compiler.stage), "std"),
+            Mode::Rustc => (Some(build_compiler.stage), "rustc"),
+            Mode::Codegen => (Some(build_compiler.stage), "codegen"),
+            Mode::ToolBootstrap => bootstrap_tool(),
+            Mode::ToolStd | Mode::ToolRustc => (Some(build_compiler.stage), "tools"),
+            Mode::ToolTarget => {
+                // If we're not cross-compiling (the common case), share the target directory with
+                // bootstrap tools to reuse the build cache.
+                if build_compiler.stage == 0 {
+                    bootstrap_tool()
+                } else {
+                    staged_tool(build_compiler)
+                }
             }
-            Mode::ToolStd | Mode::ToolRustc => "-tools",
         };
-        self.out.join(compiler.host).join(format!("stage{}{}", compiler.stage, suffix))
+        let path = self.out.join(build_compiler.host);
+        let mut dir_name = String::new();
+        if let Some(stage) = stage {
+            write!(dir_name, "stage{stage}-").unwrap();
+        }
+        dir_name.push_str(suffix);
+        path.join(dir_name)
     }
 
     /// Returns the root output directory for all Cargo output in a given stage,
diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml
index f0c52fe3d1c..0a6ebe44b3d 100644
--- a/src/ci/github-actions/jobs.yml
+++ b/src/ci/github-actions/jobs.yml
@@ -172,9 +172,9 @@ try:
 optional:
   # This job is used just to test optional jobs.
   # It will be replaced by tier 2 and tier 3 jobs in the future.
-  - name: optional-mingw-check-1
+  - name: optional-pr-check-1
     env:
-      IMAGE: mingw-check-1
+      IMAGE: pr-check-1
     <<: *job-linux-4c
 
 # Main CI jobs that have to be green to merge a commit into master
diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md
index 63aa08c389c..6685add461e 100644
--- a/src/doc/rustc-dev-guide/src/tests/directives.md
+++ b/src/doc/rustc-dev-guide/src/tests/directives.md
@@ -75,8 +75,10 @@ expectations](ui.md#controlling-passfail-expectations).
 | `check-fail`                | Building (no codegen) should fail           | `ui`, `crashes`                           | N/A             |
 | `build-pass`                | Building should pass                        | `ui`, `crashes`, `codegen`, `incremental` | N/A             |
 | `build-fail`                | Building should fail                        | `ui`, `crashes`                           | N/A             |
-| `run-pass`                  | Running the test binary should pass         | `ui`, `crashes`, `incremental`            | N/A             |
-| `run-fail`                  | Running the test binary should fail         | `ui`, `crashes`                           | N/A             |
+| `run-pass`                  | Program must exit with code `0`             | `ui`, `crashes`, `incremental`            | N/A             |
+| `run-fail`                  | Program must exit with code `1..=127`       | `ui`, `crashes`                           | N/A             |
+| `run-crash`                 | Program must crash                          | `ui`                                      | N/A             |
+| `run-fail-or-crash`         | Program must `run-fail` or `run-crash`      | `ui`                                      | N/A             |
 | `ignore-pass`               | Ignore `--pass` flag                        | `ui`, `crashes`, `codegen`, `incremental` | N/A             |
 | `dont-check-failure-status` | Don't check exact failure status (i.e. `1`) | `ui`, `incremental`                       | N/A             |
 | `failure-status`            | Check                                       | `ui`, `crashes`                           | Any `u16`       |
diff --git a/src/doc/rustc-dev-guide/src/tests/ui.md b/src/doc/rustc-dev-guide/src/tests/ui.md
index 4fce5838b6e..9bfc60e08a6 100644
--- a/src/doc/rustc-dev-guide/src/tests/ui.md
+++ b/src/doc/rustc-dev-guide/src/tests/ui.md
@@ -448,7 +448,7 @@ even run the resulting program. Just add one of the following
   - `//@ build-pass` — compilation and linking should succeed but do
     not run the resulting binary.
   - `//@ run-pass` — compilation should succeed and running the resulting
-    binary should also succeed.
+    binary should make it exit with code 0 which indicates success.
 - Fail directives:
   - `//@ check-fail` — compilation should fail (the codegen phase is skipped).
     This is the default for UI tests.
@@ -457,10 +457,20 @@ even run the resulting program. Just add one of the following
     - First time is to ensure that the compile succeeds without the codegen phase
     - Second time is to ensure that the full compile fails
   - `//@ run-fail` — compilation should succeed, but running the resulting
-    binary should fail.
-
-For `run-pass` and `run-fail` tests, by default the output of the program itself
-is not checked.
+    binary should make it exit with a code in the range `1..=127` which
+    indicates regular failure. On targets without unwind support, crashes
+    are also accepted.
+  - `//@ run-crash` — compilation should succeed, but running the resulting
+    binary should fail with a crash. Crashing is defined as "not exiting with
+    a code in the range `0..=127`". Example on Linux: Termination by `SIGABRT`
+    or `SIGSEGV`. Example on Windows: Exiting with the code for
+    `STATUS_ILLEGAL_INSTRUCTION` (`0xC000001D`).
+  - `//@ run-fail-or-crash` — compilation should succeed, but running the
+    resulting binary should either `run-fail` or `run-crash`. Useful if a test
+    crashes on some targets but just fails on others.
+
+For `run-pass`. `run-fail`, `run-crash` and `run-fail-or-crash` tests, by
+default the output of the program itself is not checked.
 
 If you want to check the output of running the program, include the
 `check-run-results` directive. This will check for a `.run.stderr` and
diff --git a/src/doc/rustc/src/exploit-mitigations.md b/src/doc/rustc/src/exploit-mitigations.md
index f8bafe03214..c80d7d8743c 100644
--- a/src/doc/rustc/src/exploit-mitigations.md
+++ b/src/doc/rustc/src/exploit-mitigations.md
@@ -54,17 +54,17 @@ Summary of exploit mitigations supported by the Rust compiler when building
 programs for the Linux operating system on the AMD64 architecture and
 equivalent.
 
-| Exploit mitigation | Supported and enabled by default | Since |
-| - | - | - |
-| Position-independent executable | Yes | 0.12.0 (2014-10-09) |
-| Integer overflow checks | Yes (enabled when debug assertions are enabled, and disabled when debug assertions are disabled) | 1.1.0 (2015-06-25) |
-| Non-executable memory regions | Yes | 1.8.0 (2016-04-14) |
-| Stack clashing protection | Yes | 1.20.0 (2017-08-31) |
-| Read-only relocations and immediate binding | Yes | 1.21.0 (2017-10-12) |
-| Heap corruption protection | Yes | 1.32.0 (2019-01-17) (via operating system default or specified allocator) |
-| Stack smashing protection | Yes | Nightly |
-| Forward-edge control flow protection | Yes | Nightly |
-| Backward-edge control flow protection (e.g., shadow and safe stack) | Yes | Nightly |
+| Exploit mitigation | Supported | Enabled by default | Since |
+| - | - | - | - |
+| Position-independent executable | Yes | Yes | 0.12.0 (2014-10-09) |
+| Integer overflow checks | Yes | (enabled when debug assertions are enabled, and disabled when debug assertions are disabled) | 1.1.0 (2015-06-25) |
+| Non-executable memory regions | Yes | Yes | 1.8.0 (2016-04-14) |
+| Stack clashing protection | Yes | Yes | 1.20.0 (2017-08-31) |
+| Read-only relocations and immediate binding | Yes | Yes | 1.21.0 (2017-10-12) |
+| Heap corruption protection | Yes | Yes | 1.32.0 (2019-01-17) (via operating system default or specified allocator) |
+| Stack smashing protection | Yes | No, `-Z stack-protector` | Nightly |
+| Forward-edge control flow protection | Yes | No, `-Z sanitizer=cfi` | Nightly |
+| Backward-edge control flow protection (e.g., shadow and safe stack) | Yes | No, `-Z sanitizer=shadow-call-stack,safestack` | Nightly |
 
 [^all-targets]: See <https://github.com/rust-lang/rust/tree/master/compiler/rustc_target/src/spec>
     for a list of targets and their default options.
diff --git a/src/doc/rustc/src/platform-support/xtensa.md b/src/doc/rustc/src/platform-support/xtensa.md
index 994b3adb92e..8592ce7eda9 100644
--- a/src/doc/rustc/src/platform-support/xtensa.md
+++ b/src/doc/rustc/src/platform-support/xtensa.md
@@ -24,4 +24,4 @@ Xtensa targets that support `std` are documented in the [ESP-IDF platform suppor
 
 ## Building the targets
 
-The targets can be built by installing the [Xtensa enabled Rust channel](https://github.com/esp-rs/rust/). See instructions in the [RISC-V and Xtensa Targets section of The Rust on ESP Book](https://docs.esp-rs.org/book/installation/riscv-and-xtensa.html).
+The targets can be built by installing the [Xtensa enabled Rust channel](https://github.com/esp-rs/rust/). See instructions in the [RISC-V and Xtensa Targets section of The Rust on ESP Book](https://docs.espressif.com/projects/rust/book/installation/index.html).
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index 09647492d93..5ac5da24299 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -1677,7 +1677,7 @@ impl Type {
         }
     }
 
-    pub(crate) fn generics<'a>(&'a self) -> Option<impl Iterator<Item = &'a Type>> {
+    pub(crate) fn generics(&self) -> Option<impl Iterator<Item = &Type>> {
         match self {
             Type::Path { path, .. } => path.generics(),
             _ => None,
@@ -2227,7 +2227,7 @@ impl Path {
         self.segments.last().map(|seg| &seg.args)
     }
 
-    pub(crate) fn generics<'a>(&'a self) -> Option<impl Iterator<Item = &'a Type>> {
+    pub(crate) fn generics(&self) -> Option<impl Iterator<Item = &Type>> {
         self.segments.last().and_then(|seg| {
             if let GenericArgs::AngleBracketed { ref args, .. } = seg.args {
                 Some(args.iter().filter_map(|arg| match arg {
diff --git a/src/librustdoc/clean/utils.rs b/src/librustdoc/clean/utils.rs
index fd1b17b6476..813fdee57e1 100644
--- a/src/librustdoc/clean/utils.rs
+++ b/src/librustdoc/clean/utils.rs
@@ -343,13 +343,11 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol {
 pub(crate) fn print_const(cx: &DocContext<'_>, n: ty::Const<'_>) -> String {
     match n.kind() {
         ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, args: _ }) => {
-            let s = if let Some(def) = def.as_local() {
+            if let Some(def) = def.as_local() {
                 rendered_const(cx.tcx, cx.tcx.hir_body_owned_by(def), def)
             } else {
                 inline::print_inlined_const(cx.tcx, def)
-            };
-
-            s
+            }
         }
         // array lengths are obviously usize
         ty::ConstKind::Value(cv) if *cv.ty.kind() == ty::Uint(ty::UintTy::Usize) => {
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index 9b4d2533954..38ba6b4503d 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -632,7 +632,7 @@ fn run_test(
         // the user to exploit nightly-only features on stable
         runner_compiler.env("RUSTC_BOOTSTRAP", "1");
         runner_compiler.args(compiler_args);
-        runner_compiler.args(&["--crate-type=bin", "-o"]).arg(&output_file);
+        runner_compiler.args(["--crate-type=bin", "-o"]).arg(&output_file);
         let mut extern_path = std::ffi::OsString::from(format!(
             "--extern=doctest_bundle_{edition}=",
             edition = doctest.edition
@@ -657,7 +657,7 @@ fn run_test(
         extern_path.push(&output_bundle_file);
         runner_compiler.arg(extern_path);
         runner_compiler.arg(&runner_input_file);
-        if std::fs::write(&runner_input_file, &merged_test_code).is_err() {
+        if std::fs::write(&runner_input_file, merged_test_code).is_err() {
             // If we cannot write this file for any reason, we leave. All combined tests will be
             // tested as standalone tests.
             return Err(TestFailure::CompileError);
diff --git a/src/librustdoc/doctest/rust.rs b/src/librustdoc/doctest/rust.rs
index 96975105ac5..f5ec828187a 100644
--- a/src/librustdoc/doctest/rust.rs
+++ b/src/librustdoc/doctest/rust.rs
@@ -140,7 +140,7 @@ impl HirCollector<'_> {
                 .iter()
                 .filter(|a| a.has_name(sym::attr))
                 .flat_map(|a| a.meta_item_list().unwrap_or_default())
-                .map(|i| pprust::meta_list_item_to_string(i))
+                .map(pprust::meta_list_item_to_string)
             {
                 // Add the additional attributes to the global_crate_attrs vector
                 self.collector.global_crate_attrs.push(attr);
diff --git a/src/librustdoc/formats/renderer.rs b/src/librustdoc/formats/renderer.rs
index 79ff1fa38c3..aa4be4db997 100644
--- a/src/librustdoc/formats/renderer.rs
+++ b/src/librustdoc/formats/renderer.rs
@@ -81,7 +81,7 @@ fn run_format_inner<'tcx, T: FormatRenderer<'tcx>>(
         let _timer =
             prof.generic_activity_with_arg("render_mod_item", item.name.unwrap().to_string());
 
-        cx.mod_item_in(&item)?;
+        cx.mod_item_in(item)?;
         let (clean::StrippedItem(box clean::ModuleItem(ref module))
         | clean::ModuleItem(ref module)) = item.inner.kind
         else {
@@ -99,7 +99,7 @@ fn run_format_inner<'tcx, T: FormatRenderer<'tcx>>(
     } else if let Some(item_name) = item.name
         && !item.is_extern_crate()
     {
-        prof.generic_activity_with_arg("render_item", item_name.as_str()).run(|| cx.item(&item))?;
+        prof.generic_activity_with_arg("render_item", item_name.as_str()).run(|| cx.item(item))?;
     }
     Ok(())
 }
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index b16485107a0..be8a2d511e9 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -114,9 +114,9 @@ impl clean::Generics {
             let real_params =
                 fmt::from_fn(|f| real_params.clone().map(|g| g.print(cx)).joined(", ", f));
             if f.alternate() {
-                write!(f, "<{:#}>", real_params)
+                write!(f, "<{real_params:#}>")
             } else {
-                write!(f, "&lt;{}&gt;", real_params)
+                write!(f, "&lt;{real_params}&gt;")
             }
         })
     }
@@ -594,7 +594,7 @@ pub(crate) fn href_with_root_path(
             }
         }
     };
-    let url_parts = make_href(root_path, shortty, url_parts, &fqp, is_remote);
+    let url_parts = make_href(root_path, shortty, url_parts, fqp, is_remote);
     Ok((url_parts, shortty, fqp.clone()))
 }
 
@@ -1115,7 +1115,7 @@ impl clean::Impl {
                 {
                     let last = ty.last();
                     if f.alternate() {
-                        write!(f, "{}<", last)?;
+                        write!(f, "{last}<")?;
                         self.print_type(inner_type, f, use_absolute, cx)?;
                         write!(f, ">")?;
                     } else {
@@ -1219,7 +1219,7 @@ pub(crate) fn print_params(params: &[clean::Parameter], cx: &Context<'_>) -> imp
             .map(|param| {
                 fmt::from_fn(|f| {
                     if let Some(name) = param.name {
-                        write!(f, "{}: ", name)?;
+                        write!(f, "{name}: ")?;
                     }
                     param.type_.print(cx).fmt(f)
                 })
@@ -1341,7 +1341,7 @@ impl clean::FnDecl {
                     write!(f, "const ")?;
                 }
                 if let Some(name) = param.name {
-                    write!(f, "{}: ", name)?;
+                    write!(f, "{name}: ")?;
                 }
                 param.type_.print(cx).fmt(f)?;
             }
diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs
index b2feee36c93..272180fb990 100644
--- a/src/librustdoc/html/highlight.rs
+++ b/src/librustdoc/html/highlight.rs
@@ -547,7 +547,7 @@ impl<'a> Iterator for TokenIter<'a> {
 fn get_real_ident_class(text: &str, allow_path_keywords: bool) -> Option<Class> {
     let ignore: &[&str] =
         if allow_path_keywords { &["self", "Self", "super", "crate"] } else { &["self", "Self"] };
-    if ignore.iter().any(|k| *k == text) {
+    if ignore.contains(&text) {
         return None;
     }
     Some(match text {
@@ -1159,7 +1159,7 @@ fn string_without_closing_tag<T: Display>(
         return Some("</a>");
     }
     if !open_tag {
-        write!(out, "{}", text_s).unwrap();
+        out.write_str(&text_s).unwrap();
         return None;
     }
     let klass_s = klass.as_html();
diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs
index 50320cb231d..1f92c521d46 100644
--- a/src/librustdoc/html/layout.rs
+++ b/src/librustdoc/html/layout.rs
@@ -132,6 +132,5 @@ pub(crate) fn redirect(url: &str) -> String {
     <script>location.replace("{url}" + location.search + location.hash);</script>
 </body>
 </html>"##,
-        url = url,
     )
 }
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index e41435de29c..4addf2c3c96 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -251,7 +251,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
                     if !parse_result.rust {
                         let added_classes = parse_result.added_classes;
                         let lang_string = if let Some(lang) = parse_result.unknown.first() {
-                            format!("language-{}", lang)
+                            format!("language-{lang}")
                         } else {
                             String::new()
                         };
@@ -999,7 +999,7 @@ impl<'a, 'tcx> TagIterator<'a, 'tcx> {
 
         if let Some((_, c)) = self.inner.next() {
             if c != '=' {
-                self.emit_error(format!("expected `=`, found `{}`", c));
+                self.emit_error(format!("expected `=`, found `{c}`"));
                 return None;
             }
         } else {
diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs
index 7b814701a73..5ceb1fc988d 100644
--- a/src/librustdoc/html/render/context.rs
+++ b/src/librustdoc/html/render/context.rs
@@ -193,14 +193,12 @@ impl<'tcx> Context<'tcx> {
         if it.is_stripped()
             && let Some(def_id) = it.def_id()
             && def_id.is_local()
+            && (self.info.is_inside_inlined_module
+                || self.shared.cache.inlined_items.contains(&def_id))
         {
-            if self.info.is_inside_inlined_module
-                || self.shared.cache.inlined_items.contains(&def_id)
-            {
-                // For now we're forced to generate a redirect page for stripped items until
-                // `record_extern_fqn` correctly points to external items.
-                render_redirect_pages = true;
-            }
+            // For now we're forced to generate a redirect page for stripped items until
+            // `record_extern_fqn` correctly points to external items.
+            render_redirect_pages = true;
         }
         let mut title = String::new();
         if !is_module {
@@ -254,40 +252,36 @@ impl<'tcx> Context<'tcx> {
                 &self.shared.style_files,
             )
         } else {
-            if let Some(&(ref names, ty)) = self.cache().paths.get(&it.item_id.expect_def_id()) {
-                if self.current.len() + 1 != names.len()
-                    || self.current.iter().zip(names.iter()).any(|(a, b)| a != b)
-                {
-                    // We checked that the redirection isn't pointing to the current file,
-                    // preventing an infinite redirection loop in the generated
-                    // documentation.
-
-                    let path = fmt::from_fn(|f| {
-                        for name in &names[..names.len() - 1] {
-                            write!(f, "{name}/")?;
-                        }
-                        write!(f, "{}", print_item_path(ty, names.last().unwrap().as_str()))
-                    });
-                    match self.shared.redirections {
-                        Some(ref redirections) => {
-                            let mut current_path = String::new();
-                            for name in &self.current {
-                                current_path.push_str(name.as_str());
-                                current_path.push('/');
-                            }
-                            let _ = write!(
-                                current_path,
-                                "{}",
-                                print_item_path(ty, names.last().unwrap().as_str())
-                            );
-                            redirections.borrow_mut().insert(current_path, path.to_string());
-                        }
-                        None => {
-                            return layout::redirect(&format!(
-                                "{root}{path}",
-                                root = self.root_path()
-                            ));
+            if let Some(&(ref names, ty)) = self.cache().paths.get(&it.item_id.expect_def_id())
+                && (self.current.len() + 1 != names.len()
+                    || self.current.iter().zip(names.iter()).any(|(a, b)| a != b))
+            {
+                // We checked that the redirection isn't pointing to the current file,
+                // preventing an infinite redirection loop in the generated
+                // documentation.
+
+                let path = fmt::from_fn(|f| {
+                    for name in &names[..names.len() - 1] {
+                        write!(f, "{name}/")?;
+                    }
+                    write!(f, "{}", print_item_path(ty, names.last().unwrap().as_str()))
+                });
+                match self.shared.redirections {
+                    Some(ref redirections) => {
+                        let mut current_path = String::new();
+                        for name in &self.current {
+                            current_path.push_str(name.as_str());
+                            current_path.push('/');
                         }
+                        let _ = write!(
+                            current_path,
+                            "{}",
+                            print_item_path(ty, names.last().unwrap().as_str())
+                        );
+                        redirections.borrow_mut().insert(current_path, path.to_string());
+                    }
+                    None => {
+                        return layout::redirect(&format!("{root}{path}", root = self.root_path()));
                     }
                 }
             }
@@ -762,11 +756,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
         // Flush pending errors.
         self.shared.fs.close();
         let nb_errors = self.shared.errors.iter().map(|err| self.tcx().dcx().err(err)).count();
-        if nb_errors > 0 {
-            Err(Error::new(io::Error::new(io::ErrorKind::Other, "I/O error"), ""))
-        } else {
-            Ok(())
-        }
+        if nb_errors > 0 { Err(Error::new(io::Error::other("I/O error"), "")) } else { Ok(()) }
     }
 
     fn mod_item_in(&mut self, item: &clean::Item) -> Result<(), Error> {
@@ -842,7 +832,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
             self.info.render_redirect_pages = item.is_stripped();
         }
 
-        let buf = self.render_item(&item, false);
+        let buf = self.render_item(item, false);
         // buf will be empty if the item is stripped and there is no redirect for it
         if !buf.is_empty() {
             let name = item.name.as_ref().unwrap();
@@ -853,7 +843,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
             self.shared.fs.write(joint_dst, buf)?;
 
             if !self.info.render_redirect_pages {
-                self.shared.all.borrow_mut().append(full_path(self, &item), &item_type);
+                self.shared.all.borrow_mut().append(full_path(self, item), &item_type);
             }
             // If the item is a macro, redirect from the old macro URL (with !)
             // to the new one (without).
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 2cfc9af39e4..872dbbcd19e 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -1483,10 +1483,10 @@ fn render_deref_methods(
             }
         }
         render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs);
-    } else if let Some(prim) = target.primitive_type() {
-        if let Some(&did) = cache.primitive_locations.get(&prim) {
-            render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs);
-        }
+    } else if let Some(prim) = target.primitive_type()
+        && let Some(&did) = cache.primitive_locations.get(&prim)
+    {
+        render_assoc_items_inner(&mut w, cx, container_item, did, what, derefs);
     }
 }
 
@@ -2058,21 +2058,20 @@ fn render_impl(
         // default items which weren't overridden in the implementation block.
         // We don't emit documentation for default items if they appear in the
         // Implementations on Foreign Types or Implementors sections.
-        if rendering_params.show_default_items {
-            if let Some(t) = trait_
-                && !impl_.is_negative_trait_impl()
-            {
-                render_default_items(
-                    &mut default_impl_items,
-                    &mut impl_items,
-                    cx,
-                    t,
-                    impl_,
-                    &i.impl_item,
-                    render_mode,
-                    rendering_params,
-                )?;
-            }
+        if rendering_params.show_default_items
+            && let Some(t) = trait_
+            && !impl_.is_negative_trait_impl()
+        {
+            render_default_items(
+                &mut default_impl_items,
+                &mut impl_items,
+                cx,
+                t,
+                impl_,
+                &i.impl_item,
+                render_mode,
+                rendering_params,
+            )?;
         }
         if render_mode == RenderMode::Normal {
             let toggled = !(impl_items.is_empty() && default_impl_items.is_empty());
@@ -2570,7 +2569,7 @@ fn collect_paths_for_type(first_ty: &clean::Type, cache: &Cache) -> Vec<String>
         match ty {
             clean::Type::Path { path } => process_path(path.def_id()),
             clean::Type::Tuple(tys) => {
-                work.extend(tys.into_iter());
+                work.extend(tys.iter());
             }
             clean::Type::Slice(ty) => {
                 work.push_back(ty);
diff --git a/src/librustdoc/html/render/ordered_json.rs b/src/librustdoc/html/render/ordered_json.rs
index d1dddfebc83..be51dad1c2b 100644
--- a/src/librustdoc/html/render/ordered_json.rs
+++ b/src/librustdoc/html/render/ordered_json.rs
@@ -25,7 +25,7 @@ impl OrderedJson {
             .into_iter()
             .sorted_unstable_by(|a, b| a.borrow().cmp(b.borrow()))
             .format_with(",", |item, f| f(item.borrow()));
-        Self(format!("[{}]", items))
+        Self(format!("[{items}]"))
     }
 
     pub(crate) fn array_unsorted<T: Borrow<Self>, I: IntoIterator<Item = T>>(items: I) -> Self {
diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs
index 5fbda4797cc..02ee34aaac6 100644
--- a/src/librustdoc/html/render/print_item.rs
+++ b/src/librustdoc/html/render/print_item.rs
@@ -1451,7 +1451,7 @@ item_template!(
 
 impl<'a, 'cx: 'a> ItemUnion<'a, 'cx> {
     fn render_union(&self) -> impl Display {
-        render_union(self.it, Some(&self.generics), &self.fields, self.cx)
+        render_union(self.it, Some(self.generics), self.fields, self.cx)
     }
 
     fn document_field(&self, field: &'a clean::Item) -> impl Display {
@@ -1982,16 +1982,14 @@ fn item_constant(
                 w.write_str(";")?;
             }
 
-            if !is_literal {
-                if let Some(value) = &value {
-                    let value_lowercase = value.to_lowercase();
-                    let expr_lowercase = expr.to_lowercase();
+            if !is_literal && let Some(value) = &value {
+                let value_lowercase = value.to_lowercase();
+                let expr_lowercase = expr.to_lowercase();
 
-                    if value_lowercase != expr_lowercase
-                        && value_lowercase.trim_end_matches("i32") != expr_lowercase
-                    {
-                        write!(w, " // {value}", value = Escape(value))?;
-                    }
+                if value_lowercase != expr_lowercase
+                    && value_lowercase.trim_end_matches("i32") != expr_lowercase
+                {
+                    write!(w, " // {value}", value = Escape(value))?;
                 }
             }
             Ok::<(), fmt::Error>(())
@@ -2071,41 +2069,39 @@ fn item_fields(
                 _ => None,
             })
             .peekable();
-        if let None | Some(CtorKind::Fn) = ctor_kind {
-            if fields.peek().is_some() {
-                let title = format!(
-                    "{}{}",
-                    if ctor_kind.is_none() { "Fields" } else { "Tuple Fields" },
-                    document_non_exhaustive_header(it),
-                );
+        if let None | Some(CtorKind::Fn) = ctor_kind
+            && fields.peek().is_some()
+        {
+            let title = format!(
+                "{}{}",
+                if ctor_kind.is_none() { "Fields" } else { "Tuple Fields" },
+                document_non_exhaustive_header(it),
+            );
+            write!(
+                w,
+                "{}",
+                write_section_heading(
+                    &title,
+                    "fields",
+                    Some("fields"),
+                    document_non_exhaustive(it)
+                )
+            )?;
+            for (index, (field, ty)) in fields.enumerate() {
+                let field_name =
+                    field.name.map_or_else(|| index.to_string(), |sym| sym.as_str().to_string());
+                let id = cx.derive_id(format!("{typ}.{field_name}", typ = ItemType::StructField));
                 write!(
                     w,
-                    "{}",
-                    write_section_heading(
-                        &title,
-                        "fields",
-                        Some("fields"),
-                        document_non_exhaustive(it)
-                    )
+                    "<span id=\"{id}\" class=\"{item_type} section-header\">\
+                        <a href=\"#{id}\" class=\"anchor field\">§</a>\
+                        <code>{field_name}: {ty}</code>\
+                    </span>\
+                    {doc}",
+                    item_type = ItemType::StructField,
+                    ty = ty.print(cx),
+                    doc = document(cx, field, Some(it), HeadingOffset::H3),
                 )?;
-                for (index, (field, ty)) in fields.enumerate() {
-                    let field_name = field
-                        .name
-                        .map_or_else(|| index.to_string(), |sym| sym.as_str().to_string());
-                    let id =
-                        cx.derive_id(format!("{typ}.{field_name}", typ = ItemType::StructField));
-                    write!(
-                        w,
-                        "<span id=\"{id}\" class=\"{item_type} section-header\">\
-                            <a href=\"#{id}\" class=\"anchor field\">§</a>\
-                            <code>{field_name}: {ty}</code>\
-                        </span>\
-                        {doc}",
-                        item_type = ItemType::StructField,
-                        ty = ty.print(cx),
-                        doc = document(cx, field, Some(it), HeadingOffset::H3),
-                    )?;
-                }
             }
         }
         Ok(())
diff --git a/src/librustdoc/html/render/sidebar.rs b/src/librustdoc/html/render/sidebar.rs
index 91540e06e33..b9f5ada417c 100644
--- a/src/librustdoc/html/render/sidebar.rs
+++ b/src/librustdoc/html/render/sidebar.rs
@@ -541,7 +541,7 @@ fn sidebar_deref_methods<'a>(
                 .iter()
                 .filter(|i| {
                     i.inner_impl().trait_.is_none()
-                        && real_target.is_doc_subtype_of(&i.inner_impl().for_, &c)
+                        && real_target.is_doc_subtype_of(&i.inner_impl().for_, c)
                 })
                 .flat_map(|i| get_methods(i.inner_impl(), true, used_links, deref_mut, cx.tcx()))
                 .collect::<Vec<_>>();
diff --git a/src/librustdoc/html/render/sorted_template.rs b/src/librustdoc/html/render/sorted_template.rs
index a7b954ab70b..659c5e6093b 100644
--- a/src/librustdoc/html/render/sorted_template.rs
+++ b/src/librustdoc/html/render/sorted_template.rs
@@ -63,7 +63,8 @@ impl<F: FileFormat> fmt::Display for SortedTemplate<F> {
         for (p, fragment) in self.fragments.iter().with_position() {
             let mut f = DeltaWriter { inner: &mut f, delta: 0 };
             let sep = if matches!(p, Position::First | Position::Only) { "" } else { F::SEPARATOR };
-            write!(f, "{}{}", sep, fragment)?;
+            f.write_str(sep)?;
+            f.write_str(fragment)?;
             fragment_lengths.push(f.delta);
         }
         let offset = Offset { start: self.before.len(), fragment_lengths };
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index 0a84d8caa30..08bc0bb1950 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -50,7 +50,7 @@ impl JsonRenderer<'_> {
         let span = item.span(self.tcx);
         let visibility = item.visibility(self.tcx);
         let clean::ItemInner { name, item_id, .. } = *item.inner;
-        let id = self.id_from_item(&item);
+        let id = self.id_from_item(item);
         let inner = match item.kind {
             clean::KeywordItem => return None,
             clean::StrippedItem(ref inner) => {
@@ -86,14 +86,14 @@ impl JsonRenderer<'_> {
         items
             .iter()
             .filter(|i| !i.is_stripped() && !i.is_keyword())
-            .map(|i| self.id_from_item(&i))
+            .map(|i| self.id_from_item(i))
             .collect()
     }
 
     fn ids_keeping_stripped(&self, items: &[clean::Item]) -> Vec<Option<Id>> {
         items
             .iter()
-            .map(|i| (!i.is_stripped() && !i.is_keyword()).then(|| self.id_from_item(&i)))
+            .map(|i| (!i.is_stripped() && !i.is_keyword()).then(|| self.id_from_item(i)))
             .collect()
     }
 }
@@ -358,12 +358,12 @@ impl FromClean<clean::Struct> for Struct {
         let clean::Struct { ctor_kind, generics, fields } = struct_;
 
         let kind = match ctor_kind {
-            Some(CtorKind::Fn) => StructKind::Tuple(renderer.ids_keeping_stripped(&fields)),
+            Some(CtorKind::Fn) => StructKind::Tuple(renderer.ids_keeping_stripped(fields)),
             Some(CtorKind::Const) => {
                 assert!(fields.is_empty());
                 StructKind::Unit
             }
-            None => StructKind::Plain { fields: renderer.ids(&fields), has_stripped_fields },
+            None => StructKind::Plain { fields: renderer.ids(fields), has_stripped_fields },
         };
 
         Struct {
@@ -381,7 +381,7 @@ impl FromClean<clean::Union> for Union {
         Union {
             generics: generics.into_json(renderer),
             has_stripped_fields,
-            fields: renderer.ids(&fields),
+            fields: renderer.ids(fields),
             impls: Vec::new(), // Added in JsonRenderer::item
         }
     }
@@ -659,7 +659,7 @@ impl FromClean<clean::FnDecl> for FunctionSignature {
         let clean::FnDecl { inputs, output, c_variadic } = decl;
         FunctionSignature {
             inputs: inputs
-                .into_iter()
+                .iter()
                 .map(|param| {
                     // `_` is the most sensible name for missing param names.
                     let name = param.name.unwrap_or(kw::Underscore).to_string();
@@ -684,7 +684,7 @@ impl FromClean<clean::Trait> for Trait {
             is_auto,
             is_unsafe,
             is_dyn_compatible,
-            items: renderer.ids(&items),
+            items: renderer.ids(items),
             generics: generics.into_json(renderer),
             bounds: bounds.into_json(renderer),
             implementations: Vec::new(), // Added in JsonRenderer::item
@@ -727,7 +727,7 @@ impl FromClean<clean::Impl> for Impl {
                 .collect(),
             trait_: trait_.into_json(renderer),
             for_: for_.into_json(renderer),
-            items: renderer.ids(&items),
+            items: renderer.ids(items),
             is_negative,
             is_synthetic,
             blanket_impl: blanket_impl.map(|x| x.into_json(renderer)),
@@ -770,7 +770,7 @@ impl FromClean<clean::Variant> for Variant {
 
         let kind = match &variant.kind {
             CLike => VariantKind::Plain,
-            Tuple(fields) => VariantKind::Tuple(renderer.ids_keeping_stripped(&fields)),
+            Tuple(fields) => VariantKind::Tuple(renderer.ids_keeping_stripped(fields)),
             Struct(s) => VariantKind::Struct {
                 has_stripped_fields: s.has_stripped_entries(),
                 fields: renderer.ids(&s.fields),
diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs
index 600a4b429f3..760e48baffa 100644
--- a/src/librustdoc/json/mod.rs
+++ b/src/librustdoc/json/mod.rs
@@ -133,7 +133,7 @@ fn target(sess: &rustc_session::Session) -> types::Target {
     let feature_stability: FxHashMap<&str, Stability> = sess
         .target
         .rust_target_features()
-        .into_iter()
+        .iter()
         .copied()
         .map(|(name, stability, _)| (name, stability))
         .collect();
@@ -143,7 +143,7 @@ fn target(sess: &rustc_session::Session) -> types::Target {
         target_features: sess
             .target
             .rust_target_features()
-            .into_iter()
+            .iter()
             .copied()
             .filter(|(_, stability, _)| {
                 // Describe only target features which the user can toggle
@@ -157,7 +157,7 @@ fn target(sess: &rustc_session::Session) -> types::Target {
                         _ => None,
                     },
                     implies_features: implied_features
-                        .into_iter()
+                        .iter()
                         .copied()
                         .filter(|name| {
                             // Imply only target features which the user can toggle
diff --git a/src/librustdoc/passes/collect_intra_doc_links.rs b/src/librustdoc/passes/collect_intra_doc_links.rs
index ca6f67eb6df..5a9aa2a94c8 100644
--- a/src/librustdoc/passes/collect_intra_doc_links.rs
+++ b/src/librustdoc/passes/collect_intra_doc_links.rs
@@ -274,7 +274,7 @@ impl From<DiagnosticInfo<'_>> for OwnedDiagnosticInfo {
 }
 
 impl OwnedDiagnosticInfo {
-    pub(crate) fn into_info(&self) -> DiagnosticInfo<'_> {
+    pub(crate) fn as_info(&self) -> DiagnosticInfo<'_> {
         DiagnosticInfo {
             item: &self.item,
             ori_link: &self.ori_link,
@@ -1177,7 +1177,7 @@ impl LinkCollector<'_, '_> {
                     // Primitive types are always valid.
                     Res::Primitive(_) => true,
                 });
-                let diag_info = info.diag_info.into_info();
+                let diag_info = info.diag_info.as_info();
                 match info.resolved.len() {
                     1 => {
                         let (res, fragment) = info.resolved.pop().unwrap();
@@ -1243,17 +1243,16 @@ impl LinkCollector<'_, '_> {
             disambiguator,
             None | Some(Disambiguator::Namespace(Namespace::TypeNS) | Disambiguator::Primitive)
         ) && !matches!(res, Res::Primitive(_))
+            && let Some(prim) = resolve_primitive(path_str, TypeNS)
         {
-            if let Some(prim) = resolve_primitive(path_str, TypeNS) {
-                // `prim@char`
-                if matches!(disambiguator, Some(Disambiguator::Primitive)) {
-                    res = prim;
-                } else {
-                    // `[char]` when a `char` module is in scope
-                    let candidates = &[(res, res.def_id(self.cx.tcx)), (prim, None)];
-                    ambiguity_error(self.cx, &diag_info, path_str, candidates, true);
-                    return None;
-                }
+            // `prim@char`
+            if matches!(disambiguator, Some(Disambiguator::Primitive)) {
+                res = prim;
+            } else {
+                // `[char]` when a `char` module is in scope
+                let candidates = &[(res, res.def_id(self.cx.tcx)), (prim, None)];
+                ambiguity_error(self.cx, &diag_info, path_str, candidates, true);
+                return None;
             }
         }
 
@@ -2233,7 +2232,7 @@ fn ambiguity_error(
     // proc macro can exist in multiple namespaces at once, so we need to compare `DefIds`
     //  to remove the candidate in the fn namespace.
     let mut possible_proc_macro_id = None;
-    let is_proc_macro_crate = cx.tcx.crate_types() == &[CrateType::ProcMacro];
+    let is_proc_macro_crate = cx.tcx.crate_types() == [CrateType::ProcMacro];
     let mut kinds = candidates
         .iter()
         .map(|(res, def_id)| {
diff --git a/src/librustdoc/passes/lint/redundant_explicit_links.rs b/src/librustdoc/passes/lint/redundant_explicit_links.rs
index 5757b6a9740..e69cf87f957 100644
--- a/src/librustdoc/passes/lint/redundant_explicit_links.rs
+++ b/src/librustdoc/passes/lint/redundant_explicit_links.rs
@@ -93,14 +93,14 @@ fn check_redundant_explicit_link<'md>(
         if let Event::Start(Tag::Link { link_type, dest_url, .. }) = event {
             let link_data = collect_link_data(&mut offset_iter);
 
-            if let Some(resolvable_link) = link_data.resolvable_link.as_ref() {
-                if &link_data.display_link.replace('`', "") != resolvable_link {
-                    // Skips if display link does not match to actual
-                    // resolvable link, usually happens if display link
-                    // has several segments, e.g.
-                    // [this is just an `Option`](Option)
-                    continue;
-                }
+            if let Some(resolvable_link) = link_data.resolvable_link.as_ref()
+                && &link_data.display_link.replace('`', "") != resolvable_link
+            {
+                // Skips if display link does not match to actual
+                // resolvable link, usually happens if display link
+                // has several segments, e.g.
+                // [this is just an `Option`](Option)
+                continue;
             }
 
             let explicit_link = dest_url.to_string();
diff --git a/src/librustdoc/passes/strip_aliased_non_local.rs b/src/librustdoc/passes/strip_aliased_non_local.rs
index b53e3b4e3d7..bb13308e6c2 100644
--- a/src/librustdoc/passes/strip_aliased_non_local.rs
+++ b/src/librustdoc/passes/strip_aliased_non_local.rs
@@ -47,13 +47,11 @@ impl DocFolder for NonLocalStripper<'_> {
         // FIXME(#125009): Not-local should probably consider same Cargo workspace
         if let Some(def_id) = i.def_id()
             && !def_id.is_local()
-        {
-            if i.is_doc_hidden()
+            && (i.is_doc_hidden()
                 // Default to *not* stripping items with inherited visibility.
-                || i.visibility(self.tcx).is_some_and(|viz| viz != Visibility::Public)
-            {
-                return Some(strip_item(i));
-            }
+                || i.visibility(self.tcx).is_some_and(|viz| viz != Visibility::Public))
+        {
+            return Some(strip_item(i));
         }
 
         Some(self.fold_item_recur(i))
diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs
index 33da1a25db1..c83070aaba7 100644
--- a/src/tools/compiletest/src/common.rs
+++ b/src/tools/compiletest/src/common.rs
@@ -88,11 +88,37 @@ string_enum! {
     }
 }
 
+string_enum! {
+    #[derive(Clone, Copy, PartialEq, Debug, Hash)]
+    pub enum RunResult {
+        Pass => "run-pass",
+        Fail => "run-fail",
+        Crash => "run-crash",
+    }
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
+pub enum RunFailMode {
+    /// Running the program must make it exit with a regular failure exit code
+    /// in the range `1..=127`. If the program is terminated by e.g. a signal
+    /// the test will fail.
+    Fail,
+    /// Running the program must result in a crash, e.g. by `SIGABRT` or
+    /// `SIGSEGV` on Unix or on Windows by having an appropriate NTSTATUS high
+    /// bit in the exit code.
+    Crash,
+    /// Running the program must either fail or crash. Useful for e.g. sanitizer
+    /// tests since some sanitizer implementations exit the process with code 1
+    /// to in the face of memory errors while others abort (crash) the process
+    /// in the face of memory errors.
+    FailOrCrash,
+}
+
 #[derive(Copy, Clone, Debug, PartialEq, PartialOrd)]
 pub enum FailMode {
     Check,
     Build,
-    Run,
+    Run(RunFailMode),
 }
 
 string_enum! {
diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs
index 93133ea0bfd..513716357f4 100644
--- a/src/tools/compiletest/src/directives.rs
+++ b/src/tools/compiletest/src/directives.rs
@@ -9,7 +9,7 @@ use camino::{Utf8Path, Utf8PathBuf};
 use semver::Version;
 use tracing::*;
 
-use crate::common::{Config, Debugger, FailMode, PassMode, TestMode};
+use crate::common::{Config, Debugger, FailMode, PassMode, RunFailMode, TestMode};
 use crate::debuggers::{extract_cdb_version, extract_gdb_version};
 use crate::directives::auxiliary::{AuxProps, parse_and_update_aux};
 use crate::directives::needs::CachedNeedsConditions;
@@ -654,7 +654,13 @@ impl TestProps {
             Some(FailMode::Build)
         } else if config.parse_name_directive(ln, "run-fail") {
             check_ui("run");
-            Some(FailMode::Run)
+            Some(FailMode::Run(RunFailMode::Fail))
+        } else if config.parse_name_directive(ln, "run-crash") {
+            check_ui("run");
+            Some(FailMode::Run(RunFailMode::Crash))
+        } else if config.parse_name_directive(ln, "run-fail-or-crash") {
+            check_ui("run");
+            Some(FailMode::Run(RunFailMode::FailOrCrash))
         } else {
             None
         };
@@ -1007,7 +1013,9 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "regex-error-pattern",
     "remap-src-base",
     "revisions",
+    "run-crash",
     "run-fail",
+    "run-fail-or-crash",
     "run-flags",
     "run-pass",
     "run-rustfix",
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index cb8f593c9df..f66d4f98f1f 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -16,8 +16,8 @@ use regex::{Captures, Regex};
 use tracing::*;
 
 use crate::common::{
-    CompareMode, Config, Debugger, FailMode, PassMode, TestMode, TestPaths, TestSuite,
-    UI_EXTENSIONS, UI_FIXED, UI_RUN_STDERR, UI_RUN_STDOUT, UI_STDERR, UI_STDOUT, UI_SVG,
+    CompareMode, Config, Debugger, FailMode, PassMode, RunFailMode, RunResult, TestMode, TestPaths,
+    TestSuite, UI_EXTENSIONS, UI_FIXED, UI_RUN_STDERR, UI_RUN_STDOUT, UI_STDERR, UI_STDOUT, UI_SVG,
     UI_WINDOWS_SVG, expected_output_path, incremental_dir, output_base_dir, output_base_name,
     output_testname_unique,
 };
@@ -282,7 +282,8 @@ impl<'test> TestCx<'test> {
     fn should_run(&self, pm: Option<PassMode>) -> WillExecute {
         let test_should_run = match self.config.mode {
             TestMode::Ui
-                if pm == Some(PassMode::Run) || self.props.fail_mode == Some(FailMode::Run) =>
+                if pm == Some(PassMode::Run)
+                    || matches!(self.props.fail_mode, Some(FailMode::Run(_))) =>
             {
                 true
             }
diff --git a/src/tools/compiletest/src/runtest/ui.rs b/src/tools/compiletest/src/runtest/ui.rs
index f6bc85cd051..0507c2600ae 100644
--- a/src/tools/compiletest/src/runtest/ui.rs
+++ b/src/tools/compiletest/src/runtest/ui.rs
@@ -6,8 +6,8 @@ use rustfix::{Filter, apply_suggestions, get_suggestions_from_json};
 use tracing::debug;
 
 use super::{
-    AllowUnused, Emit, FailMode, LinkToAux, PassMode, TargetLocation, TestCx, TestOutput,
-    Truncated, UI_FIXED, WillExecute,
+    AllowUnused, Emit, FailMode, LinkToAux, PassMode, RunFailMode, RunResult, TargetLocation,
+    TestCx, TestOutput, Truncated, UI_FIXED, WillExecute,
 };
 use crate::json;
 
@@ -140,12 +140,53 @@ impl TestCx<'_> {
                     &proc_res,
                 );
             }
+            let code = proc_res.status.code();
+            let run_result = if proc_res.status.success() {
+                RunResult::Pass
+            } else if code.is_some_and(|c| c >= 1 && c <= 127) {
+                RunResult::Fail
+            } else {
+                RunResult::Crash
+            };
+            // Help users understand why the test failed by including the actual
+            // exit code and actual run result in the failure message.
+            let pass_hint = format!("code={code:?} so test would pass with `{run_result}`");
             if self.should_run_successfully(pm) {
-                if !proc_res.status.success() {
-                    self.fatal_proc_rec("test run failed!", &proc_res);
+                if run_result != RunResult::Pass {
+                    self.fatal_proc_rec(
+                        &format!("test did not exit with success! {pass_hint}"),
+                        &proc_res,
+                    );
+                }
+            } else if self.props.fail_mode == Some(FailMode::Run(RunFailMode::Fail)) {
+                // If the test is marked as `run-fail` but do not support
+                // unwinding we allow it to crash, since a panic will trigger an
+                // abort (crash) instead of unwind (exit with code 101).
+                let crash_ok = !self.config.can_unwind();
+                if run_result != RunResult::Fail && !(crash_ok && run_result == RunResult::Crash) {
+                    let err = if crash_ok {
+                        format!(
+                            "test did not exit with failure or crash (`{}` can't unwind)! {pass_hint}",
+                            self.config.target
+                        )
+                    } else {
+                        format!("test did not exit with failure! {pass_hint}")
+                    };
+                    self.fatal_proc_rec(&err, &proc_res);
                 }
-            } else if proc_res.status.success() {
-                self.fatal_proc_rec("test run succeeded!", &proc_res);
+            } else if self.props.fail_mode == Some(FailMode::Run(RunFailMode::Crash)) {
+                if run_result != RunResult::Crash {
+                    self.fatal_proc_rec(&format!("test did not crash! {pass_hint}"), &proc_res);
+                }
+            } else if self.props.fail_mode == Some(FailMode::Run(RunFailMode::FailOrCrash)) {
+                if run_result != RunResult::Fail && run_result != RunResult::Crash {
+                    self.fatal_proc_rec(
+                        &format!("test did not exit with failure or crash! {pass_hint}"),
+                        &proc_res,
+                    );
+                }
+            } else {
+                unreachable!("run_ui_test() must not be called if the test should not run");
             }
 
             self.get_output(&proc_res)
diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version
index 601acc00bd6..d734ec333a5 100644
--- a/src/tools/miri/rust-version
+++ b/src/tools/miri/rust-version
@@ -1 +1 @@
-ebd8557637b33cc09b6ee8273f3154d5d3af6a15
+6707bf0f59485cf054ac1095725df43220e4be20
diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.rs
index 047fe07df14..5778765589d 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.rs
@@ -1,4 +1,6 @@
 //@ignore-target: windows # No pthreads on Windows
+//@ normalize-stderr-test: "(\n)ALLOC \(.*\) \{\n(.*\n)*\}(\n)" -> "${1}ALLOC DUMP${3}"
+//@ normalize-stderr-test: "\[0x[0-9a-z]..0x[0-9a-z]\]" -> "[0xX..0xY]"
 
 /// Test that destroying a pthread_cond twice fails, even without a check for number validity
 
@@ -15,6 +17,6 @@ fn main() {
         libc::pthread_cond_destroy(cond.as_mut_ptr());
 
         libc::pthread_cond_destroy(cond.as_mut_ptr());
-        //~^ ERROR: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+        //~^ ERROR: /Undefined Behavior: reading memory .*, but memory is uninitialized/
     }
 }
diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.stderr
index 7abdfa87f75..6156070cf95 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.stderr
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0xX..0xY], but memory is uninitialized at [0xX..0xY], and this operation requires initialized memory
   --> tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.rs:LL:CC
    |
 LL |         libc::pthread_cond_destroy(cond.as_mut_ptr());
@@ -9,6 +9,9 @@ LL |         libc::pthread_cond_destroy(cond.as_mut_ptr());
    = note: BACKTRACE:
    = note: inside `main` at tests/fail-dep/concurrency/libc_pthread_cond_double_destroy.rs:LL:CC
 
+Uninitialized memory occurred at ALLOC[0xX..0xY], in this allocation:
+ALLOC DUMP
+
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
 error: aborting due to 1 previous error
diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.rs
index 90e33d58673..91169660491 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.rs
@@ -1,5 +1,7 @@
 //@ignore-target: windows # No pthreads on Windows
 //@ignore-target: apple # Our macOS condattr don't have any fields so we do not notice this.
+//@ normalize-stderr-test: "(\n)ALLOC \(.*\) \{\n(.*\n)*\}(\n)" -> "${1}ALLOC DUMP${3}"
+//@ normalize-stderr-test: "\[0x[0-9a-z]..0x[0-9a-z]\]" -> "[0xX..0xY]"
 
 /// Test that destroying a pthread_condattr twice fails, even without a check for number validity
 
@@ -13,6 +15,6 @@ fn main() {
         libc::pthread_condattr_destroy(attr.as_mut_ptr());
 
         libc::pthread_condattr_destroy(attr.as_mut_ptr());
-        //~^ ERROR: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+        //~^ ERROR: /Undefined Behavior: reading memory .*, but memory is uninitialized/
     }
 }
diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.stderr
index 28a66253ae8..da64970ff2e 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.stderr
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0xX..0xY], but memory is uninitialized at [0xX..0xY], and this operation requires initialized memory
   --> tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.rs:LL:CC
    |
 LL |         libc::pthread_condattr_destroy(attr.as_mut_ptr());
@@ -9,6 +9,9 @@ LL |         libc::pthread_condattr_destroy(attr.as_mut_ptr());
    = note: BACKTRACE:
    = note: inside `main` at tests/fail-dep/concurrency/libc_pthread_condattr_double_destroy.rs:LL:CC
 
+Uninitialized memory occurred at ALLOC[0xX..0xY], in this allocation:
+ALLOC DUMP
+
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
 error: aborting due to 1 previous error
diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.rs
index 1792c227e13..f04fe8be6b3 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.rs
@@ -1,4 +1,6 @@
 //@ignore-target: windows # No pthreads on Windows
+//@ normalize-stderr-test: "(\n)ALLOC \(.*\) \{\n(.*\n)*\}(\n)" -> "${1}ALLOC DUMP${3}"
+//@ normalize-stderr-test: "\[0x[0-9a-z]..0x[0-9a-z]\]" -> "[0xX..0xY]"
 
 /// Test that destroying a pthread_mutex twice fails, even without a check for number validity
 
@@ -16,6 +18,6 @@ fn main() {
         libc::pthread_mutex_destroy(mutex.as_mut_ptr());
 
         libc::pthread_mutex_destroy(mutex.as_mut_ptr());
-        //~^ ERROR: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+        //~^ ERROR: /Undefined Behavior: reading memory .*, but memory is uninitialized/
     }
 }
diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.stderr
index e7a6dee0203..05db823b252 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.stderr
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0xX..0xY], but memory is uninitialized at [0xX..0xY], and this operation requires initialized memory
   --> tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.rs:LL:CC
    |
 LL |         libc::pthread_mutex_destroy(mutex.as_mut_ptr());
@@ -9,6 +9,9 @@ LL |         libc::pthread_mutex_destroy(mutex.as_mut_ptr());
    = note: BACKTRACE:
    = note: inside `main` at tests/fail-dep/concurrency/libc_pthread_mutex_double_destroy.rs:LL:CC
 
+Uninitialized memory occurred at ALLOC[0xX..0xY], in this allocation:
+ALLOC DUMP
+
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
 error: aborting due to 1 previous error
diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.rs
index 3711c1f8dc1..d9daf5259bb 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.rs
@@ -1,4 +1,6 @@
 //@ignore-target: windows # No pthreads on Windows
+//@ normalize-stderr-test: "(\n)ALLOC \(.*\) \{\n(.*\n)*\}(\n)" -> "${1}ALLOC DUMP${3}"
+//@ normalize-stderr-test: "\[0x[0-9a-z]..0x[0-9a-z]\]" -> "[0xX..0xY]"
 
 /// Test that destroying a pthread_mutexattr twice fails, even without a check for number validity
 
@@ -12,6 +14,6 @@ fn main() {
         libc::pthread_mutexattr_destroy(attr.as_mut_ptr());
 
         libc::pthread_mutexattr_destroy(attr.as_mut_ptr());
-        //~^ ERROR: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+        //~^ ERROR: /Undefined Behavior: reading memory .*, but memory is uninitialized/
     }
 }
diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.stderr
index 0c9ee71de45..ee3883de36b 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.stderr
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0xX..0xY], but memory is uninitialized at [0xX..0xY], and this operation requires initialized memory
   --> tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.rs:LL:CC
    |
 LL |         libc::pthread_mutexattr_destroy(attr.as_mut_ptr());
@@ -9,6 +9,9 @@ LL |         libc::pthread_mutexattr_destroy(attr.as_mut_ptr());
    = note: BACKTRACE:
    = note: inside `main` at tests/fail-dep/concurrency/libc_pthread_mutexattr_double_destroy.rs:LL:CC
 
+Uninitialized memory occurred at ALLOC[0xX..0xY], in this allocation:
+ALLOC DUMP
+
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
 error: aborting due to 1 previous error
diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.rs b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.rs
index 6a31e972e68..720ba71d238 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.rs
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.rs
@@ -1,4 +1,6 @@
 //@ignore-target: windows # No pthreads on Windows
+//@ normalize-stderr-test: "(\n)ALLOC \(.*\) \{\n(.*\n)*\}(\n)" -> "${1}ALLOC DUMP${3}"
+//@ normalize-stderr-test: "\[0x[0-9a-z]..0x[0-9a-z]\]" -> "[0xX..0xY]"
 
 /// Test that destroying a pthread_rwlock twice fails, even without a check for number validity
 
@@ -9,6 +11,6 @@ fn main() {
         libc::pthread_rwlock_destroy(&mut lock);
 
         libc::pthread_rwlock_destroy(&mut lock);
-        //~^ ERROR: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+        //~^ ERROR: /Undefined Behavior: reading memory .*, but memory is uninitialized/
     }
 }
diff --git a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.stderr b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.stderr
index 836f0d060bd..430398dc8fd 100644
--- a/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.stderr
+++ b/src/tools/miri/tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0xX..0xY], but memory is uninitialized at [0xX..0xY], and this operation requires initialized memory
   --> tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.rs:LL:CC
    |
 LL |         libc::pthread_rwlock_destroy(&mut lock);
@@ -9,6 +9,9 @@ LL |         libc::pthread_rwlock_destroy(&mut lock);
    = note: BACKTRACE:
    = note: inside `main` at tests/fail-dep/concurrency/libc_pthread_rwlock_double_destroy.rs:LL:CC
 
+Uninitialized memory occurred at ALLOC[0xX..0xY], in this allocation:
+ALLOC DUMP
+
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
 error: aborting due to 1 previous error
diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_after.stderr b/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_after.stderr
index 6a7d9a495f9..3252368ea6d 100644
--- a/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_after.stderr
+++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_after.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0x0..0x4], but memory is uninitialized at [0x0..0x4], and this operation requires initialized memory
   --> tests/fail/function_calls/arg_inplace_observe_after.rs:LL:CC
    |
 LL |             _observe = non_copy.0;
@@ -9,6 +9,11 @@ LL |             _observe = non_copy.0;
    = note: BACKTRACE:
    = note: inside `main` at tests/fail/function_calls/arg_inplace_observe_after.rs:LL:CC
 
+Uninitialized memory occurred at ALLOC[0x0..0x4], in this allocation:
+ALLOC (stack variable, size: 4, align: 4) {
+    __ __ __ __                                     │ ░░░░
+}
+
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
 error: aborting due to 1 previous error
diff --git a/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.none.stderr b/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.none.stderr
index 0fc634bb7fc..09a5b9a6496 100644
--- a/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.none.stderr
+++ b/src/tools/miri/tests/fail/function_calls/arg_inplace_observe_during.none.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0x0..0x4], but memory is uninitialized at [0x0..0x4], and this operation requires initialized memory
   --> tests/fail/function_calls/arg_inplace_observe_during.rs:LL:CC
    |
 LL |     unsafe { ptr.read() };
@@ -14,6 +14,11 @@ note: inside `main`
 LL |             Call(_unit = change_arg(Move(*ptr), ptr), ReturnTo(after_call), UnwindContinue())
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
+Uninitialized memory occurred at ALLOC[0x0..0x4], in this allocation:
+ALLOC (stack variable, size: 4, align: 4) {
+    __ __ __ __                                     │ ░░░░
+}
+
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
 error: aborting due to 1 previous error
diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_read.none.stderr b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_read.none.stderr
index 746ab2e59ca..24091547258 100644
--- a/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_read.none.stderr
+++ b/src/tools/miri/tests/fail/function_calls/return_pointer_aliasing_read.none.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0x0..0x4], but memory is uninitialized at [0x0..0x4], and this operation requires initialized memory
   --> tests/fail/function_calls/return_pointer_aliasing_read.rs:LL:CC
    |
 LL |     unsafe { ptr.read() };
@@ -14,6 +14,11 @@ note: inside `main`
 LL |             Call(*ptr = myfun(ptr), ReturnTo(after_call), UnwindContinue())
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
+Uninitialized memory occurred at ALLOC[0x0..0x4], in this allocation:
+ALLOC (stack variable, size: 4, align: 4) {
+    __ __ __ __                                     │ ░░░░
+}
+
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
 error: aborting due to 1 previous error
diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr b/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr
index 7747a75d1cf..93720ca7d27 100644
--- a/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr
+++ b/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr
@@ -3,7 +3,7 @@ thread 'main' panicked at tests/fail/function_calls/return_pointer_on_unwind.rs:
 explicit panic
 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
 note: in Miri, you may have to set `MIRIFLAGS=-Zmiri-env-forward=RUST_BACKTRACE` for the environment variable to have an effect
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0x0..0x4], but memory is uninitialized at [0x0..0x4], and this operation requires initialized memory
   --> tests/fail/function_calls/return_pointer_on_unwind.rs:LL:CC
    |
 LL |     dbg!(x.0);
@@ -15,6 +15,19 @@ LL |     dbg!(x.0);
    = note: inside `main` at RUSTLIB/std/src/macros.rs:LL:CC
    = note: this error originates in the macro `dbg` (in Nightly builds, run with -Z macro-backtrace for more info)
 
+Uninitialized memory occurred at ALLOC[0x0..0x4], in this allocation:
+ALLOC (stack variable, size: 132, align: 4) {
+    0x00 │ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ ░░░░░░░░░░░░░░░░
+    0x10 │ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ ░░░░░░░░░░░░░░░░
+    0x20 │ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ ░░░░░░░░░░░░░░░░
+    0x30 │ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ ░░░░░░░░░░░░░░░░
+    0x40 │ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ ░░░░░░░░░░░░░░░░
+    0x50 │ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ ░░░░░░░░░░░░░░░░
+    0x60 │ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ ░░░░░░░░░░░░░░░░
+    0x70 │ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ __ │ ░░░░░░░░░░░░░░░░
+    0x80 │ __ __ __ __                                     │ ░░░░
+}
+
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
 error: aborting due to 1 previous error
diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_data.rs b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_data.rs
index 0c305eed6e1..c03e468cfba 100644
--- a/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_data.rs
+++ b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_data.rs
@@ -1,4 +1,7 @@
 //@compile-flags: -Zmiri-disable-validation
+//@ normalize-stderr-test: "(\n)ALLOC \(.*\) \{\n(.*\n)*\}(\n)" -> "${1}ALLOC DUMP${3}"
+//@ normalize-stderr-test: "\[0x[0-9a-z]..0x[0-9a-z]\]" -> "[0xX..0xY]"
+
 #![feature(core_intrinsics, custom_mir)]
 use std::intrinsics::mir::*;
 
@@ -9,7 +12,7 @@ use std::intrinsics::mir::*;
 pub unsafe fn deref_meta(p: *const *const [i32]) -> usize {
     mir! {
         {
-            RET = PtrMetadata(*p); //~ ERROR: Undefined Behavior: using uninitialized data
+            RET = PtrMetadata(*p); //~ ERROR: /Undefined Behavior: .* but memory is uninitialized/
             Return()
         }
     }
diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_data.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_data.stderr
index 1c22876ba43..1e7f500edb2 100644
--- a/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_data.stderr
+++ b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_data.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0xX..0xY], but memory is uninitialized at [0xX..0xY], and this operation requires initialized memory
   --> tests/fail/intrinsics/ptr_metadata_uninit_slice_data.rs:LL:CC
    |
 LL |             RET = PtrMetadata(*p);
@@ -14,6 +14,9 @@ note: inside `main`
 LL |         let _meta = deref_meta(p.as_ptr().cast());
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
+Uninitialized memory occurred at ALLOC[0xX..0xY], in this allocation:
+ALLOC DUMP
+
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
 error: aborting due to 1 previous error
diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_len.rs b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_len.rs
index a2ffdc92c4e..7053c0f6e18 100644
--- a/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_len.rs
+++ b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_len.rs
@@ -1,4 +1,7 @@
 //@compile-flags: -Zmiri-disable-validation
+//@ normalize-stderr-test: "(\n)ALLOC \(.*\) \{\n(.*\n)*\}(\n)" -> "${1}ALLOC DUMP${3}"
+//@ normalize-stderr-test: "\[0x[0-9a-z]+..0x[0-9a-z]+\]" -> "[0xX..0xY]"
+
 #![feature(core_intrinsics, custom_mir)]
 use std::intrinsics::mir::*;
 
@@ -9,7 +12,7 @@ use std::intrinsics::mir::*;
 pub unsafe fn deref_meta(p: *const *const [i32]) -> usize {
     mir! {
         {
-            RET = PtrMetadata(*p); //~ ERROR: Undefined Behavior: using uninitialized data
+            RET = PtrMetadata(*p); //~ ERROR: /Undefined Behavior: .* but memory is uninitialized/
             Return()
         }
     }
diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_len.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_len.stderr
index 00e63b1275f..80b4c8bec0d 100644
--- a/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_len.stderr
+++ b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_slice_len.stderr
@@ -12,7 +12,7 @@ LL |         (*p.as_mut_ptr().cast::<[*const i32; 2]>())[0] = 4 as *const i32;
    = note: BACKTRACE:
    = note: inside `main` at tests/fail/intrinsics/ptr_metadata_uninit_slice_len.rs:LL:CC
 
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0xX..0xY], but memory is uninitialized at [0xX..0xY], and this operation requires initialized memory
   --> tests/fail/intrinsics/ptr_metadata_uninit_slice_len.rs:LL:CC
    |
 LL |             RET = PtrMetadata(*p);
@@ -28,6 +28,9 @@ note: inside `main`
 LL |         let _meta = deref_meta(p.as_ptr().cast());
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
+Uninitialized memory occurred at ALLOC[0xX..0xY], in this allocation:
+ALLOC DUMP
+
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
 error: aborting due to 1 previous error; 1 warning emitted
diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_thin.rs b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_thin.rs
index e5a51289a8a..3ba29847337 100644
--- a/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_thin.rs
+++ b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_thin.rs
@@ -1,4 +1,7 @@
 //@compile-flags: -Zmiri-disable-validation
+//@ normalize-stderr-test: "(\n)ALLOC \(.*\) \{\n(.*\n)*\}(\n)" -> "${1}ALLOC DUMP${3}"
+//@ normalize-stderr-test: "\[0x[0-9a-z]..0x[0-9a-z]\]" -> "[0xX..0xY]"
+
 #![feature(core_intrinsics, custom_mir)]
 use std::intrinsics::mir::*;
 
@@ -9,7 +12,7 @@ use std::intrinsics::mir::*;
 pub unsafe fn deref_meta(p: *const *const i32) -> () {
     mir! {
         {
-            RET = PtrMetadata(*p); //~ ERROR: Undefined Behavior: using uninitialized data
+            RET = PtrMetadata(*p); //~ ERROR: /Undefined Behavior: .*, but memory is uninitialized/
             Return()
         }
     }
diff --git a/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_thin.stderr b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_thin.stderr
index 24066953d79..7a1f3d6ea84 100644
--- a/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_thin.stderr
+++ b/src/tools/miri/tests/fail/intrinsics/ptr_metadata_uninit_thin.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0xX..0xY], but memory is uninitialized at [0xX..0xY], and this operation requires initialized memory
   --> tests/fail/intrinsics/ptr_metadata_uninit_thin.rs:LL:CC
    |
 LL |             RET = PtrMetadata(*p);
@@ -14,6 +14,9 @@ note: inside `main`
 LL |         let _meta = deref_meta(p.as_ptr());
    |                     ^^^^^^^^^^^^^^^^^^^^^^
 
+Uninitialized memory occurred at ALLOC[0xX..0xY], in this allocation:
+ALLOC DUMP
+
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
 error: aborting due to 1 previous error
diff --git a/src/tools/miri/tests/fail/read_from_trivial_switch.rs b/src/tools/miri/tests/fail/read_from_trivial_switch.rs
index d34b1cd5820..2696c42aeae 100644
--- a/src/tools/miri/tests/fail/read_from_trivial_switch.rs
+++ b/src/tools/miri/tests/fail/read_from_trivial_switch.rs
@@ -4,11 +4,14 @@
 //
 // See <https://github.com/rust-lang/miri/issues/4237>.
 
+//@ normalize-stderr-test: "(\n)ALLOC \(.*\) \{\n(.*\n)*\}(\n)" -> "${1}ALLOC DUMP${3}"
+//@ normalize-stderr-test: "\[0x[0-9a-z]..0x[0-9a-z]\]" -> "[0xX..0xY]"
+
 use std::mem::MaybeUninit;
 
 fn main() {
     let uninit: MaybeUninit<i32> = MaybeUninit::uninit();
     let bad_ref: &i32 = unsafe { uninit.assume_init_ref() };
     let &(0 | _) = bad_ref;
-    //~^ ERROR: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+    //~^ ERROR: /Undefined Behavior: .*, but memory is uninitialized .* requires initialized memory/
 }
diff --git a/src/tools/miri/tests/fail/read_from_trivial_switch.stderr b/src/tools/miri/tests/fail/read_from_trivial_switch.stderr
index 923d836ee0c..1dcc341b7e6 100644
--- a/src/tools/miri/tests/fail/read_from_trivial_switch.stderr
+++ b/src/tools/miri/tests/fail/read_from_trivial_switch.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0xX..0xY], but memory is uninitialized at [0xX..0xY], and this operation requires initialized memory
   --> tests/fail/read_from_trivial_switch.rs:LL:CC
    |
 LL |     let &(0 | _) = bad_ref;
@@ -9,6 +9,9 @@ LL |     let &(0 | _) = bad_ref;
    = note: BACKTRACE:
    = note: inside `main` at tests/fail/read_from_trivial_switch.rs:LL:CC
 
+Uninitialized memory occurred at ALLOC[0xX..0xY], in this allocation:
+ALLOC DUMP
+
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
 error: aborting due to 1 previous error
diff --git a/src/tools/miri/tests/fail/uninit/padding-enum.rs b/src/tools/miri/tests/fail/uninit/padding-enum.rs
index e1a16bea23c..606fa21016f 100644
--- a/src/tools/miri/tests/fail/uninit/padding-enum.rs
+++ b/src/tools/miri/tests/fail/uninit/padding-enum.rs
@@ -1,3 +1,6 @@
+//@ normalize-stderr-test: "(\n)ALLOC \(.*\) \{\n(.*\n)*\}(\n)" -> "${1}ALLOC DUMP${3}"
+//@ normalize-stderr-test: "\[0x[0-9a-z]..0x[0-9a-z]\]" -> "[0xX..0xY]"
+
 use std::mem;
 
 // We have three fields to avoid the ScalarPair optimization.
diff --git a/src/tools/miri/tests/fail/uninit/padding-enum.stderr b/src/tools/miri/tests/fail/uninit/padding-enum.stderr
index a9a5568f4e8..64229ac8817 100644
--- a/src/tools/miri/tests/fail/uninit/padding-enum.stderr
+++ b/src/tools/miri/tests/fail/uninit/padding-enum.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0xX..0xY], but memory is uninitialized at [0xX..0xY], and this operation requires initialized memory
   --> tests/fail/uninit/padding-enum.rs:LL:CC
    |
 LL |         let _val = *c.add(padding_offset);
@@ -9,6 +9,9 @@ LL |         let _val = *c.add(padding_offset);
    = note: BACKTRACE:
    = note: inside `main` at tests/fail/uninit/padding-enum.rs:LL:CC
 
+Uninitialized memory occurred at ALLOC[0xX..0xY], in this allocation:
+ALLOC DUMP
+
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
 error: aborting due to 1 previous error
diff --git a/src/tools/miri/tests/fail/uninit/padding-pair.rs b/src/tools/miri/tests/fail/uninit/padding-pair.rs
index c8c00b3c65a..70ae48ff77d 100644
--- a/src/tools/miri/tests/fail/uninit/padding-pair.rs
+++ b/src/tools/miri/tests/fail/uninit/padding-pair.rs
@@ -1,3 +1,6 @@
+//@ normalize-stderr-test: "(\n)ALLOC \(.*\) \{\n(.*\n)*\}(\n)" -> "${1}ALLOC DUMP${3}"
+//@ normalize-stderr-test: "\[0x[0-9a-z]..0x[0-9a-z]\]" -> "[0xX..0xY]"
+
 #![feature(core_intrinsics)]
 
 use std::mem::{self, MaybeUninit};
diff --git a/src/tools/miri/tests/fail/uninit/padding-pair.stderr b/src/tools/miri/tests/fail/uninit/padding-pair.stderr
index d281a351d41..2e7a577f204 100644
--- a/src/tools/miri/tests/fail/uninit/padding-pair.stderr
+++ b/src/tools/miri/tests/fail/uninit/padding-pair.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0xX..0xY], but memory is uninitialized at [0xX..0xY], and this operation requires initialized memory
   --> tests/fail/uninit/padding-pair.rs:LL:CC
    |
 LL |     let v = unsafe { *z.offset(first_undef) };
@@ -9,6 +9,9 @@ LL |     let v = unsafe { *z.offset(first_undef) };
    = note: BACKTRACE:
    = note: inside `main` at tests/fail/uninit/padding-pair.rs:LL:CC
 
+Uninitialized memory occurred at ALLOC[0xX..0xY], in this allocation:
+ALLOC DUMP
+
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
 error: aborting due to 1 previous error
diff --git a/src/tools/miri/tests/fail/uninit/padding-struct.stderr b/src/tools/miri/tests/fail/uninit/padding-struct.stderr
index 3298f6a4510..05d754625d3 100644
--- a/src/tools/miri/tests/fail/uninit/padding-struct.stderr
+++ b/src/tools/miri/tests/fail/uninit/padding-struct.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0x1..0x2], but memory is uninitialized at [0x1..0x2], and this operation requires initialized memory
   --> tests/fail/uninit/padding-struct.rs:LL:CC
    |
 LL |         let _val = *c.add(1);
@@ -9,6 +9,11 @@ LL |         let _val = *c.add(1);
    = note: BACKTRACE:
    = note: inside `main` at tests/fail/uninit/padding-struct.rs:LL:CC
 
+Uninitialized memory occurred at ALLOC[0x1..0x2], in this allocation:
+ALLOC (stack variable, size: 4, align: 2) {
+    00 __ 00 00                                     │ .░..
+}
+
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
 error: aborting due to 1 previous error
diff --git a/src/tools/miri/tests/fail/uninit/padding-wide-ptr.rs b/src/tools/miri/tests/fail/uninit/padding-wide-ptr.rs
index 4e363dbf81e..549785e0223 100644
--- a/src/tools/miri/tests/fail/uninit/padding-wide-ptr.rs
+++ b/src/tools/miri/tests/fail/uninit/padding-wide-ptr.rs
@@ -1,3 +1,6 @@
+//@ normalize-stderr-test: "(\n)ALLOC \(.*\) \{\n(.*\n)*\}(\n)" -> "${1}ALLOC DUMP${3}"
+//@ normalize-stderr-test: "\[0x[0-9a-z]..0x[0-9a-z]\]" -> "[0xX..0xY]"
+
 use std::mem;
 
 // If this is `None`, the metadata becomes padding.
diff --git a/src/tools/miri/tests/fail/uninit/padding-wide-ptr.stderr b/src/tools/miri/tests/fail/uninit/padding-wide-ptr.stderr
index d92d05ae631..ce11320ca1b 100644
--- a/src/tools/miri/tests/fail/uninit/padding-wide-ptr.stderr
+++ b/src/tools/miri/tests/fail/uninit/padding-wide-ptr.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0xX..0xY], but memory is uninitialized at [0xX..0xY], and this operation requires initialized memory
   --> tests/fail/uninit/padding-wide-ptr.rs:LL:CC
    |
 LL |         let _val = *c.add(mem::size_of::<*const u8>());
@@ -9,6 +9,9 @@ LL |         let _val = *c.add(mem::size_of::<*const u8>());
    = note: BACKTRACE:
    = note: inside `main` at tests/fail/uninit/padding-wide-ptr.rs:LL:CC
 
+Uninitialized memory occurred at ALLOC[0xX..0xY], in this allocation:
+ALLOC DUMP
+
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
 error: aborting due to 1 previous error
diff --git a/src/tools/miri/tests/fail/uninit/transmute-pair-uninit.rs b/src/tools/miri/tests/fail/uninit/transmute-pair-uninit.rs
index 0ba5520a544..c1d284c7057 100644
--- a/src/tools/miri/tests/fail/uninit/transmute-pair-uninit.rs
+++ b/src/tools/miri/tests/fail/uninit/transmute-pair-uninit.rs
@@ -1,3 +1,6 @@
+//@ normalize-stderr-test: "(\n)ALLOC \(.*\) \{\n(.*\n)*\}(\n)" -> "${1}ALLOC DUMP${3}"
+//@ normalize-stderr-test: "\[0x[0-9a-z]..0x[0-9a-z]\]" -> "[0xX..0xY]"
+
 #![feature(core_intrinsics)]
 
 use std::mem::{self, MaybeUninit};
diff --git a/src/tools/miri/tests/fail/uninit/transmute-pair-uninit.stderr b/src/tools/miri/tests/fail/uninit/transmute-pair-uninit.stderr
index 0ae0ce5de9c..eb049dd41ec 100644
--- a/src/tools/miri/tests/fail/uninit/transmute-pair-uninit.stderr
+++ b/src/tools/miri/tests/fail/uninit/transmute-pair-uninit.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0xX..0xY], but memory is uninitialized at [0xX..0xY], and this operation requires initialized memory
   --> tests/fail/uninit/transmute-pair-uninit.rs:LL:CC
    |
 LL |     let v = unsafe { *z.offset(first_undef) };
@@ -9,6 +9,9 @@ LL |     let v = unsafe { *z.offset(first_undef) };
    = note: BACKTRACE:
    = note: inside `main` at tests/fail/uninit/transmute-pair-uninit.rs:LL:CC
 
+Uninitialized memory occurred at ALLOC[0xX..0xY], in this allocation:
+ALLOC DUMP
+
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
 error: aborting due to 1 previous error
diff --git a/src/tools/miri/tests/fail/uninit/uninit_byte_read.stderr b/src/tools/miri/tests/fail/uninit/uninit_byte_read.stderr
index d2a5a2d3831..5a5aa12987c 100644
--- a/src/tools/miri/tests/fail/uninit/uninit_byte_read.stderr
+++ b/src/tools/miri/tests/fail/uninit/uninit_byte_read.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0x5..0x6], but memory is uninitialized at [0x5..0x6], and this operation requires initialized memory
   --> tests/fail/uninit/uninit_byte_read.rs:LL:CC
    |
 LL |     let undef = unsafe { *v.as_ptr().add(5) };
@@ -9,6 +9,11 @@ LL |     let undef = unsafe { *v.as_ptr().add(5) };
    = note: BACKTRACE:
    = note: inside `main` at tests/fail/uninit/uninit_byte_read.rs:LL:CC
 
+Uninitialized memory occurred at ALLOC[0x5..0x6], in this allocation:
+ALLOC (Rust heap, size: 10, align: 1) {
+    __ __ __ __ __ __ __ __ __ __                   │ ░░░░░░░░░░
+}
+
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
 error: aborting due to 1 previous error
diff --git a/src/tools/miri/tests/fail/validity/invalid_int_op.stderr b/src/tools/miri/tests/fail/validity/invalid_int_op.stderr
index 6e24cadfc20..0b1915621b2 100644
--- a/src/tools/miri/tests/fail/validity/invalid_int_op.stderr
+++ b/src/tools/miri/tests/fail/validity/invalid_int_op.stderr
@@ -1,4 +1,4 @@
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0x0..0x4], but memory is uninitialized at [0x0..0x4], and this operation requires initialized memory
   --> tests/fail/validity/invalid_int_op.rs:LL:CC
    |
 LL |     let i = unsafe { std::mem::MaybeUninit::<i32>::uninit().assume_init() };
@@ -9,6 +9,11 @@ LL |     let i = unsafe { std::mem::MaybeUninit::<i32>::uninit().assume_init() }
    = note: BACKTRACE:
    = note: inside `main` at tests/fail/validity/invalid_int_op.rs:LL:CC
 
+Uninitialized memory occurred at ALLOC[0x0..0x4], in this allocation:
+ALLOC (stack variable, size: 4, align: 4) {
+    __ __ __ __                                     │ ░░░░
+}
+
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
 error: aborting due to 1 previous error
diff --git a/src/tools/miri/tests/native-lib/fail/tracing/partial_init.rs b/src/tools/miri/tests/native-lib/fail/tracing/partial_init.rs
index e267f82e215..7ab160773ff 100644
--- a/src/tools/miri/tests/native-lib/fail/tracing/partial_init.rs
+++ b/src/tools/miri/tests/native-lib/fail/tracing/partial_init.rs
@@ -20,6 +20,6 @@ fn partial_init() {
         assert!(*slice_ptr == 0);
         assert!(*slice_ptr.offset(1) == 0);
         // Reading the third is UB!
-        let _val = *slice_ptr.offset(2); //~ ERROR: Undefined Behavior: using uninitialized data
+        let _val = *slice_ptr.offset(2); //~ ERROR: /Undefined Behavior: reading memory.*, but memory is uninitialized/
     }
 }
diff --git a/src/tools/miri/tests/native-lib/fail/tracing/partial_init.stderr b/src/tools/miri/tests/native-lib/fail/tracing/partial_init.stderr
index 84fd913b5e5..74a599ede5c 100644
--- a/src/tools/miri/tests/native-lib/fail/tracing/partial_init.stderr
+++ b/src/tools/miri/tests/native-lib/fail/tracing/partial_init.stderr
@@ -17,7 +17,7 @@ note: inside `main`
 LL |     partial_init();
    |     ^^^^^^^^^^^^^^
 
-error: Undefined Behavior: using uninitialized data, but this operation requires initialized memory
+error: Undefined Behavior: reading memory at ALLOC[0x2..0x3], but memory is uninitialized at [0x2..0x3], and this operation requires initialized memory
   --> tests/native-lib/fail/tracing/partial_init.rs:LL:CC
    |
 LL |         let _val = *slice_ptr.offset(2);
@@ -33,6 +33,11 @@ note: inside `main`
 LL |     partial_init();
    |     ^^^^^^^^^^^^^^
 
+Uninitialized memory occurred at ALLOC[0x2..0x3], in this allocation:
+ALLOC (stack variable, size: 3, align: 1) {
+    ╾00[wildcard] (1 ptr byte)╼ ╾00[wildcard] (1 ptr byte)╼ __                                        │ ━━░
+}
+
 note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
 
 error: aborting due to 1 previous error; 1 warning emitted
diff --git a/src/tools/miri/tests/pass-dep/libc/libc-time.rs b/src/tools/miri/tests/pass-dep/libc/libc-time.rs
index e8957846ad5..9e9fadfca9e 100644
--- a/src/tools/miri/tests/pass-dep/libc/libc-time.rs
+++ b/src/tools/miri/tests/pass-dep/libc/libc-time.rs
@@ -336,7 +336,7 @@ fn test_nanosleep() {
     let remainder = ptr::null_mut::<libc::timespec>();
     let is_error = unsafe { libc::nanosleep(&duration_zero, remainder) };
     assert_eq!(is_error, 0);
-    assert!(start_test_sleep.elapsed() < Duration::from_millis(10));
+    assert!(start_test_sleep.elapsed() < Duration::from_millis(100));
 
     let start_test_sleep = Instant::now();
     let duration_100_millis = libc::timespec { tv_sec: 0, tv_nsec: 1_000_000_000 / 10 };
@@ -390,7 +390,7 @@ mod test_clock_nanosleep {
             )
         };
         assert_eq!(error, 0);
-        assert!(start_test_sleep.elapsed() < Duration::from_millis(10));
+        assert!(start_test_sleep.elapsed() < Duration::from_millis(100));
 
         let start_test_sleep = Instant::now();
         let hunderd_millis_after_start = add_100_millis(timespec_now(libc::CLOCK_MONOTONIC));
@@ -417,7 +417,7 @@ mod test_clock_nanosleep {
             libc::clock_nanosleep(libc::CLOCK_MONOTONIC, NO_FLAGS, &duration_zero, remainder)
         };
         assert_eq!(error, 0);
-        assert!(start_test_sleep.elapsed() < Duration::from_millis(10));
+        assert!(start_test_sleep.elapsed() < Duration::from_millis(100));
 
         let start_test_sleep = Instant::now();
         let duration_100_millis = libc::timespec { tv_sec: 0, tv_nsec: 1_000_000_000 / 10 };
diff --git a/src/tools/opt-dist/src/environment.rs b/src/tools/opt-dist/src/environment.rs
index 946e926a3c0..d41dc80e6b2 100644
--- a/src/tools/opt-dist/src/environment.rs
+++ b/src/tools/opt-dist/src/environment.rs
@@ -27,6 +27,7 @@ pub struct Environment {
     shared_llvm: bool,
     run_tests: bool,
     fast_try_build: bool,
+    build_llvm: bool,
 }
 
 impl Environment {
@@ -111,6 +112,10 @@ impl Environment {
     pub fn is_fast_try_build(&self) -> bool {
         self.fast_try_build
     }
+
+    pub fn build_llvm(&self) -> bool {
+        self.build_llvm
+    }
 }
 
 /// What is the extension of binary executables on this platform?
diff --git a/src/tools/opt-dist/src/exec.rs b/src/tools/opt-dist/src/exec.rs
index 0dc6e56b9d5..56eff2ca2a7 100644
--- a/src/tools/opt-dist/src/exec.rs
+++ b/src/tools/opt-dist/src/exec.rs
@@ -139,8 +139,10 @@ impl Bootstrap {
         self
     }
 
-    pub fn llvm_pgo_optimize(mut self, profile: &LlvmPGOProfile) -> Self {
-        self.cmd = self.cmd.arg("--llvm-profile-use").arg(profile.0.as_str());
+    pub fn llvm_pgo_optimize(mut self, profile: Option<&LlvmPGOProfile>) -> Self {
+        if let Some(prof) = profile {
+            self.cmd = self.cmd.arg("--llvm-profile-use").arg(prof.0.as_str());
+        }
         self
     }
 
@@ -174,8 +176,10 @@ impl Bootstrap {
         self
     }
 
-    pub fn with_bolt_profile(mut self, profile: BoltProfile) -> Self {
-        self.cmd = self.cmd.arg("--reproducible-artifact").arg(profile.0.as_str());
+    pub fn with_bolt_profile(mut self, profile: Option<BoltProfile>) -> Self {
+        if let Some(prof) = profile {
+            self.cmd = self.cmd.arg("--reproducible-artifact").arg(prof.0.as_str());
+        }
         self
     }
 
diff --git a/src/tools/opt-dist/src/main.rs b/src/tools/opt-dist/src/main.rs
index 9c8a6637a3b..7857f196626 100644
--- a/src/tools/opt-dist/src/main.rs
+++ b/src/tools/opt-dist/src/main.rs
@@ -98,6 +98,10 @@ enum EnvironmentCmd {
         /// Perform tests after final build if it's not a fast try build
         #[arg(long)]
         run_tests: bool,
+
+        /// Will be LLVM built during the run?
+        #[arg(long, default_value_t = true, action(clap::ArgAction::Set))]
+        build_llvm: bool,
     },
     /// Perform an optimized build on Linux CI, from inside Docker.
     LinuxCi {
@@ -133,6 +137,7 @@ fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec<String>)>
             benchmark_cargo_config,
             shared,
             run_tests,
+            build_llvm,
         } => {
             let env = EnvironmentBuilder::default()
                 .host_tuple(target_triple)
@@ -148,6 +153,7 @@ fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec<String>)>
                 .benchmark_cargo_config(benchmark_cargo_config)
                 .run_tests(run_tests)
                 .fast_try_build(is_fast_try_build)
+                .build_llvm(build_llvm)
                 .build()?;
 
             (env, shared.build_args)
@@ -172,6 +178,7 @@ fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec<String>)>
                 .skipped_tests(vec![])
                 .run_tests(true)
                 .fast_try_build(is_fast_try_build)
+                .build_llvm(true)
                 .build()?;
 
             (env, shared.build_args)
@@ -193,6 +200,7 @@ fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec<String>)>
                 .skipped_tests(vec![])
                 .run_tests(true)
                 .fast_try_build(is_fast_try_build)
+                .build_llvm(true)
                 .build()?;
 
             (env, shared.build_args)
@@ -255,30 +263,35 @@ fn execute_pipeline(
     // Stage 2: Gather LLVM PGO profiles
     // Here we build a PGO instrumented LLVM, reusing the previously PGO optimized rustc.
     // Then we use the instrumented LLVM to gather LLVM PGO profiles.
-    let llvm_pgo_profile = timer.section("Stage 2 (LLVM PGO)", |stage| {
-        // Remove the previous, uninstrumented build of LLVM.
-        clear_llvm_files(env)?;
+    let llvm_pgo_profile = if env.build_llvm() {
+        timer.section("Stage 2 (LLVM PGO)", |stage| {
+            // Remove the previous, uninstrumented build of LLVM.
+            clear_llvm_files(env)?;
 
-        let llvm_profile_dir_root = env.artifact_dir().join("llvm-pgo");
+            let llvm_profile_dir_root = env.artifact_dir().join("llvm-pgo");
 
-        stage.section("Build PGO instrumented LLVM", |section| {
-            Bootstrap::build(env)
-                .llvm_pgo_instrument(&llvm_profile_dir_root)
-                .avoid_rustc_rebuild()
-                .run(section)
-        })?;
+            stage.section("Build PGO instrumented LLVM", |section| {
+                Bootstrap::build(env)
+                    .llvm_pgo_instrument(&llvm_profile_dir_root)
+                    .avoid_rustc_rebuild()
+                    .run(section)
+            })?;
 
-        let profile = stage
-            .section("Gather profiles", |_| gather_llvm_profiles(env, &llvm_profile_dir_root))?;
+            let profile = stage.section("Gather profiles", |_| {
+                gather_llvm_profiles(env, &llvm_profile_dir_root)
+            })?;
 
-        print_free_disk_space()?;
+            print_free_disk_space()?;
 
-        // Proactively delete the instrumented artifacts, to avoid using them by accident in
-        // follow-up stages.
-        clear_llvm_files(env)?;
+            // Proactively delete the instrumented artifacts, to avoid using them by accident in
+            // follow-up stages.
+            clear_llvm_files(env)?;
 
-        Ok(profile)
-    })?;
+            Ok(Some(profile))
+        })?
+    } else {
+        None
+    };
 
     let bolt_profiles = if env.use_bolt() {
         // Stage 3: Build BOLT instrumented LLVM
@@ -286,37 +299,43 @@ fn execute_pipeline(
         // Note that we don't remove LLVM artifacts after this step, so that they are reused in the final dist build.
         // BOLT instrumentation is performed "on-the-fly" when the LLVM library is copied to the sysroot of rustc,
         // therefore the LLVM artifacts on disk are not "tainted" with BOLT instrumentation and they can be reused.
+        let libdir = env.build_artifacts().join("stage2").join("lib");
         timer.section("Stage 3 (BOLT)", |stage| {
-            stage.section("Build PGO optimized LLVM", |stage| {
-                Bootstrap::build(env)
-                    .with_llvm_bolt_ldflags()
-                    .llvm_pgo_optimize(&llvm_pgo_profile)
-                    .avoid_rustc_rebuild()
-                    .run(stage)
-            })?;
-
-            let libdir = env.build_artifacts().join("stage2").join("lib");
-            // The actual name will be something like libLLVM.so.18.1-rust-dev.
-            let llvm_lib = io::find_file_in_dir(&libdir, "libLLVM.so", "")?;
-
-            log::info!("Optimizing {llvm_lib} with BOLT");
-
-            // FIXME(kobzol): try gather profiles together, at once for LLVM and rustc
-            // Instrument the libraries and gather profiles
-            let llvm_profile = with_bolt_instrumented(&llvm_lib, |llvm_profile_dir| {
-                stage.section("Gather profiles", |_| {
-                    gather_bolt_profiles(env, "LLVM", llvm_benchmarks(env), llvm_profile_dir)
-                })
-            })?;
-            print_free_disk_space()?;
-
-            // Now optimize the library with BOLT. The `libLLVM-XXX.so` library is actually hard-linked
-            // from several places, and this specific path (`llvm_lib`) will *not* be packaged into
-            // the final dist build. However, when BOLT optimizes an artifact, it does so *in-place*,
-            // therefore it will actually optimize all the hard links, which means that the final
-            // packaged `libLLVM.so` file *will* be BOLT optimized.
-            bolt_optimize(&llvm_lib, &llvm_profile, env)
-                .context("Could not optimize LLVM with BOLT")?;
+            let llvm_profile = if env.build_llvm() {
+                stage.section("Build PGO optimized LLVM", |stage| {
+                    Bootstrap::build(env)
+                        .with_llvm_bolt_ldflags()
+                        .llvm_pgo_optimize(llvm_pgo_profile.as_ref())
+                        .avoid_rustc_rebuild()
+                        .run(stage)
+                })?;
+
+                // The actual name will be something like libLLVM.so.18.1-rust-dev.
+                let llvm_lib = io::find_file_in_dir(&libdir, "libLLVM.so", "")?;
+
+                log::info!("Optimizing {llvm_lib} with BOLT");
+
+                // FIXME(kobzol): try gather profiles together, at once for LLVM and rustc
+                // Instrument the libraries and gather profiles
+                let llvm_profile = with_bolt_instrumented(&llvm_lib, |llvm_profile_dir| {
+                    stage.section("Gather profiles", |_| {
+                        gather_bolt_profiles(env, "LLVM", llvm_benchmarks(env), llvm_profile_dir)
+                    })
+                })?;
+                print_free_disk_space()?;
+
+                // Now optimize the library with BOLT. The `libLLVM-XXX.so` library is actually hard-linked
+                // from several places, and this specific path (`llvm_lib`) will *not* be packaged into
+                // the final dist build. However, when BOLT optimizes an artifact, it does so *in-place*,
+                // therefore it will actually optimize all the hard links, which means that the final
+                // packaged `libLLVM.so` file *will* be BOLT optimized.
+                bolt_optimize(&llvm_lib, &llvm_profile, env)
+                    .context("Could not optimize LLVM with BOLT")?;
+
+                Some(llvm_profile)
+            } else {
+                None
+            };
 
             let rustc_lib = io::find_file_in_dir(&libdir, "librustc_driver", ".so")?;
 
@@ -334,15 +353,16 @@ fn execute_pipeline(
             bolt_optimize(&rustc_lib, &rustc_profile, env)
                 .context("Could not optimize rustc with BOLT")?;
 
-            // LLVM is not being cleared here, we want to use the BOLT-optimized LLVM
-            Ok(vec![llvm_profile, rustc_profile])
+            // LLVM is not being cleared here. Either we built it and we want to use the BOLT-optimized LLVM, or we
+            // didn't build it, so we don't want to remove it.
+            Ok(vec![llvm_profile, Some(rustc_profile)])
         })?
     } else {
         vec![]
     };
 
     let mut dist = Bootstrap::dist(env, &dist_args)
-        .llvm_pgo_optimize(&llvm_pgo_profile)
+        .llvm_pgo_optimize(llvm_pgo_profile.as_ref())
         .rustc_pgo_optimize(&rustc_pgo_profile)
         .avoid_rustc_rebuild();
 
diff --git a/src/tools/opt-dist/src/training.rs b/src/tools/opt-dist/src/training.rs
index 36a7d6a7cba..ae062d5c60c 100644
--- a/src/tools/opt-dist/src/training.rs
+++ b/src/tools/opt-dist/src/training.rs
@@ -163,7 +163,9 @@ pub fn gather_rustc_profiles(
     let merged_profile = env.artifact_dir().join("rustc-pgo.profdata");
     log::info!("Merging Rustc PGO profiles to {merged_profile}");
 
-    merge_llvm_profiles(env, &merged_profile, profile_root, LlvmProfdata::Target)?;
+    let llvm_profdata = if env.build_llvm() { LlvmProfdata::Target } else { LlvmProfdata::Host };
+
+    merge_llvm_profiles(env, &merged_profile, profile_root, llvm_profdata)?;
     log_profile_stats("Rustc", &merged_profile, profile_root)?;
 
     // We don't need the individual .profraw files now that they have been merged
diff --git a/src/tools/tidy/src/features.rs b/src/tools/tidy/src/features.rs
index e83b47e1380..fb00b3a943f 100644
--- a/src/tools/tidy/src/features.rs
+++ b/src/tools/tidy/src/features.rs
@@ -331,11 +331,9 @@ fn collect_lang_features_in(features: &mut Features, base: &Path, file: &str, ba
             continue;
         }
 
-        if in_feature_group {
-            if let Some(doc_comment) = line.strip_prefix("///") {
-                doc_comments.push(doc_comment.trim().to_string());
-                continue;
-            }
+        if in_feature_group && let Some(doc_comment) = line.strip_prefix("///") {
+            doc_comments.push(doc_comment.trim().to_string());
+            continue;
         }
 
         let mut parts = line.split(',');
@@ -465,19 +463,20 @@ fn get_and_check_lib_features(
     map_lib_features(base_src_path, &mut |res, file, line| match res {
         Ok((name, f)) => {
             let mut check_features = |f: &Feature, list: &Features, display: &str| {
-                if let Some(s) = list.get(name) {
-                    if f.tracking_issue != s.tracking_issue && f.level != Status::Accepted {
-                        tidy_error!(
-                            bad,
-                            "{}:{}: feature gate {} has inconsistent `issue`: \"{}\" mismatches the {} `issue` of \"{}\"",
-                            file.display(),
-                            line,
-                            name,
-                            f.tracking_issue_display(),
-                            display,
-                            s.tracking_issue_display(),
-                        );
-                    }
+                if let Some(s) = list.get(name)
+                    && f.tracking_issue != s.tracking_issue
+                    && f.level != Status::Accepted
+                {
+                    tidy_error!(
+                        bad,
+                        "{}:{}: feature gate {} has inconsistent `issue`: \"{}\" mismatches the {} `issue` of \"{}\"",
+                        file.display(),
+                        line,
+                        name,
+                        f.tracking_issue_display(),
+                        display,
+                        s.tracking_issue_display(),
+                    );
                 }
             };
             check_features(&f, lang_features, "corresponding lang feature");
diff --git a/src/tools/tidy/src/fluent_period.rs b/src/tools/tidy/src/fluent_period.rs
index 85c1ef6166a..836b5699289 100644
--- a/src/tools/tidy/src/fluent_period.rs
+++ b/src/tools/tidy/src/fluent_period.rs
@@ -33,14 +33,14 @@ fn check_period(filename: &str, contents: &str, bad: &mut bool) {
                 continue;
             }
 
-            if let Some(pat) = &m.value {
-                if let Some(PatternElement::TextElement { value }) = pat.elements.last() {
-                    // We don't care about ellipses.
-                    if value.ends_with(".") && !value.ends_with("...") {
-                        let ll = find_line(contents, value);
-                        let name = m.id.name;
-                        tidy_error!(bad, "{filename}:{ll}: message `{name}` ends in a period");
-                    }
+            if let Some(pat) = &m.value
+                && let Some(PatternElement::TextElement { value }) = pat.elements.last()
+            {
+                // We don't care about ellipses.
+                if value.ends_with(".") && !value.ends_with("...") {
+                    let ll = find_line(contents, value);
+                    let name = m.id.name;
+                    tidy_error!(bad, "{filename}:{ll}: message `{name}` ends in a period");
                 }
             }
 
@@ -50,12 +50,13 @@ fn check_period(filename: &str, contents: &str, bad: &mut bool) {
                     continue;
                 }
 
-                if let Some(PatternElement::TextElement { value }) = attr.value.elements.last() {
-                    if value.ends_with(".") && !value.ends_with("...") {
-                        let ll = find_line(contents, value);
-                        let name = attr.id.name;
-                        tidy_error!(bad, "{filename}:{ll}: attr `{name}` ends in a period");
-                    }
+                if let Some(PatternElement::TextElement { value }) = attr.value.elements.last()
+                    && value.ends_with(".")
+                    && !value.ends_with("...")
+                {
+                    let ll = find_line(contents, value);
+                    let name = attr.id.name;
+                    tidy_error!(bad, "{filename}:{ll}: attr `{name}` ends in a period");
                 }
             }
         }
diff --git a/src/tools/tidy/src/fluent_used.rs b/src/tools/tidy/src/fluent_used.rs
index 12fafd9a7ff..909bf482ddf 100644
--- a/src/tools/tidy/src/fluent_used.rs
+++ b/src/tools/tidy/src/fluent_used.rs
@@ -12,7 +12,7 @@ fn filter_used_messages(
 ) {
     // we don't just check messages never appear in Rust files,
     // because messages can be used as parts of other fluent messages in Fluent files,
-    // so we do checking messages appear only once in all Rust and Fluent files.
+    // so we check messages appear only once in all Rust and Fluent files.
     let matches = static_regex!(r"\w+").find_iter(contents);
     for name in matches {
         if let Some((name, filename)) = msgs_not_appeared_yet.remove_entry(name.as_str()) {
diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs
index ade4055b5bd..cb875504e1e 100644
--- a/src/tools/tidy/src/lib.rs
+++ b/src/tools/tidy/src/lib.rs
@@ -13,8 +13,9 @@ use termcolor::WriteColor;
 
 macro_rules! static_regex {
     ($re:literal) => {{
-        static RE: ::std::sync::OnceLock<::regex::Regex> = ::std::sync::OnceLock::new();
-        RE.get_or_init(|| ::regex::Regex::new($re).unwrap())
+        static RE: ::std::sync::LazyLock<::regex::Regex> =
+            ::std::sync::LazyLock::new(|| ::regex::Regex::new($re).unwrap());
+        &*RE
     }};
 }
 
@@ -134,7 +135,7 @@ pub fn files_modified(ci_info: &CiInfo, pred: impl Fn(&str) -> bool) -> bool {
         eprintln!("No base commit, assuming all files are modified");
         return true;
     };
-    match crate::git_diff(&base_commit, "--name-status") {
+    match crate::git_diff(base_commit, "--name-status") {
         Some(output) => {
             let modified_files = output.lines().filter_map(|ln| {
                 let (status, name) = ln
diff --git a/src/tools/tidy/src/mir_opt_tests.rs b/src/tools/tidy/src/mir_opt_tests.rs
index 1efe71b1687..6119eb58383 100644
--- a/src/tools/tidy/src/mir_opt_tests.rs
+++ b/src/tools/tidy/src/mir_opt_tests.rs
@@ -55,22 +55,21 @@ fn check_dash_files(path: &Path, bless: bool, bad: &mut bool) {
         .filter(|e| e.file_type().is_file())
     {
         let path = file.path();
-        if path.extension() == Some("rs".as_ref()) {
-            if let Some(name) = path.file_name().and_then(|s| s.to_str()) {
-                if name.contains('-') {
-                    if !bless {
-                        tidy_error!(
-                            bad,
-                            "mir-opt test files should not have dashes in them: {}",
-                            path.display()
-                        );
-                    } else {
-                        let new_name = name.replace('-', "_");
-                        let mut new_path = path.to_owned();
-                        new_path.set_file_name(new_name);
-                        let _ = std::fs::rename(path, new_path);
-                    }
-                }
+        if path.extension() == Some("rs".as_ref())
+            && let Some(name) = path.file_name().and_then(|s| s.to_str())
+            && name.contains('-')
+        {
+            if !bless {
+                tidy_error!(
+                    bad,
+                    "mir-opt test files should not have dashes in them: {}",
+                    path.display()
+                );
+            } else {
+                let new_name = name.replace('-', "_");
+                let mut new_path = path.to_owned();
+                new_path.set_file_name(new_name);
+                let _ = std::fs::rename(path, new_path);
             }
         }
     }
diff --git a/src/tools/tidy/src/rustdoc_templates.rs b/src/tools/tidy/src/rustdoc_templates.rs
index dca3e8d9d25..597290a6a9a 100644
--- a/src/tools/tidy/src/rustdoc_templates.rs
+++ b/src/tools/tidy/src/rustdoc_templates.rs
@@ -26,7 +26,7 @@ pub fn check(librustdoc_path: &Path, bad: &mut bool) {
                         None
                     // Then we check if this a comment tag.
                     } else if *tag != "{#" {
-                        return Some(false);
+                        Some(false)
                     // And finally we check if the comment is empty (ie, only there to strip
                     // extra whitespace characters).
                     } else if let Some(start_pos) = line.rfind(tag) {
diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs
index 8dde4618ce5..35ed61eacc7 100644
--- a/src/tools/tidy/src/style.rs
+++ b/src/tools/tidy/src/style.rs
@@ -417,10 +417,10 @@ pub fn check(path: &Path, bad: &mut bool) {
             return;
         }
         // Shell completions are automatically generated
-        if let Some(p) = file.parent() {
-            if p.ends_with(Path::new("src/etc/completions")) {
-                return;
-            }
+        if let Some(p) = file.parent()
+            && p.ends_with(Path::new("src/etc/completions"))
+        {
+            return;
         }
         let [
             mut skip_cr,
@@ -604,25 +604,25 @@ pub fn check(path: &Path, bad: &mut bool) {
                         backtick_count += comment_text.chars().filter(|ch| *ch == '`').count();
                     }
                     comment_block = Some((start_line, backtick_count));
-                } else if let Some((start_line, backtick_count)) = comment_block.take() {
-                    if backtick_count % 2 == 1 {
-                        let mut err = |msg: &str| {
-                            tidy_error!(bad, "{}:{start_line}: {msg}", file.display());
-                        };
-                        let block_len = (i + 1) - start_line;
-                        if block_len == 1 {
-                            suppressible_tidy_err!(
-                                err,
-                                skip_odd_backticks,
-                                "comment with odd number of backticks"
-                            );
-                        } else {
-                            suppressible_tidy_err!(
-                                err,
-                                skip_odd_backticks,
-                                "{block_len}-line comment block with odd number of backticks"
-                            );
-                        }
+                } else if let Some((start_line, backtick_count)) = comment_block.take()
+                    && backtick_count % 2 == 1
+                {
+                    let mut err = |msg: &str| {
+                        tidy_error!(bad, "{}:{start_line}: {msg}", file.display());
+                    };
+                    let block_len = (i + 1) - start_line;
+                    if block_len == 1 {
+                        suppressible_tidy_err!(
+                            err,
+                            skip_odd_backticks,
+                            "comment with odd number of backticks"
+                        );
+                    } else {
+                        suppressible_tidy_err!(
+                            err,
+                            skip_odd_backticks,
+                            "{block_len}-line comment block with odd number of backticks"
+                        );
                     }
                 }
             }
diff --git a/src/tools/tidy/src/target_specific_tests.rs b/src/tools/tidy/src/target_specific_tests.rs
index 1a6fd3eaf2d..f4a6783abb6 100644
--- a/src/tools/tidy/src/target_specific_tests.rs
+++ b/src/tools/tidy/src/target_specific_tests.rs
@@ -30,17 +30,17 @@ pub fn check(tests_path: &Path, bad: &mut bool) {
                         comp_vec.push(component);
                     }
                 }
-            } else if let Some(compile_flags) = directive.strip_prefix(COMPILE_FLAGS_HEADER) {
-                if let Some((_, v)) = compile_flags.split_once("--target") {
-                    let v = v.trim_start_matches([' ', '=']);
-                    let v = if v == "{{target}}" { Some((v, v)) } else { v.split_once("-") };
-                    if let Some((arch, _)) = v {
-                        let info = header_map.entry(revision).or_insert(RevisionInfo::default());
-                        info.target_arch.replace(arch);
-                    } else {
-                        eprintln!("{file}: seems to have a malformed --target value");
-                        *bad = true;
-                    }
+            } else if let Some(compile_flags) = directive.strip_prefix(COMPILE_FLAGS_HEADER)
+                && let Some((_, v)) = compile_flags.split_once("--target")
+            {
+                let v = v.trim_start_matches([' ', '=']);
+                let v = if v == "{{target}}" { Some((v, v)) } else { v.split_once("-") };
+                if let Some((arch, _)) = v {
+                    let info = header_map.entry(revision).or_insert(RevisionInfo::default());
+                    info.target_arch.replace(arch);
+                } else {
+                    eprintln!("{file}: seems to have a malformed --target value");
+                    *bad = true;
                 }
             }
         });
diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs
index 7e295731c56..b9d22ece597 100644
--- a/src/tools/tidy/src/ui_tests.rs
+++ b/src/tools/tidy/src/ui_tests.rs
@@ -161,31 +161,30 @@ pub fn check(root_path: &Path, bless: bool, bad: &mut bool) {
                     tidy_error!(bad, "Stray file with UI testing output: {:?}", file_path);
                 }
 
-                if let Ok(metadata) = fs::metadata(file_path) {
-                    if metadata.len() == 0 {
-                        tidy_error!(bad, "Empty file with UI testing output: {:?}", file_path);
-                    }
+                if let Ok(metadata) = fs::metadata(file_path)
+                    && metadata.len() == 0
+                {
+                    tidy_error!(bad, "Empty file with UI testing output: {:?}", file_path);
                 }
             }
 
-            if ext == "rs" {
-                if let Some(test_name) = static_regex!(r"^issues?[-_]?(\d{3,})").captures(testname)
-                {
-                    // these paths are always relative to the passed `path` and always UTF8
-                    let stripped_path = file_path
-                        .strip_prefix(path)
-                        .unwrap()
-                        .to_str()
-                        .unwrap()
-                        .replace(std::path::MAIN_SEPARATOR_STR, "/");
-
-                    if !remaining_issue_names.remove(stripped_path.as_str()) {
-                        tidy_error!(
-                            bad,
-                            "file `tests/{stripped_path}` must begin with a descriptive name, consider `{{reason}}-issue-{issue_n}.rs`",
-                            issue_n = &test_name[1],
-                        );
-                    }
+            if ext == "rs"
+                && let Some(test_name) = static_regex!(r"^issues?[-_]?(\d{3,})").captures(testname)
+            {
+                // these paths are always relative to the passed `path` and always UTF8
+                let stripped_path = file_path
+                    .strip_prefix(path)
+                    .unwrap()
+                    .to_str()
+                    .unwrap()
+                    .replace(std::path::MAIN_SEPARATOR_STR, "/");
+
+                if !remaining_issue_names.remove(stripped_path.as_str()) {
+                    tidy_error!(
+                        bad,
+                        "file `tests/{stripped_path}` must begin with a descriptive name, consider `{{reason}}-issue-{issue_n}.rs`",
+                        issue_n = &test_name[1],
+                    );
                 }
             }
         }
diff --git a/src/tools/tidy/src/x_version.rs b/src/tools/tidy/src/x_version.rs
index 6a5e9eca813..9f7f43c4000 100644
--- a/src/tools/tidy/src/x_version.rs
+++ b/src/tools/tidy/src/x_version.rs
@@ -25,12 +25,12 @@ pub fn check(root: &Path, cargo: &Path, bad: &mut bool) {
                 if let Some(version) = iter.next() {
                     // Check this is the rust-lang/rust x tool installation since it should be
                     // installed at a path containing `src/tools/x`.
-                    if let Some(path) = iter.next() {
-                        if path.contains("src/tools/x") {
-                            let version = version.strip_prefix("v").unwrap();
-                            installed = Some(Version::parse(version).unwrap());
-                            break;
-                        }
+                    if let Some(path) = iter.next()
+                        && path.contains("src/tools/x")
+                    {
+                        let version = version.strip_prefix("v").unwrap();
+                        installed = Some(Version::parse(version).unwrap());
+                        break;
                     };
                 }
             } else {