about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2025-04-25 09:09:27 +0000
committerbors <bors@rust-lang.org>2025-04-25 09:09:27 +0000
commit5c54aa781f97ae95ef01a3120650907b87d385d3 (patch)
treed55283dfee0f556b72d23174b5707dacce002e67
parent862156d6f25ccb0d915d2a0c8ebab520d05e4f72 (diff)
parentbc25dc23765b225197c62470fc70da0882220a12 (diff)
downloadrust-5c54aa781f97ae95ef01a3120650907b87d385d3.tar.gz
rust-5c54aa781f97ae95ef01a3120650907b87d385d3.zip
Auto merge of #140273 - matthiaskrgr:rollup-rxmuvkg, r=matthiaskrgr
Rollup of 8 pull requests

Successful merges:

 - #137096 (Stabilize flags for doctest cross compilation)
 - #140148 (CI: use aws codebuild for job dist-arm-linux)
 - #140187 ([AIX] Handle AIX dynamic library extensions within c-link-to-rust-dylib run-make test)
 - #140196 (Improved diagnostics for non-primitive cast on non-primitive types (`Arc`, `Option`))
 - #140210 (Work around cygwin issue on condvar timeout)
 - #140213 (mention about `x.py setup` in `INSTALL.md`)
 - #140229 (`DelimArgs` tweaks)
 - #140248 (Fix impl block items indent)

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--.github/workflows/ci.yml13
-rw-r--r--INSTALL.md25
-rw-r--r--compiler/rustc_ast/src/ast.rs14
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs2
-rw-r--r--compiler/rustc_attr_parsing/src/context.rs8
-rw-r--r--compiler/rustc_hir_typeck/src/cast.rs25
-rw-r--r--library/std/src/sys/pal/unix/sync/condvar.rs5
-rw-r--r--src/ci/citool/src/jobs.rs33
-rw-r--r--src/ci/docker/host-x86_64/dist-arm-linux/Dockerfile2
-rwxr-xr-xsrc/ci/docker/run.sh2
-rw-r--r--src/ci/github-actions/jobs.yml11
-rwxr-xr-xsrc/ci/scripts/free-disk-space.sh43
-rw-r--r--src/doc/rustdoc/src/command-line-arguments.md22
-rw-r--r--src/doc/rustdoc/src/unstable-features.md54
-rw-r--r--src/doc/rustdoc/src/write-documentation/documentation-tests.md37
-rw-r--r--src/librustdoc/config.rs23
-rw-r--r--src/librustdoc/doctest.rs6
-rw-r--r--src/librustdoc/doctest/markdown.rs8
-rw-r--r--src/librustdoc/doctest/rust.rs6
-rw-r--r--src/librustdoc/html/markdown.rs30
-rw-r--r--src/librustdoc/html/markdown/tests.rs4
-rw-r--r--src/librustdoc/html/render/mod.rs26
-rw-r--r--src/librustdoc/html/static/css/rustdoc.css5
-rw-r--r--src/librustdoc/lib.rs18
-rw-r--r--src/librustdoc/passes/calculate_doc_coverage.rs2
-rw-r--r--src/librustdoc/passes/check_doc_test_visibility.rs2
-rw-r--r--src/tools/miri/cargo-miri/src/main.rs2
-rw-r--r--src/tools/miri/cargo-miri/src/phases.rs10
-rw-r--r--tests/run-make/c-link-to-rust-dylib/rmake.rs6
-rw-r--r--tests/run-make/doctests-runtool/rmake.rs6
-rw-r--r--tests/run-make/rustdoc-default-output/output-default.stdout7
-rw-r--r--tests/rustdoc-gui/docblock-table-overflow.goml2
-rw-r--r--tests/rustdoc-gui/impl-doc-indent.goml16
-rw-r--r--tests/rustdoc-gui/item-info-overflow.goml2
-rw-r--r--tests/rustdoc-gui/src/test_docs/lib.rs26
-rw-r--r--tests/rustdoc/doctest/auxiliary/doctest-runtool.rs21
-rw-r--r--tests/rustdoc/doctest/doctest-runtool.rs13
-rw-r--r--tests/ui/coercion/issue-73886.stderr7
-rw-r--r--tests/ui/coercion/non-primitive-cast-135412.fixed10
-rw-r--r--tests/ui/coercion/non-primitive-cast-135412.rs10
-rw-r--r--tests/ui/coercion/non-primitive-cast-135412.stderr29
41 files changed, 399 insertions, 194 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index efe4157aae2..93316b9cff7 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -91,6 +91,17 @@ jobs:
         # Check the `calculate_matrix` job to see how is the matrix defined.
         include: ${{ fromJSON(needs.calculate_matrix.outputs.jobs) }}
     steps:
+      - name: Install cargo in AWS CodeBuild
+        if: matrix.codebuild
+        run: |
+          # Check if cargo is installed
+          if ! command -v cargo &> /dev/null; then
+            echo "Cargo not found, installing Rust..."
+            curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile=minimal
+            # Make cargo available in PATH
+            echo "$HOME/.cargo/bin" >> $GITHUB_PATH
+          fi
+
       - name: disable git crlf conversion
         run: git config --global core.autocrlf false
 
@@ -165,6 +176,8 @@ jobs:
         run: src/ci/scripts/install-ninja.sh
 
       - name: enable ipv6 on Docker
+        # Don't run on codebuild because systemctl is not available
+        if: ${{ !matrix.codebuild }}
         run: src/ci/scripts/enable-docker-ipv6.sh
 
       # Disable automatic line ending conversion (again). On Windows, when we're
diff --git a/INSTALL.md b/INSTALL.md
index 30e08201d6d..98eb825cd10 100644
--- a/INSTALL.md
+++ b/INSTALL.md
@@ -75,8 +75,31 @@ See [the rustc-dev-guide for more info][sysllvm].
 
 2. Configure the build settings:
 
+   If you're unsure which build configurations to use and need a good default, you
+   can run the interactive `x.py setup` command. This will guide you through selecting
+   a config profile, setting up the LSP, configuring a Git hook, etc.
+
+   With `configure` script, you can handle multiple configurations in a single
+   command which is useful to create complex/advanced config files. For example:
+
    ```sh
-   ./configure
+   ./configure --build=aarch64-unknown-linux-gnu \
+      --enable-full-tools \
+      --enable-profiler \
+      --enable-sanitizers \
+      --enable-compiler-docs \
+      --set target.aarch64-unknown-linux-gnu.linker=clang \
+      --set target.aarch64-unknown-linux-gnu.ar=/rustroot/bin/llvm-ar \
+      --set target.aarch64-unknown-linux-gnu.ranlib=/rustroot/bin/llvm-ranlib \
+      --set llvm.link-shared=true \
+      --set llvm.thin-lto=true \
+      --set llvm.libzstd=true \
+      --set llvm.ninja=false \
+      --set rust.debug-assertions=false \
+      --set rust.jemalloc \
+      --set rust.use-lld=true \
+      --set rust.lto=thin \
+      --set rust.codegen-units=1
    ```
 
    If you plan to use `x.py install` to create an installation, you can either
diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index 1532ca77f71..8986430141b 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -1927,7 +1927,7 @@ impl AttrArgs {
 }
 
 /// Delimited arguments, as used in `#[attr()/[]/{}]` or `mac!()/[]/{}`.
