about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorThe Miri Cronjob Bot <miri@cron.bot>2025-04-26 04:59:48 +0000
committerThe Miri Cronjob Bot <miri@cron.bot>2025-04-26 04:59:48 +0000
commit6c2fa0bce7c2673e6ef1a6776fcc279fa3342b72 (patch)
tree810a1c5fc6bda4807d26ad0d0059ebf71f1ccd37 /src
parent5980c8ca18aaa976af672825a8bf172abfd782a3 (diff)
parentdfa972e4548d7719d50540af9661c42dd9085b30 (diff)
downloadrust-6c2fa0bce7c2673e6ef1a6776fcc279fa3342b72.tar.gz
rust-6c2fa0bce7c2673e6ef1a6776fcc279fa3342b72.zip
Merge from rustc
Diffstat (limited to 'src')
-rw-r--r--src/bootstrap/src/core/build_steps/dist.rs23
-rw-r--r--src/bootstrap/src/core/builder/tests.rs32
-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/read-documentation/search.md2
-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/doc/unstable-book/src/library-features/concat-idents.md3
m---------src/gcc0
-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/compiletest/src/common.rs11
-rw-r--r--src/tools/compiletest/src/lib.rs7
-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--src/tools/tidy/src/gcc_submodule.rs47
-rw-r--r--src/tools/tidy/src/lib.rs1
-rw-r--r--src/tools/tidy/src/main.rs1
31 files changed, 302 insertions, 171 deletions
diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs
index ed90ede7936..3c412683b94 100644
--- a/src/bootstrap/src/core/build_steps/dist.rs
+++ b/src/bootstrap/src/core/build_steps/dist.rs
@@ -421,13 +421,13 @@ impl Step for Rustc {
                 builder.install(&rustdoc, &image.join("bin"), FileType::Executable);
             }
 
