about summary refs log tree commit diff
path: root/src
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 /src
parent862156d6f25ccb0d915d2a0c8ebab520d05e4f72 (diff)
parentbc25dc23765b225197c62470fc70da0882220a12 (diff)
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
Diffstat (limited to 'src')
-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
21 files changed, 192 insertions, 154 deletions
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)