-#[derive(Clone, Encodable, Decodable, Debug)]
+#[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
 pub struct DelimArgs {
     pub dspan: DelimSpan,
     pub delim: Delimiter, // Note: `Delimiter::Invisible` never occurs
@@ -1942,18 +1942,6 @@ impl DelimArgs {
     }
 }
 
-impl<CTX> HashStable<CTX> for DelimArgs
-where
-    CTX: crate::HashStableContext,
-{
-    fn hash_stable(&self, ctx: &mut CTX, hasher: &mut StableHasher) {
-        let DelimArgs { dspan, delim, tokens } = self;
-        dspan.hash_stable(ctx, hasher);
-        delim.hash_stable(ctx, hasher);
-        tokens.hash_stable(ctx, hasher);
-    }
-}
-
 /// Represents a macro definition.
 #[derive(Clone, Encodable, Decodable, Debug, HashStable_Generic)]
 pub struct MacroDef {
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 534e85e8bcb..403b4dc6b53 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -916,7 +916,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
     }
 
     fn lower_delim_args(&self, args: &DelimArgs) -> DelimArgs {
-        DelimArgs { dspan: args.dspan, delim: args.delim, tokens: args.tokens.clone() }
+        args.clone()
     }
 
     /// Lower an associated item constraint.
diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs
index 63597b37cb5..55c3df003fe 100644
--- a/compiler/rustc_attr_parsing/src/context.rs
+++ b/compiler/rustc_attr_parsing/src/context.rs
@@ -3,7 +3,7 @@ use std::collections::BTreeMap;
 use std::ops::Deref;
 use std::sync::LazyLock;
 
-use rustc_ast::{self as ast, DelimArgs};
+use rustc_ast as ast;
 use rustc_attr_data_structures::AttributeKind;
 use rustc_errors::{DiagCtxtHandle, Diagnostic};
 use rustc_feature::Features;