+            let ra_proc_macro_srv_compiler =
+                builder.compiler_for(compiler.stage, builder.config.build, compiler.host);
+            builder.ensure(compile::Rustc::new(ra_proc_macro_srv_compiler, compiler.host));
+
             if let Some(ra_proc_macro_srv) = builder.ensure_if_default(
                 tool::RustAnalyzerProcMacroSrv {
-                    compiler: builder.compiler_for(
-                        compiler.stage,
-                        builder.config.build,
-                        compiler.host,
-                    ),
+                    compiler: ra_proc_macro_srv_compiler,
                     target: compiler.host,
                 },
                 builder.kind,
@@ -1178,6 +1178,8 @@ impl Step for Cargo {
         let compiler = self.compiler;
         let target = self.target;
 
+        builder.ensure(compile::Rustc::new(compiler, target));
+
         let cargo = builder.ensure(tool::Cargo { compiler, target });
         let src = builder.src.join("src/tools/cargo");
         let etc = src.join("src/etc");
@@ -1232,6 +1234,8 @@ impl Step for RustAnalyzer {
         let compiler = self.compiler;
         let target = self.target;
 
+        builder.ensure(compile::Rustc::new(compiler, target));
+
         let rust_analyzer = builder.ensure(tool::RustAnalyzer { compiler, target });
 
         let mut tarball = Tarball::new(builder, "rust-analyzer", &target.triple);
@@ -1274,6 +1278,8 @@ impl Step for Clippy {
         let compiler = self.compiler;
         let target = self.target;
 
+        builder.ensure(compile::Rustc::new(compiler, target));
+
         // Prepare the image directory
         // We expect clippy to build, because we've exited this step above if tool
         // state for clippy isn't testing.
@@ -1324,9 +1330,12 @@ impl Step for Miri {
         if !builder.build.unstable_features() {
             return None;
         }
+
         let compiler = self.compiler;
         let target = self.target;
 
+        builder.ensure(compile::Rustc::new(compiler, target));
+
         let miri = builder.ensure(tool::Miri { compiler, target });
         let cargomiri = builder.ensure(tool::CargoMiri { compiler, target });
 
@@ -1463,6 +1472,8 @@ impl Step for Rustfmt {
         let compiler = self.compiler;
         let target = self.target;
 
+        builder.ensure(compile::Rustc::new(compiler, target));
+
         let rustfmt = builder.ensure(tool::Rustfmt { compiler, target });
         let cargofmt = builder.ensure(tool::Cargofmt { compiler, target });
         let mut tarball = Tarball::new(builder, "rustfmt", &target.triple);
@@ -2328,6 +2339,8 @@ impl Step for LlvmBitcodeLinker {
         let compiler = self.compiler;
         let target = self.target;
 
+        builder.ensure(compile::Rustc::new(compiler, target));
+
         let llbc_linker =
             builder.ensure(tool::LlvmBitcodeLinker { compiler, target, extra_features: vec![] });
 
diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs
index 8e2c6fc52cd..51852099dc3 100644
--- a/src/bootstrap/src/core/builder/tests.rs
+++ b/src/bootstrap/src/core/builder/tests.rs
@@ -449,6 +449,7 @@ mod dist {
     use pretty_assertions::assert_eq;
 
     use super::{Config, TEST_TRIPLE_1, TEST_TRIPLE_2, TEST_TRIPLE_3, first, run_build};
+    use crate::Flags;
     use crate::core::builder::*;
 
     fn configure(host: &[&str], target: &[&str]) -> Config {
@@ -687,6 +688,37 @@ mod dist {
         );
     }
 
+    /// This also serves as an important regression test for <https://github.com/rust-lang/rust/issues/138123>
+    /// and <https://github.com/rust-lang/rust/issues/138004>.
+    #[test]
+    fn dist_all_cross() {
+        let cmd_args =
+            &["dist", "--stage", "2", "--dry-run", "--config=/does/not/exist"].map(str::to_owned);
+        let config_str = r#"
+            [rust]
+            channel = "nightly"
+
+            [build]
+            extended = true
+
+            build = "i686-unknown-haiku"
+            host = ["i686-unknown-netbsd"]
+            target = ["i686-unknown-netbsd"]
+        "#;
+        let config = Config::parse_inner(Flags::parse(cmd_args), |&_| toml::from_str(config_str));
+        let mut cache = run_build(&[], config);
+
+        // Stage 2 `compile::Rustc` should **NEVER** be cached here.
+        assert_eq!(
+            first(cache.all::<compile::Rustc>()),
+            &[
+                rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 0),
+                rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_1, stage = 1),
+                rustc!(TEST_TRIPLE_1 => TEST_TRIPLE_3, stage = 1),
+            ]
+        );
+    }
+
     #[test]
     fn build_all() {
         let build = Build::new(configure(
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/read-documentation/search.md b/src/doc/rustdoc/src/read-documentation/search.md
index bace2f5f953..5635f68b1b3 100644
--- a/src/doc/rustdoc/src/read-documentation/search.md
+++ b/src/doc/rustdoc/src/read-documentation/search.md
@@ -89,7 +89,7 @@ the standard library and functions that are included in the results list:
 
 ### Non-functions in type-based search
 Certain items that are not functions are treated as though they
-were a semantically equivelent function.
+were a semantically equivalent function.
 
 For example, struct fields are treated as though they were getter methods.
 This means that a search for `CpuidResult -> u32` will show
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/doc/unstable-book/src/library-features/concat-idents.md b/src/doc/unstable-book/src/library-features/concat-idents.md
index 4366172fb99..8a38d155e3d 100644
--- a/src/doc/unstable-book/src/library-features/concat-idents.md
+++ b/src/doc/unstable-book/src/library-features/concat-idents.md
@@ -2,7 +2,10 @@
 
 The tracking issue for this feature is: [#29599]
 
+This feature is deprecated, to be replaced by [`macro_metavar_expr_concat`].
+
 [#29599]: https://github.com/rust-lang/rust/issues/29599
+[`macro_metavar_expr_concat`]: https://github.com/rust-lang/rust/issues/124225
 
 ------------------------
 
diff --git a/src/gcc b/src/gcc
-Subproject 13cc8243226a9028bb08ab6c5e1c5fe6d533bcd
+Subproject 0ea98a1365b81f7488073512c850e8ee951a4af
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/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs
index e0132056d6c..b5bbe70c48c 100644
--- a/src/tools/compiletest/src/common.rs
+++ b/src/tools/compiletest/src/common.rs
@@ -414,13 +414,10 @@ pub struct Config {
     /// ABI tests.
     pub minicore_path: Utf8PathBuf,
 
-    /// If true, disable the "new" executor, and use the older libtest-based
-    /// executor to run tests instead. This is a temporary fallback, to make
-    /// manual comparative testing easier if bugs are found in the new executor.
-    ///
-    /// FIXME(Zalathar): Eventually remove this flag and remove the libtest
-    /// dependency.
-    pub no_new_executor: bool,
+    /// If true, run tests with the "new" executor that was written to replace
+    /// compiletest's dependency on libtest. Eventually this will become the
+    /// default, and the libtest dependency will be removed.
+    pub new_executor: bool,
 }
 
 impl Config {
diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs
index 0a3888a7d50..3cf13671ef0 100644
--- a/src/tools/compiletest/src/lib.rs
+++ b/src/tools/compiletest/src/lib.rs
@@ -202,7 +202,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
             "COMMAND",
         )
         .reqopt("", "minicore-path", "path to minicore aux library", "PATH")
-        .optflag("N", "no-new-executor", "disables the new test executor, and uses libtest instead")
+        .optflag("n", "new-executor", "enables the new test executor instead of using libtest")
         .optopt(
             "",
             "debugger",
@@ -448,7 +448,7 @@ pub fn parse_config(args: Vec<String>) -> Config {
 
         minicore_path: opt_path(matches, "minicore-path"),
 
-        no_new_executor: matches.opt_present("no-new-executor"),
+        new_executor: matches.opt_present("new-executor"),
     }
 }
 
@@ -575,10 +575,9 @@ pub fn run_tests(config: Arc<Config>) {
     // Delegate to the executor to filter and run the big list of test structures
     // created during test discovery. When the executor decides to run a test,
     // it will return control to the rest of compiletest by calling `runtest::run`.
-    let res = if !config.no_new_executor {
+    let res = if config.new_executor {
         Ok(executor::run_tests(&config, tests))
     } else {
-        // FIXME(Zalathar): Eventually remove the libtest executor entirely.
         crate::executor::libtest::execute_tests(&config, tests)
     };
 
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/src/tools/tidy/src/gcc_submodule.rs b/src/tools/tidy/src/gcc_submodule.rs
new file mode 100644
index 00000000000..952ebe9e0cf
--- /dev/null
+++ b/src/tools/tidy/src/gcc_submodule.rs
@@ -0,0 +1,47 @@
+//! Tidy check to ensure that the commit SHA of the `src/gcc` submodule is the same as the
+//! required GCC version of the GCC codegen backend.
+
+use std::path::Path;
+use std::process::Command;
+
+pub fn check(root_path: &Path, compiler_path: &Path, bad: &mut bool) {
+    let cg_gcc_version_path = compiler_path.join("rustc_codegen_gcc/libgccjit.version");
+    let cg_gcc_version = std::fs::read_to_string(&cg_gcc_version_path)
+        .expect(&format!("Cannot read GCC version from {}", cg_gcc_version_path.display()))
+        .trim()
+        .to_string();
+
+    let git_output = Command::new("git")
+        .current_dir(root_path)
+        .arg("submodule")
+        .arg("status")
+        // --cached asks for the version that is actually committed in the repository, not the one
+        // that is currently checked out.
+        .arg("--cached")
+        .arg("src/gcc")
+        .output()
+        .expect("Cannot determine git SHA of the src/gcc checkout");
+
+    // This can return e.g.
+    // -e607be166673a8de9fc07f6f02c60426e556c5f2 src/gcc
+    //  e607be166673a8de9fc07f6f02c60426e556c5f2 src/gcc (master-e607be166673a8de9fc07f6f02c60426e556c5f2.e607be)
+    // +e607be166673a8de9fc07f6f02c60426e556c5f2 src/gcc (master-e607be166673a8de9fc07f6f02c60426e556c5f2.e607be)
+    let git_output = String::from_utf8_lossy(&git_output.stdout)
+        .trim()
+        .split_whitespace()
+        .next()
+        .unwrap_or_default()
+        .to_string();
+
+    // The SHA can start with + if the submodule is modified or - if it is not checked out.
+    let gcc_submodule_sha = git_output.trim_start_matches(&['+', '-']);
+    if gcc_submodule_sha != cg_gcc_version {
+        *bad = true;
+        eprintln!(
+            r#"Commit SHA of the src/gcc submodule (`{gcc_submodule_sha}`) does not match the required GCC version of the GCC codegen backend (`{cg_gcc_version}`).
+Make sure to set the src/gcc submodule to commit {cg_gcc_version}.
+The GCC codegen backend commit is configured at {}."#,
+            cg_gcc_version_path.display(),
+        );
+    }
+}
diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs
index 66856f5247b..ca45f8bb84b 100644
--- a/src/tools/tidy/src/lib.rs
+++ b/src/tools/tidy/src/lib.rs
@@ -75,6 +75,7 @@ pub mod features;
 pub mod fluent_alphabetical;
 pub mod fluent_period;
 mod fluent_used;
+pub mod gcc_submodule;
 pub(crate) mod iter_header;
 pub mod known_bug;
 pub mod mir_opt_tests;
diff --git a/src/tools/tidy/src/main.rs b/src/tools/tidy/src/main.rs
index 4078d462f55..48122129b01 100644
--- a/src/tools/tidy/src/main.rs
+++ b/src/tools/tidy/src/main.rs
@@ -116,6 +116,7 @@ fn main() {
         check!(fluent_alphabetical, &compiler_path, bless);
         check!(fluent_period, &compiler_path);
         check!(target_policy, &root_path);
+        check!(gcc_submodule, &root_path, &compiler_path);
 
         // Checks that only make sense for the std libs.
         check!(pal, &library_path);