@@ -315,11 +315,7 @@ impl<'sess> AttributeParser<'sess> {
     fn lower_attr_args(&self, args: &ast::AttrArgs, lower_span: impl Fn(Span) -> Span) -> AttrArgs {
         match args {
             ast::AttrArgs::Empty => AttrArgs::Empty,
-            ast::AttrArgs::Delimited(args) => AttrArgs::Delimited(DelimArgs {
-                dspan: args.dspan,
-                delim: args.delim,
-                tokens: args.tokens.clone(),
-            }),
+            ast::AttrArgs::Delimited(args) => AttrArgs::Delimited(args.clone()),
             // This is an inert key-value attribute - it will never be visible to macros
             // after it gets lowered to HIR. Therefore, we can extract literals to handle
             // nonterminals in `#[doc]` (e.g. `#[doc = $e]`).
diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs
index b19d9efe2c6..caf36ba47bd 100644
--- a/compiler/rustc_hir_typeck/src/cast.rs
+++ b/compiler/rustc_hir_typeck/src/cast.rs
@@ -501,12 +501,25 @@ impl<'a, 'tcx> CastCheck<'tcx> {
                             .must_apply_modulo_regions()
                         {
                             label = false;
-                            err.span_suggestion(
-                                self.span,
-                                "consider using the `From` trait instead",
-                                format!("{}::from({})", self.cast_ty, snippet),
-                                Applicability::MaybeIncorrect,
-                            );
+                            if let ty::Adt(def, args) = self.cast_ty.kind() {
+                                err.span_suggestion_verbose(
+                                    self.span,
+                                    "consider using the `From` trait instead",
+                                    format!(
+                                        "{}::from({})",
+                                        fcx.tcx.value_path_str_with_args(def.did(), args),
+                                        snippet
+                                    ),
+                                    Applicability::MaybeIncorrect,
+                                );
+                            } else {
+                                err.span_suggestion(
+                                    self.span,
+                                    "consider using the `From` trait instead",
+                                    format!("{}::from({})", self.cast_ty, snippet),
+                                    Applicability::MaybeIncorrect,
+                                );
+                            };
                         }
                     }
 
diff --git a/library/std/src/sys/pal/unix/sync/condvar.rs b/library/std/src/sys/pal/unix/sync/condvar.rs
index 73631053e9f..efa6f8d7765 100644
--- a/library/std/src/sys/pal/unix/sync/condvar.rs
+++ b/library/std/src/sys/pal/unix/sync/condvar.rs
@@ -64,7 +64,10 @@ impl Condvar {
         // https://gist.github.com/stepancheg/198db4623a20aad2ad7cddb8fda4a63c
         //
         // To work around this issue, the timeout is clamped to 1000 years.
-        #[cfg(target_vendor = "apple")]
+        //
+        // Cygwin implementation is based on NT API and a super large timeout
+        // makes the syscall block forever.
+        #[cfg(any(target_vendor = "apple", target_os = "cygwin"))]
         let dur = Duration::min(dur, Duration::from_secs(1000 * 365 * 86400));
 
         let timeout = Timespec::now(Self::CLOCK).checked_add_duration(&dur);
diff --git a/src/ci/citool/src/jobs.rs b/src/ci/citool/src/jobs.rs
index 13880ad466a..5600d7b4db5 100644
--- a/src/ci/citool/src/jobs.rs
+++ b/src/ci/citool/src/jobs.rs
@@ -3,12 +3,15 @@ mod tests;
 
 use std::collections::BTreeMap;
 
+use anyhow::Context as _;
 use serde_yaml::Value;
 
 use crate::GitHubContext;
+use crate::utils::load_env_var;
 
 /// Representation of a job loaded from the `src/ci/github-actions/jobs.yml` file.
 #[derive(serde::Deserialize, Debug, Clone)]
+#[serde(deny_unknown_fields)]
 pub struct Job {
     /// Name of the job, e.g. mingw-check
     pub name: String,
@@ -26,6 +29,8 @@ pub struct Job {
     pub free_disk: Option<bool>,
     /// Documentation link to a resource that could help people debug this CI job.
     pub doc_url: Option<String>,
+    /// Whether the job is executed on AWS CodeBuild.
+    pub codebuild: Option<bool>,
 }
 
 impl Job {
@@ -80,7 +85,7 @@ impl JobDatabase {
 }
 
 pub fn load_job_db(db: &str) -> anyhow::Result<JobDatabase> {
-    let mut db: Value = serde_yaml::from_str(&db)?;
+    let mut db: Value = serde_yaml::from_str(db)?;
 
     // We need to expand merge keys (<<), because serde_yaml can't deal with them
     // `apply_merge` only applies the merge once, so do it a few times to unwrap nested merges.
@@ -107,6 +112,29 @@ struct GithubActionsJob {
     free_disk: Option<bool>,
     #[serde(skip_serializing_if = "Option::is_none")]
     doc_url: Option<String>,
+    #[serde(skip_serializing_if = "Option::is_none")]
+    codebuild: Option<bool>,
+}
+
+/// Replace GitHub context variables with environment variables in job configs.
+/// Used for codebuild jobs like
+/// `codebuild-ubuntu-22-8c-$github.run_id-$github.run_attempt`
+fn substitute_github_vars(jobs: Vec<Job>) -> anyhow::Result<Vec<Job>> {
+    let run_id = load_env_var("GITHUB_RUN_ID")?;
+    let run_attempt = load_env_var("GITHUB_RUN_ATTEMPT")?;
+
+    let jobs = jobs
+        .into_iter()
+        .map(|mut job| {
+            job.os = job
+                .os
+                .replace("$github.run_id", &run_id)
+                .replace("$github.run_attempt", &run_attempt);
+            job
+        })
+        .collect();
+
+    Ok(jobs)
 }
 
 /// Skip CI jobs that are not supposed to be executed on the given `channel`.
@@ -177,6 +205,8 @@ fn calculate_jobs(
         }
         RunType::AutoJob => (db.auto_jobs.clone(), "auto", &db.envs.auto_env),
     };
+    let jobs = substitute_github_vars(jobs.clone())
+        .context("Failed to substitute GitHub context variables in jobs")?;
     let jobs = skip_jobs(jobs, channel);
     let jobs = jobs
         .into_iter()
@@ -207,6 +237,7 @@ fn calculate_jobs(
                 continue_on_error: job.continue_on_error,
                 free_disk: job.free_disk,
                 doc_url: job.doc_url,
+                codebuild: job.codebuild,
             }
         })
         .collect();
diff --git a/src/ci/docker/host-x86_64/dist-arm-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-arm-linux/Dockerfile
index 420c42bc9d8..3795859f308 100644
--- a/src/ci/docker/host-x86_64/dist-arm-linux/Dockerfile
+++ b/src/ci/docker/host-x86_64/dist-arm-linux/Dockerfile
@@ -1,4 +1,4 @@
-FROM ubuntu:22.04
+FROM ghcr.io/rust-lang/ubuntu:22.04
 
 COPY scripts/cross-apt-packages.sh /scripts/
 RUN sh /scripts/cross-apt-packages.sh
diff --git a/src/ci/docker/run.sh b/src/ci/docker/run.sh
index 00d791eeb6b..36f7df2b069 100755
--- a/src/ci/docker/run.sh
+++ b/src/ci/docker/run.sh
@@ -288,7 +288,7 @@ args="$args --privileged"
 # `LOCAL_USER_ID` (recognized in `src/ci/run.sh`) to ensure that files are all
 # read/written as the same user as the bare-metal user.
 if [ -f /.dockerenv ]; then
-  docker create -v /checkout --name checkout alpine:3.4 /bin/true
+  docker create -v /checkout --name checkout ghcr.io/rust-lang/alpine:3.4 /bin/true
   docker cp . checkout:/checkout
   args="$args --volumes-from checkout"
 else
diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml
index 950a75721c4..88b29d2df56 100644
--- a/src/ci/github-actions/jobs.yml
+++ b/src/ci/github-actions/jobs.yml
@@ -56,6 +56,15 @@ runners:
   - &job-aarch64-linux-8c
     os: ubuntu-24.04-arm64-8core-32gb
     <<: *base-job
+
+  # Codebuild runners are provisioned in
+  # https://github.com/rust-lang/simpleinfra/blob/b7ddd5e6bec8a93ec30510cdddec02c5666fefe9/terragrunt/accounts/ci-prod/ci-runners/terragrunt.hcl#L2
+  - &job-linux-36c-codebuild
+    free_disk: true
+    codebuild: true
+    os: codebuild-ubuntu-22-36c-$github.run_id-$github.run_attempt
+    <<: *base-job
+
 envs:
   env-x86_64-apple-tests: &env-x86_64-apple-tests
     SCRIPT: ./x.py check compiletest --set build.compiletest-use-stage0-libtest=true && ./x.py --stage 2 test --skip tests/ui --skip tests/rustdoc -- --exact
@@ -151,7 +160,7 @@ auto:
     <<: *job-linux-4c
 
   - name: dist-arm-linux
-    <<: *job-linux-8c
+    <<: *job-linux-36c-codebuild
 
   - name: dist-armhf-linux
     <<: *job-linux-4c
diff --git a/src/ci/scripts/free-disk-space.sh b/src/ci/scripts/free-disk-space.sh
index 055a6ac2211..ad7ee136e9c 100755
--- a/src/ci/scripts/free-disk-space.sh
+++ b/src/ci/scripts/free-disk-space.sh
@@ -14,6 +14,17 @@ isX86() {
     fi
 }
 
+# Check if we're on a GitHub hosted runner.
+# In aws codebuild, the variable RUNNER_ENVIRONMENT is "self-hosted".
+isGitHubRunner() {
+    # `:-` means "use the value of RUNNER_ENVIRONMENT if it exists, otherwise use an empty string".
+    if [[ "${RUNNER_ENVIRONMENT:-}" == "github-hosted" ]]; then
+        return 0
+    else
+        return 1
+    fi
+}
+
 # print a line of the specified character
 printSeparationLine() {
     for ((i = 0; i < 80; i++)); do
@@ -32,7 +43,7 @@ getAvailableSpace() {
 # make Kb human readable (assume the input is Kb)
 # REF: https://unix.stackexchange.com/a/44087/60849
 formatByteCount() {
-    numfmt --to=iec-i --suffix=B --padding=7 "$1"'000'
+    numfmt --to=iec-i --suffix=B --padding=7 "${1}000"
 }
 
 # macro to output saved space
@@ -45,6 +56,11 @@ printSavedSpace() {
     after=$(getAvailableSpace)
     local saved=$((after - before))
 
+    if [ "$saved" -lt 0 ]; then
+        echo "::warning::Saved space is negative: $saved. Using '0' as saved space."
+        saved=0
+    fi
+
     echo ""
     printSeparationLine "*"
     if [ -n "${title}" ]; then
@@ -118,10 +134,14 @@ removeUnusedFilesAndDirs() {
         # Azure
         "/opt/az"
         "/usr/share/az_"*
+    )
 
+    if [ -n "${AGENT_TOOLSDIRECTORY:-}" ]; then
         # Environment variable set by GitHub Actions
-        "$AGENT_TOOLSDIRECTORY"
-    )
+        to_remove+=(
+            "${AGENT_TOOLSDIRECTORY}"
+        )
+    fi
 
     for element in "${to_remove[@]}"; do
         if [ ! -e "$element" ]; then
@@ -155,20 +175,25 @@ cleanPackages() {
         '^dotnet-.*'
         '^llvm-.*'
         '^mongodb-.*'
-        'azure-cli'
         'firefox'
         'libgl1-mesa-dri'
         'mono-devel'
         'php.*'
     )
 
-    if isX86; then
+    if isGitHubRunner; then
         packages+=(
-            'google-chrome-stable'
-            'google-cloud-cli'
-            'google-cloud-sdk'
-            'powershell'
+            azure-cli
         )
+
+        if isX86; then
+            packages+=(
+                'google-chrome-stable'
+                'google-cloud-cli'
+                'google-cloud-sdk'
+                'powershell'
+            )
+        fi
     fi
 
     sudo apt-get -qq remove -y --fix-missing "${packages[@]}"
diff --git a/src/doc/rustdoc/src/command-line-arguments.md b/src/doc/rustdoc/src/command-line-arguments.md
index 872592d669d..b55ddf6e0e1 100644
--- a/src/doc/rustdoc/src/command-line-arguments.md
+++ b/src/doc/rustdoc/src/command-line-arguments.md
@@ -222,6 +222,28 @@ For more, see [the chapter on documentation tests](write-documentation/documenta
 
 See also `--test`.
 
+## `--test-runtool`, `--test-runtool-arg`: program to run tests with; args to pass to it
+
+A doctest wrapper program can be specified with the `--test-runtool` flag.
+Rustdoc will execute that wrapper instead of the doctest executable when
+running tests. The first arguments to the wrapper will be any arguments
+specified with the `--test-runtool-arg` flag, followed by the path to the
+doctest executable to run.
+
+Using these options looks like this:
+
+```bash
+$ rustdoc src/lib.rs --test-runtool path/to/runner --test-runtool-arg --do-thing --test-runtool-arg --do-other-thing
+```
+
+For example, if you want to run your doctests under valgrind you might run:
+
+```bash
+$ rustdoc src/lib.rs --test-runtool valgrind
+```
+
+Another use case would be to run a test inside an emulator, or through a Virtual Machine.
+
 ## `--target`: generate documentation for the specified target triple
 
 Using this flag looks like this:
diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md
index d4ff69a9933..69e5a5adbec 100644
--- a/src/doc/rustdoc/src/unstable-features.md
+++ b/src/doc/rustdoc/src/unstable-features.md
@@ -631,60 +631,6 @@ The generated output (formatted) will look like this:
 `--output-format html` has no effect, as the default output is HTML. This is
 accepted on stable, even though the other options for this flag aren't.
 
-## `--enable-per-target-ignores`: allow `ignore-foo` style filters for doctests
-
- * Tracking issue: [#64245](https://github.com/rust-lang/rust/issues/64245)
-
-Using this flag looks like this:
-
-```bash
-$ rustdoc src/lib.rs -Z unstable-options --enable-per-target-ignores
-```
-
-This flag allows you to tag doctests with compiletest style `ignore-foo` filters that prevent
-rustdoc from running that test if the target triple string contains foo. For example:
-
-```rust
-///```ignore-foo,ignore-bar
-///assert!(2 == 2);
-///```
-struct Foo;
-```
-
-This will not be run when the build target is `super-awesome-foo` or `less-bar-awesome`.
-If the flag is not enabled, then rustdoc will consume the filter, but do nothing with it, and
-the above example will be run for all targets.
-If you want to preserve backwards compatibility for older versions of rustdoc, you can use
-
-```rust
-///```ignore,ignore-foo
-///assert!(2 == 2);
-///```
-struct Foo;
-```
-
-In older versions, this will be ignored on all targets, but on newer versions `ignore-gnu` will
-override `ignore`.
-
-## `--runtool`, `--runtool-arg`: program to run tests with; args to pass to it
-
- * Tracking issue: [#64245](https://github.com/rust-lang/rust/issues/64245)
-
-Using these options looks like this:
-
-```bash
-$ rustdoc src/lib.rs -Z unstable-options --runtool runner --runtool-arg --do-thing --runtool-arg --do-other-thing
-```
-
-These options can be used to run the doctest under a program, and also pass arguments to
-that program. For example, if you want to run your doctests under valgrind you might run
-
-```bash
-$ rustdoc src/lib.rs -Z unstable-options --runtool valgrind
-```
-
-Another use case would be to run a test inside an emulator, or through a Virtual Machine.
-
 ## `--with-examples`: include examples of uses of items as documentation
 
  * Tracking issue: [#88791](https://github.com/rust-lang/rust/issues/88791)
diff --git a/src/doc/rustdoc/src/write-documentation/documentation-tests.md b/src/doc/rustdoc/src/write-documentation/documentation-tests.md
index b921f677857..077b02d603d 100644
--- a/src/doc/rustdoc/src/write-documentation/documentation-tests.md
+++ b/src/doc/rustdoc/src/write-documentation/documentation-tests.md
@@ -427,6 +427,43 @@ should not be merged with the others. So the previous code should use it:
 In this case, it means that the line information will not change if you add/remove other
 doctests.
 
+### Ignoring targets
+
+Attributes starting with `ignore-` can be used to ignore doctests for specific
+targets. For example, `ignore-x86_64` will avoid building doctests when the
+target name contains `x86_64`.
+
+```rust
+/// ```ignore-x86_64
+/// assert!(2 == 2);
+/// ```
+struct Foo;
+```
+
+This doctest will not be built for targets such as `x86_64-unknown-linux-gnu`.
+
+Multiple ignore attributes can be specified to ignore multiple targets:
+
+```rust
+/// ```ignore-x86_64,ignore-windows
+/// assert!(2 == 2);
+/// ```
+struct Foo;
+```
+
+If you want to preserve backwards compatibility for older versions of rustdoc,
+you can specify both `ignore` and `ignore-`, such as:
+
+```rust
+/// ```ignore,ignore-x86_64
+/// assert!(2 == 2);
+/// ```
+struct Foo;
+```
+
+In older versions, this will be ignored on all targets, but starting with
+version CURRENT_RUSTC_VERSION, `ignore-x86_64` will override `ignore`.
+
 ### Custom CSS classes for code blocks
 
 ```rust
diff --git a/src/librustdoc/config.rs b/src/librustdoc/config.rs
index 23a2bcd9011..4ef73ff48ed 100644
--- a/src/librustdoc/config.rs
+++ b/src/librustdoc/config.rs
@@ -124,13 +124,9 @@ pub(crate) struct Options {
     /// temporary directory if not set.
     pub(crate) persist_doctests: Option<PathBuf>,
     /// Runtool to run doctests with
-    pub(crate) runtool: Option<String>,
+    pub(crate) test_runtool: Option<String>,
     /// Arguments to pass to the runtool
-    pub(crate) runtool_args: Vec<String>,
-    /// Whether to allow ignoring doctests on a per-target basis
-    /// For example, using ignore-foo to ignore running the doctest on any target that
-    /// contains "foo" as a substring
-    pub(crate) enable_per_target_ignores: bool,
+    pub(crate) test_runtool_args: Vec<String>,
     /// Do not run doctests, compile them if should_test is active.
     pub(crate) no_run: bool,
     /// What sources are being mapped.
@@ -215,9 +211,8 @@ impl fmt::Debug for Options {
             .field("persist_doctests", &self.persist_doctests)
             .field("show_coverage", &self.show_coverage)
             .field("crate_version", &self.crate_version)
-            .field("runtool", &self.runtool)
-            .field("runtool_args", &self.runtool_args)
-            .field("enable-per-target-ignores", &self.enable_per_target_ignores)
+            .field("test_runtool", &self.test_runtool)
+            .field("test_runtool_args", &self.test_runtool_args)
             .field("run_check", &self.run_check)
             .field("no_run", &self.no_run)
             .field("test_builder_wrappers", &self.test_builder_wrappers)
@@ -779,9 +774,8 @@ impl Options {
         let unstable_opts_strs = matches.opt_strs("Z");
         let lib_strs = matches.opt_strs("L");
         let extern_strs = matches.opt_strs("extern");
-        let runtool = matches.opt_str("runtool");
-        let runtool_args = matches.opt_strs("runtool-arg");
-        let enable_per_target_ignores = matches.opt_present("enable-per-target-ignores");
+        let test_runtool = matches.opt_str("test-runtool");
+        let test_runtool_args = matches.opt_strs("test-runtool-arg");
         let document_private = matches.opt_present("document-private-items");
         let document_hidden = matches.opt_present("document-hidden-items");
         let run_check = matches.opt_present("check");
@@ -843,9 +837,8 @@ impl Options {
             crate_version,
             test_run_directory,
             persist_doctests,
-            runtool,
-            runtool_args,
-            enable_per_target_ignores,
+            test_runtool,
+            test_runtool_args,
             test_builder,
             run_check,
             no_run,
diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs
index 88eaa52c6de..829a9ca6e7d 100644
--- a/src/librustdoc/doctest.rs
+++ b/src/librustdoc/doctest.rs
@@ -219,11 +219,9 @@ pub(crate) fn run(dcx: DiagCtxtHandle<'_>, input: Input, options: RustdocOptions
             let crate_name = tcx.crate_name(LOCAL_CRATE).to_string();
             let crate_attrs = tcx.hir_attrs(CRATE_HIR_ID);
             let opts = scrape_test_config(crate_name, crate_attrs, args_path);
-            let enable_per_target_ignores = options.enable_per_target_ignores;
 
             let hir_collector = HirCollector::new(
                 ErrorCodes::from(compiler.sess.opts.unstable_features.is_nightly_build()),
-                enable_per_target_ignores,
                 tcx,
             );
             let tests = hir_collector.collect_crate();
@@ -782,10 +780,10 @@ fn run_test(
     let mut cmd;
 
     let output_file = make_maybe_absolute_path(output_file);
-    if let Some(tool) = &rustdoc_options.runtool {
+    if let Some(tool) = &rustdoc_options.test_runtool {
         let tool = make_maybe_absolute_path(tool.into());
         cmd = Command::new(tool);
-        cmd.args(&rustdoc_options.runtool_args);
+        cmd.args(&rustdoc_options.test_runtool_args);
         cmd.arg(&output_file);
     } else {
         cmd = Command::new(&output_file);
diff --git a/src/librustdoc/doctest/markdown.rs b/src/librustdoc/doctest/markdown.rs
index a0d39ce749d..497a8d7c4a7 100644
--- a/src/librustdoc/doctest/markdown.rs
+++ b/src/librustdoc/doctest/markdown.rs
@@ -104,13 +104,7 @@ pub(crate) fn test(input: &Input, options: Options) -> Result<(), String> {
     };
     let codes = ErrorCodes::from(options.unstable_features.is_nightly_build());
 
-    find_testable_code(
-        &input_str,
-        &mut md_collector,
-        codes,
-        options.enable_per_target_ignores,
-        None,
-    );
+    find_testable_code(&input_str, &mut md_collector, codes, None);
 
     let mut collector = CreateRunnableDocTests::new(options.clone(), opts);
     md_collector.tests.into_iter().for_each(|t| collector.add_test(t));
diff --git a/src/librustdoc/doctest/rust.rs b/src/librustdoc/doctest/rust.rs
index be2dea641d7..43dcfab880b 100644
--- a/src/librustdoc/doctest/rust.rs
+++ b/src/librustdoc/doctest/rust.rs
@@ -63,19 +63,18 @@ impl DocTestVisitor for RustCollector {
 pub(super) struct HirCollector<'tcx> {
     codes: ErrorCodes,
     tcx: TyCtxt<'tcx>,
-    enable_per_target_ignores: bool,
     collector: RustCollector,
 }
 
 impl<'tcx> HirCollector<'tcx> {
-    pub fn new(codes: ErrorCodes, enable_per_target_ignores: bool, tcx: TyCtxt<'tcx>) -> Self {
+    pub fn new(codes: ErrorCodes, tcx: TyCtxt<'tcx>) -> Self {
         let collector = RustCollector {
             source_map: tcx.sess.psess.clone_source_map(),
             cur_path: vec![],
             position: DUMMY_SP,
             tests: vec![],
         };
-        Self { codes, enable_per_target_ignores, tcx, collector }
+        Self { codes, tcx, collector }
     }
 
     pub fn collect_crate(mut self) -> Vec<ScrapedDocTest> {
@@ -131,7 +130,6 @@ impl HirCollector<'_> {
                 &doc,
                 &mut self.collector,
                 self.codes,
-                self.enable_per_target_ignores,
                 Some(&crate::html::markdown::ExtraInfo::new(self.tcx, def_id, span)),
             );
         }
diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs
index 44134bda5ea..fc46293e7ea 100644
--- a/src/librustdoc/html/markdown.rs
+++ b/src/librustdoc/html/markdown.rs
@@ -246,7 +246,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
             match kind {
                 CodeBlockKind::Fenced(ref lang) => {
                     let parse_result =
-                        LangString::parse_without_check(lang, self.check_error_codes, false);
+                        LangString::parse_without_check(lang, self.check_error_codes);
                     if !parse_result.rust {
                         let added_classes = parse_result.added_classes;
                         let lang_string = if let Some(lang) = parse_result.unknown.first() {
@@ -707,17 +707,15 @@ pub(crate) fn find_testable_code<T: doctest::DocTestVisitor>(
     doc: &str,
     tests: &mut T,
     error_codes: ErrorCodes,
-    enable_per_target_ignores: bool,
     extra_info: Option<&ExtraInfo<'_>>,
 ) {
-    find_codes(doc, tests, error_codes, enable_per_target_ignores, extra_info, false)
+    find_codes(doc, tests, error_codes, extra_info, false)
 }
 
 pub(crate) fn find_codes<T: doctest::DocTestVisitor>(
     doc: &str,
     tests: &mut T,
     error_codes: ErrorCodes,
-    enable_per_target_ignores: bool,
     extra_info: Option<&ExtraInfo<'_>>,
     include_non_rust: bool,
 ) {
@@ -733,12 +731,7 @@ pub(crate) fn find_codes<T: doctest::DocTestVisitor>(
                         if lang.is_empty() {
                             Default::default()
                         } else {
-                            LangString::parse(
-                                lang,
-                                error_codes,
-                                enable_per_target_ignores,
-                                extra_info,
-                            )
+                            LangString::parse(lang, error_codes, extra_info)
                         }
                     }
                     CodeBlockKind::Indented => Default::default(),
@@ -1162,18 +1155,13 @@ impl Default for LangString {
 }
 
 impl LangString {
-    fn parse_without_check(
-        string: &str,
-        allow_error_code_check: ErrorCodes,
-        enable_per_target_ignores: bool,
-    ) -> Self {
-        Self::parse(string, allow_error_code_check, enable_per_target_ignores, None)
+    fn parse_without_check(string: &str, allow_error_code_check: ErrorCodes) -> Self {
+        Self::parse(string, allow_error_code_check, None)
     }
 
     fn parse(
         string: &str,
         allow_error_code_check: ErrorCodes,
-        enable_per_target_ignores: bool,
         extra: Option<&ExtraInfo<'_>>,
     ) -> Self {
         let allow_error_code_check = allow_error_code_check.as_bool();
@@ -1203,10 +1191,8 @@ impl LangString {
                     LangStringToken::LangToken(x)
                         if let Some(ignore) = x.strip_prefix("ignore-") =>
                     {
-                        if enable_per_target_ignores {
-                            ignores.push(ignore.to_owned());
-                            seen_rust_tags = !seen_other_tags;
-                        }
+                        ignores.push(ignore.to_owned());
+                        seen_rust_tags = !seen_other_tags;
                     }
                     LangStringToken::LangToken("rust") => {
                         data.rust = true;
@@ -1968,7 +1954,7 @@ pub(crate) fn rust_code_blocks(md: &str, extra_info: &ExtraInfo<'_>) -> Vec<Rust
                     let lang_string = if syntax.is_empty() {
                         Default::default()
                     } else {
-                        LangString::parse(syntax, ErrorCodes::Yes, false, Some(extra_info))
+                        LangString::parse(syntax, ErrorCodes::Yes, Some(extra_info))
                     };
                     if !lang_string.rust {
                         continue;
diff --git a/src/librustdoc/html/markdown/tests.rs b/src/librustdoc/html/markdown/tests.rs
index bb42b877a2c..784d0c5d21e 100644
--- a/src/librustdoc/html/markdown/tests.rs
+++ b/src/librustdoc/html/markdown/tests.rs
@@ -49,7 +49,7 @@ fn test_unique_id() {
 fn test_lang_string_parse() {
     fn t(lg: LangString) {
         let s = &lg.original;
-        assert_eq!(LangString::parse(s, ErrorCodes::Yes, true, None), lg)
+        assert_eq!(LangString::parse(s, ErrorCodes::Yes, None), lg)
     }
 
     t(Default::default());
@@ -479,7 +479,7 @@ fn test_markdown_html_escape() {
 fn test_find_testable_code_line() {
     fn t(input: &str, expect: &[usize]) {
         let mut lines = Vec::<usize>::new();
-        find_testable_code(input, &mut lines, ErrorCodes::No, false, None);
+        find_testable_code(input, &mut lines, ErrorCodes::No, None);
         assert_eq!(lines, expect);
     }
 
diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs
index 7e17f09aecd..beaa6497b8c 100644
--- a/src/librustdoc/html/render/mod.rs
+++ b/src/librustdoc/html/render/mod.rs
@@ -2086,6 +2086,7 @@ fn render_impl(
                     .split_summary_and_content()
                 })
                 .unwrap_or((None, None));
+
             write!(
                 w,
                 "{}",
@@ -2097,24 +2098,19 @@ fn render_impl(
                     use_absolute,
                     aliases,
                     before_dox.as_deref(),
+                    trait_.is_none() && impl_.items.is_empty(),
                 )
             )?;
             if toggled {
                 w.write_str("</summary>")?;
             }
 
-            if before_dox.is_some() {
-                if trait_.is_none() && impl_.items.is_empty() {
-                    w.write_str(
-                        "<div class=\"item-info\">\
-                         <div class=\"stab empty-impl\">This impl block contains no items.</div>\
-                     </div>",
-                    )?;
-                }
-                if let Some(after_dox) = after_dox {
-                    write!(w, "<div class=\"docblock\">{after_dox}</div>")?;
-                }
+            if before_dox.is_some()
+                && let Some(after_dox) = after_dox
+            {
+                write!(w, "<div class=\"docblock\">{after_dox}</div>")?;
             }
+
             if !default_impl_items.is_empty() || !impl_items.is_empty() {
                 w.write_str("<div class=\"impl-items\">")?;
                 close_tags.push("</div>");
@@ -2182,6 +2178,7 @@ fn render_impl_summary(
     // in documentation pages for trait with automatic implementations like "Send" and "Sync".
     aliases: &[String],
     doc: Option<&str>,
+    impl_is_empty: bool,
 ) -> impl fmt::Display {
     fmt::from_fn(move |w| {
         let inner_impl = i.inner_impl();
@@ -2237,6 +2234,13 @@ fn render_impl_summary(
         }
 
         if let Some(doc) = doc {
+            if impl_is_empty {
+                w.write_str(
+                    "<div class=\"item-info\">\
+                         <div class=\"stab empty-impl\">This impl block contains no items.</div>\
+                     </div>",
+                )?;
+            }
             write!(w, "<div class=\"docblock\">{doc}</div>")?;
         }
 
diff --git a/src/librustdoc/html/static/css/rustdoc.css b/src/librustdoc/html/static/css/rustdoc.css
index a6dd06b76ea..19ac24a5d6e 100644
--- a/src/librustdoc/html/static/css/rustdoc.css
+++ b/src/librustdoc/html/static/css/rustdoc.css
@@ -2319,7 +2319,10 @@ details.toggle > summary:not(.hideme)::before {
 	doc block while aligning it with the impl block items. */
 .implementors-toggle > .docblock,
 /* We indent trait items as well. */
-#main-content > .methods > :not(.item-info) {
+#main-content > .methods > :not(.item-info),
+.impl > .item-info,
+.impl > .docblock,
+.impl + .docblock {
 	margin-left: var(--impl-items-indent);
 }
 
diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs
index 4fe5e13c3af..44bd96a7e45 100644
--- a/src/librustdoc/lib.rs
+++ b/src/librustdoc/lib.rs
@@ -507,28 +507,20 @@ fn opts() -> Vec<RustcOptGroup> {
             "",
         ),
         opt(
-            Unstable,
-            FlagMulti,
-            "",
-            "enable-per-target-ignores",
-            "parse ignore-foo for ignoring doctests on a per-target basis",
-            "",
-        ),
-        opt(
-            Unstable,
+            Stable,
             Opt,
             "",
-            "runtool",
+            "test-runtool",
             "",
             "The tool to run tests with when building for a different target than host",
         ),
         opt(
-            Unstable,
+            Stable,
             Multi,
             "",
-            "runtool-arg",
+            "test-runtool-arg",
             "",
-            "One (of possibly many) arguments to pass to the runtool",
+            "One argument (of possibly many) to pass to the runtool",
         ),
         opt(
             Unstable,
diff --git a/src/librustdoc/passes/calculate_doc_coverage.rs b/src/librustdoc/passes/calculate_doc_coverage.rs
index 761282bde7c..c9f0baaaa4c 100644
--- a/src/librustdoc/passes/calculate_doc_coverage.rs
+++ b/src/librustdoc/passes/calculate_doc_coverage.rs
@@ -212,7 +212,7 @@ impl DocVisitor<'_> for CoverageCalculator<'_, '_> {
                 let has_docs = !i.attrs.doc_strings.is_empty();
                 let mut tests = Tests { found_tests: 0 };
 
-                find_testable_code(&i.doc_value(), &mut tests, ErrorCodes::No, false, None);
+                find_testable_code(&i.doc_value(), &mut tests, ErrorCodes::No, None);
 
                 let has_doc_example = tests.found_tests != 0;
                 let hir_id = DocContext::as_local_hir_id(self.ctx.tcx, i.item_id).unwrap();
diff --git a/src/librustdoc/passes/check_doc_test_visibility.rs b/src/librustdoc/passes/check_doc_test_visibility.rs
index 70dbb944d4c..8028afea363 100644
--- a/src/librustdoc/passes/check_doc_test_visibility.rs
+++ b/src/librustdoc/passes/check_doc_test_visibility.rs
@@ -122,7 +122,7 @@ pub(crate) fn look_for_tests(cx: &DocContext<'_>, dox: &str, item: &Item) {
 
     let mut tests = Tests { found_tests: 0 };
 
-    find_testable_code(dox, &mut tests, ErrorCodes::No, false, None);
+    find_testable_code(dox, &mut tests, ErrorCodes::No, None);
 
     if tests.found_tests == 0 && cx.tcx.features().rustdoc_missing_doc_code_examples() {
         if should_have_doc_example(cx, item) {
diff --git a/src/tools/miri/cargo-miri/src/main.rs b/src/tools/miri/cargo-miri/src/main.rs
index 7d9f77f3752..322ef0a6c2a 100644
--- a/src/tools/miri/cargo-miri/src/main.rs
+++ b/src/tools/miri/cargo-miri/src/main.rs
@@ -53,7 +53,7 @@ fn main() {
     //     with `RustcPhase::Rustdoc`. There we perform a check-build (needed to get the expected
     //     build failures for `compile_fail` doctests) and then store a JSON file with the
     //     information needed to run this test.
-    //   - We also set `--runtool` to ourselves, which ends up in `phase_runner` with
+    //   - We also set `--test-runtool` to ourselves, which ends up in `phase_runner` with
     //     `RunnerPhase::Rustdoc`. There we parse the JSON file written in `phase_rustc` and invoke
     //     the Miri driver for interpretation.
 
diff --git a/src/tools/miri/cargo-miri/src/phases.rs b/src/tools/miri/cargo-miri/src/phases.rs
index 71ea07f3463..cb62e12413c 100644
--- a/src/tools/miri/cargo-miri/src/phases.rs
+++ b/src/tools/miri/cargo-miri/src/phases.rs
@@ -666,8 +666,8 @@ pub fn phase_rustdoc(mut args: impl Iterator<Item = String>) {
         if arg == "--extern" {
             // Patch --extern arguments to use *.rmeta files, since phase_cargo_rustc only creates stub *.rlib files.
             forward_patched_extern_arg(&mut args, &mut cmd);
-        } else if arg == "--runtool" {
-            // An existing --runtool flag indicates cargo is running in cross-target mode, which we don't support.
+        } else if arg == "--test-runtool" {
+            // An existing --test-runtool flag indicates cargo is running in cross-target mode, which we don't support.
             // Note that this is only passed when cargo is run with the unstable -Zdoctest-xcompile flag;
             // otherwise, we won't be called as rustdoc at all.
             show_error!("cross-interpreting doctests is not currently supported by Miri.");
@@ -693,8 +693,8 @@ pub fn phase_rustdoc(mut args: impl Iterator<Item = String>) {
     // to let phase_cargo_rustc know to expect that. We'll use this environment variable as a flag:
     cmd.env("MIRI_CALLED_FROM_RUSTDOC", "1");
 
-    // The `--test-builder` and `--runtool` arguments are unstable rustdoc features,
-    // which are disabled by default. We first need to enable them explicitly:
+    // The `--test-builder` is an unstable rustdoc features,
+    // which is disabled by default. We first need to enable them explicitly:
     cmd.arg("-Zunstable-options");
 
     // rustdoc needs to know the right sysroot.
@@ -705,7 +705,7 @@ pub fn phase_rustdoc(mut args: impl Iterator<Item = String>) {
     // Make rustdoc call us back.
     let cargo_miri_path = env::current_exe().expect("current executable path invalid");
     cmd.arg("--test-builder").arg(&cargo_miri_path); // invoked by forwarding most arguments
-    cmd.arg("--runtool").arg(&cargo_miri_path); // invoked with just a single path argument
+    cmd.arg("--test-runtool").arg(&cargo_miri_path); // invoked with just a single path argument
 
     debug_cmd("[cargo-miri rustdoc]", verbose, &cmd);
     exec(cmd)
diff --git a/tests/run-make/c-link-to-rust-dylib/rmake.rs b/tests/run-make/c-link-to-rust-dylib/rmake.rs
index ab9aa445402..3a48af8a366 100644
--- a/tests/run-make/c-link-to-rust-dylib/rmake.rs
+++ b/tests/run-make/c-link-to-rust-dylib/rmake.rs
@@ -23,7 +23,11 @@ fn main() {
         if path.is_file()
             && path.extension().is_some_and(|ext| ext == expected_extension)
             && path.file_name().and_then(|name| name.to_str()).is_some_and(|name| {
-                name.ends_with(".so") || name.ends_with(".dll") || name.ends_with(".dylib")
+                if cfg!(target_os = "aix") {
+                    name.ends_with(".a")
+                } else {
+                    name.ends_with(".so") || name.ends_with(".dll") || name.ends_with(".dylib")
+                }
             })
         {
             rfs::remove_file(path);
diff --git a/tests/run-make/doctests-runtool/rmake.rs b/tests/run-make/doctests-runtool/rmake.rs
index c7be829c215..817001c514b 100644
--- a/tests/run-make/doctests-runtool/rmake.rs
+++ b/tests/run-make/doctests-runtool/rmake.rs
@@ -1,4 +1,4 @@
-// Tests behavior of rustdoc `--runtool`.
+// Tests behavior of rustdoc `--test-runtool`.
 
 use std::path::PathBuf;
 
@@ -11,7 +11,7 @@ fn mkdir(name: &str) -> PathBuf {
     dir
 }
 
-// Behavior with --runtool with relative paths and --test-run-directory.
+// Behavior with --test-runtool with relative paths and --test-run-directory.
 fn main() {
     let run_dir_name = "rundir";
     let run_dir = mkdir(run_dir_name);
@@ -27,7 +27,7 @@ fn main() {
         .arg("--test")
         .arg("--test-run-directory")
         .arg(run_dir_name)
-        .arg("--runtool")
+        .arg("--test-runtool")
         .arg(&run_tool_binary)
         .extern_("t", "libt.rlib")
         .run();
diff --git a/tests/run-make/rustdoc-default-output/output-default.stdout b/tests/run-make/rustdoc-default-output/output-default.stdout
index 01f470f6e16..563f8ec50cd 100644
--- a/tests/run-make/rustdoc-default-output/output-default.stdout
+++ b/tests/run-make/rustdoc-default-output/output-default.stdout
@@ -138,12 +138,9 @@ Options:
         --show-coverage 
                         calculate percentage of public items with
                         documentation
-        --enable-per-target-ignores 
-                        parse ignore-foo for ignoring doctests on a per-target
-                        basis
-        --runtool The tool to run tests with when building for a different target than host
+        --test-runtool The tool to run tests with when building for a different target than host
                         
-        --runtool-arg One (of possibly many) arguments to pass to the runtool
+        --test-runtool-arg One argument (of possibly many) to pass to the runtool
                         
         --test-builder PATH
                         The rustc-like binary to use as the test builder
diff --git a/tests/rustdoc-gui/docblock-table-overflow.goml b/tests/rustdoc-gui/docblock-table-overflow.goml
index 18e5b4d7f35..e603c3a4d22 100644
--- a/tests/rustdoc-gui/docblock-table-overflow.goml
+++ b/tests/rustdoc-gui/docblock-table-overflow.goml
@@ -11,7 +11,7 @@ assert-property: (".top-doc .docblock table", {"scrollWidth": "1572"})
 // Checking it works on other doc blocks as well...
 
 // Logically, the ".docblock" and the "<p>" should have the same scroll width (if we exclude the margin).
-assert-property: ("#implementations-list > details .docblock", {"scrollWidth": 816})
+assert-property: ("#implementations-list > details .docblock", {"scrollWidth": 835})
 assert-property: ("#implementations-list > details .docblock > p", {"scrollWidth": 835})
 // However, since there is overflow in the <table>, its scroll width is bigger.
 assert-property: ("#implementations-list > details .docblock table", {"scrollWidth": "1572"})
diff --git a/tests/rustdoc-gui/impl-doc-indent.goml b/tests/rustdoc-gui/impl-doc-indent.goml
new file mode 100644
index 00000000000..d647fec6d73
--- /dev/null
+++ b/tests/rustdoc-gui/impl-doc-indent.goml
@@ -0,0 +1,16 @@
+// Checks the impl block docs have the correct indent.
+go-to: "file://" + |DOC_PATH| + "/test_docs/impls_indent/struct.Context.html"
+
+// First we ensure that the impl items are indent (more on the right of the screen) than the
+// impl itself.
+store-position: ("#impl-Context", {"x": impl_x})
+store-position: ("#impl-Context > .item-info", {"x": impl_item_x})
+assert: |impl_x| < |impl_item_x|
+
+// And we ensure that all impl items have the same indent.
+assert-position: ("#impl-Context > .docblock", {"x": |impl_item_x|})
+assert-position: ("#impl-Context + .docblock", {"x": |impl_item_x|})
+
+// Same with the collapsible impl block.
+assert-position: ("#impl-Context-1 > .docblock", {"x": |impl_item_x|})
+assert-position: (".implementors-toggle > summary + .docblock", {"x": |impl_item_x|})
diff --git a/tests/rustdoc-gui/item-info-overflow.goml b/tests/rustdoc-gui/item-info-overflow.goml
index c325beb6d06..2c4e06e297c 100644
--- a/tests/rustdoc-gui/item-info-overflow.goml
+++ b/tests/rustdoc-gui/item-info-overflow.goml
@@ -21,7 +21,7 @@ compare-elements-property: (
 )
 assert-property: (
     "#impl-SimpleTrait-for-LongItemInfo2 .item-info",
-    {"scrollWidth": "916"},
+    {"scrollWidth": "935"},
 )
 // Just to be sure we're comparing the correct "item-info":
 assert-text: (
diff --git a/tests/rustdoc-gui/src/test_docs/lib.rs b/tests/rustdoc-gui/src/test_docs/lib.rs
index 31f6b7f09b7..bb0015b8f9c 100644
--- a/tests/rustdoc-gui/src/test_docs/lib.rs
+++ b/tests/rustdoc-gui/src/test_docs/lib.rs
@@ -740,3 +740,29 @@ pub mod SidebarSort {
     impl Sort for Cell<u8> {}
     impl<'a> Sort for &'a str {}
 }
+
+pub mod impls_indent {
+    pub struct Context;
+
+    /// Working with objects.
+    ///
+    /// # Safety
+    ///
+    /// Functions that take indices of locals do not check bounds on these indices;
+    /// the caller must ensure that the indices are less than the number of locals
+    /// in the current stack frame.
+    impl Context {
+    }
+
+    /// Working with objects.
+    ///
+    /// # Safety
+    ///
+    /// Functions that take indices of locals do not check bounds on these indices;
+    /// the caller must ensure that the indices are less than the number of locals
+    /// in the current stack frame.
+    impl Context {
+        /// bla
+        pub fn bar() {}
+    }
+}
diff --git a/tests/rustdoc/doctest/auxiliary/doctest-runtool.rs b/tests/rustdoc/doctest/auxiliary/doctest-runtool.rs
new file mode 100644
index 00000000000..a21ae0664fe
--- /dev/null
+++ b/tests/rustdoc/doctest/auxiliary/doctest-runtool.rs
@@ -0,0 +1,21 @@
+// For some reason on Windows, the PATH to the libstd dylib doesn't seem to
+// carry over to running the runtool.
+//@ no-prefer-dynamic
+
+use std::path::Path;
+use std::process::Command;
+
+fn main() {
+    let args: Vec<_> = std::env::args().collect();
+    eprintln!("{args:#?}");
+    assert_eq!(args.len(), 4);
+    assert_eq!(args[1], "arg1");
+    assert_eq!(args[2], "arg2 with space");
+    let path = Path::new(&args[3]);
+    let output = Command::new(path).output().unwrap();
+    // Should fail without env var.
+    assert!(!output.status.success());
+    let output = Command::new(path).env("DOCTEST_RUNTOOL_CHECK", "xyz").output().unwrap();
+    // Should pass with env var.
+    assert!(output.status.success());
+}
diff --git a/tests/rustdoc/doctest/doctest-runtool.rs b/tests/rustdoc/doctest/doctest-runtool.rs
new file mode 100644
index 00000000000..c4fb02e5228
--- /dev/null
+++ b/tests/rustdoc/doctest/doctest-runtool.rs
@@ -0,0 +1,13 @@
+// Tests that the --test-runtool argument works.
+
+//@ ignore-cross-compile
+//@ aux-bin: doctest-runtool.rs
+//@ compile-flags: --test
+//@ compile-flags: --test-runtool=auxiliary/bin/doctest-runtool
+//@ compile-flags: --test-runtool-arg=arg1 --test-runtool-arg
+//@ compile-flags: 'arg2 with space'
+
+/// ```
+/// assert_eq!(std::env::var("DOCTEST_RUNTOOL_CHECK"), Ok("xyz".to_string()));
+/// ```
+pub fn main() {}
diff --git a/tests/ui/coercion/issue-73886.stderr b/tests/ui/coercion/issue-73886.stderr
index a6f8ba65ab5..0d4c90017cf 100644
--- a/tests/ui/coercion/issue-73886.stderr
+++ b/tests/ui/coercion/issue-73886.stderr
@@ -8,9 +8,14 @@ error[E0605]: non-primitive cast: `u32` as `Option<_>`
   --> $DIR/issue-73886.rs:4:13
    |
 LL |     let _ = 7u32 as Option<_>;
-   |             ^^^^^^^^^^^^^^^^^ help: consider using the `From` trait instead: `Option<_>::from(7u32)`
+   |             ^^^^^^^^^^^^^^^^^
    |
    = note: an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
+help: consider using the `From` trait instead
+   |
+LL -     let _ = 7u32 as Option<_>;
+LL +     let _ = Option::<_>::from(7u32);
+   |
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/coercion/non-primitive-cast-135412.fixed b/tests/ui/coercion/non-primitive-cast-135412.fixed
new file mode 100644
index 00000000000..5cadc9368d5
--- /dev/null
+++ b/tests/ui/coercion/non-primitive-cast-135412.fixed
@@ -0,0 +1,10 @@
+//@ run-rustfix
+
+use std::sync::Arc;
+
+fn main() {
+    let _ = Option::<_>::from(7u32);
+    //~^ ERROR non-primitive cast: `u32` as `Option<_>`
+    let _ = Arc::<str>::from("String");
+    //~^ ERROR non-primitive cast: `&'static str` as `Arc<str>`
+}
diff --git a/tests/ui/coercion/non-primitive-cast-135412.rs b/tests/ui/coercion/non-primitive-cast-135412.rs
new file mode 100644
index 00000000000..67a3ef340d2
--- /dev/null
+++ b/tests/ui/coercion/non-primitive-cast-135412.rs
@@ -0,0 +1,10 @@
+//@ run-rustfix
+
+use std::sync::Arc;
+
+fn main() {
+    let _ = 7u32 as Option<_>;
+    //~^ ERROR non-primitive cast: `u32` as `Option<_>`
+    let _ = "String" as Arc<str>;
+    //~^ ERROR non-primitive cast: `&'static str` as `Arc<str>`
+}
diff --git a/tests/ui/coercion/non-primitive-cast-135412.stderr b/tests/ui/coercion/non-primitive-cast-135412.stderr
new file mode 100644
index 00000000000..7e5861f83e9
--- /dev/null
+++ b/tests/ui/coercion/non-primitive-cast-135412.stderr
@@ -0,0 +1,29 @@
+error[E0605]: non-primitive cast: `u32` as `Option<_>`
+  --> $DIR/non-primitive-cast-135412.rs:6:13
+   |
+LL |     let _ = 7u32 as Option<_>;
+   |             ^^^^^^^^^^^^^^^^^
+   |
+   = note: an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
+help: consider using the `From` trait instead
+   |
+LL -     let _ = 7u32 as Option<_>;
+LL +     let _ = Option::<_>::from(7u32);
+   |
+
+error[E0605]: non-primitive cast: `&'static str` as `Arc<str>`
+  --> $DIR/non-primitive-cast-135412.rs:8:13
+   |
+LL |     let _ = "String" as Arc<str>;
+   |             ^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
+help: consider using the `From` trait instead
+   |
+LL -     let _ = "String" as Arc<str>;
+LL +     let _ = Arc::<str>::from("String");
+   |
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0605`.