From 794434ec639277273d2154607bdcbe033691cc29 Mon Sep 17 00:00:00 2001
From: EtomicBomb <ethan@ethan.ws>
Date: Wed, 24 Jul 2024 21:52:32 +0000
Subject: [PATCH 01/16] initial implementation of rustdoc nested aux-build

---
 src/tools/compiletest/src/command-list.rs     |   2 +
 src/tools/compiletest/src/header.rs           |  13 +++
 src/tools/compiletest/src/runtest.rs          | 104 ++++++++++++------
 src/tools/compiletest/src/runtest/coverage.rs |   2 +-
 .../cargo-transitive-no-index/auxiliary/q.rs  |   4 +
 .../cargo-transitive-no-index/auxiliary/t.rs  |   6 +
 .../cargo-transitive-no-index/s.rs            |  17 +++
 .../cargo-transitive/auxiliary/q.rs           |   6 +
 .../cargo-transitive/auxiliary/t.rs           |   8 ++
 .../cross-crate-info/cargo-transitive/s.rs    |  24 ++++
 .../cargo-two-no-index/auxiliary/f.rs         |   4 +
 .../cross-crate-info/cargo-two-no-index/e.rs  |  15 +++
 .../cross-crate-info/cargo-two/auxiliary/f.rs |   6 +
 tests/rustdoc/cross-crate-info/cargo-two/e.rs |  21 ++++
 .../index-on-last/auxiliary/f.rs              |   4 +
 .../cross-crate-info/index-on-last/e.rs       |  21 ++++
 .../kitchen-sink/auxiliary/q.rs               |   6 +
 .../kitchen-sink/auxiliary/r.rs               |   8 ++
 .../kitchen-sink/auxiliary/s.rs               |   9 ++
 .../kitchen-sink/auxiliary/t.rs               |   8 ++
 .../cross-crate-info/kitchen-sink/i.rs        |  32 ++++++
 .../single-crate-baseline/q.rs                |  13 +++
 .../single-crate-no-index/q.rs                |   8 ++
 .../transitive/auxiliary/q.rs                 |   4 +
 .../transitive/auxiliary/t.rs                 |   6 +
 .../rustdoc/cross-crate-info/transitive/s.rs  |   9 ++
 .../cross-crate-info/two/auxiliary/f.rs       |   4 +
 tests/rustdoc/cross-crate-info/two/e.rs       |   9 ++
 .../working-dir-examples/q.rs                 |  10 ++
 .../write-docs-somewhere-else/auxiliary/f.rs  |   5 +
 .../write-docs-somewhere-else/e.rs            |  15 +++
 31 files changed, 369 insertions(+), 34 deletions(-)
 create mode 100644 tests/rustdoc/cross-crate-info/cargo-transitive-no-index/auxiliary/q.rs
 create mode 100644 tests/rustdoc/cross-crate-info/cargo-transitive-no-index/auxiliary/t.rs
 create mode 100644 tests/rustdoc/cross-crate-info/cargo-transitive-no-index/s.rs
 create mode 100644 tests/rustdoc/cross-crate-info/cargo-transitive/auxiliary/q.rs
 create mode 100644 tests/rustdoc/cross-crate-info/cargo-transitive/auxiliary/t.rs
 create mode 100644 tests/rustdoc/cross-crate-info/cargo-transitive/s.rs
 create mode 100644 tests/rustdoc/cross-crate-info/cargo-two-no-index/auxiliary/f.rs
 create mode 100644 tests/rustdoc/cross-crate-info/cargo-two-no-index/e.rs
 create mode 100644 tests/rustdoc/cross-crate-info/cargo-two/auxiliary/f.rs
 create mode 100644 tests/rustdoc/cross-crate-info/cargo-two/e.rs
 create mode 100644 tests/rustdoc/cross-crate-info/index-on-last/auxiliary/f.rs
 create mode 100644 tests/rustdoc/cross-crate-info/index-on-last/e.rs
 create mode 100644 tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/q.rs
 create mode 100644 tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/r.rs
 create mode 100644 tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/s.rs
 create mode 100644 tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/t.rs
 create mode 100644 tests/rustdoc/cross-crate-info/kitchen-sink/i.rs
 create mode 100644 tests/rustdoc/cross-crate-info/single-crate-baseline/q.rs
 create mode 100644 tests/rustdoc/cross-crate-info/single-crate-no-index/q.rs
 create mode 100644 tests/rustdoc/cross-crate-info/transitive/auxiliary/q.rs
 create mode 100644 tests/rustdoc/cross-crate-info/transitive/auxiliary/t.rs
 create mode 100644 tests/rustdoc/cross-crate-info/transitive/s.rs
 create mode 100644 tests/rustdoc/cross-crate-info/two/auxiliary/f.rs
 create mode 100644 tests/rustdoc/cross-crate-info/two/e.rs
 create mode 100644 tests/rustdoc/cross-crate-info/working-dir-examples/q.rs
 create mode 100644 tests/rustdoc/cross-crate-info/write-docs-somewhere-else/auxiliary/f.rs
 create mode 100644 tests/rustdoc/cross-crate-info/write-docs-somewhere-else/e.rs

diff --git a/src/tools/compiletest/src/command-list.rs b/src/tools/compiletest/src/command-list.rs
index c356f4266f016..305bf9c228c43 100644
--- a/src/tools/compiletest/src/command-list.rs
+++ b/src/tools/compiletest/src/command-list.rs
@@ -9,6 +9,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "aux-codegen-backend",
     "aux-crate",
     "build-aux-docs",
+    "unique-doc-out-dir",
     "build-fail",
     "build-pass",
     "check-fail",
@@ -18,6 +19,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "check-test-line-numbers-match",
     "compare-output-lines-by-subset",
     "compile-flags",
+    "doc-flags",
     "dont-check-compiler-stderr",
     "dont-check-compiler-stdout",
     "dont-check-failure-status",
diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs
index 0208ed34ac182..3a08a8e38950c 100644
--- a/src/tools/compiletest/src/header.rs
+++ b/src/tools/compiletest/src/header.rs
@@ -95,6 +95,8 @@ pub struct TestProps {
     pub compile_flags: Vec<String>,
     // Extra flags to pass when the compiled code is run (such as --bench)
     pub run_flags: Vec<String>,
+    /// Extra flags to pass to rustdoc but not the compiler.
+    pub doc_flags: Vec<String>,
     // If present, the name of a file that this test should match when
     // pretty-printed
     pub pp_exact: Option<PathBuf>,
@@ -122,6 +124,9 @@ pub struct TestProps {
     pub unset_exec_env: Vec<String>,
     // Build documentation for all specified aux-builds as well
     pub build_aux_docs: bool,
+    /// Build the documentation for each crate in a unique output directory.
+    /// Uses <root output directory>/docs/<test name>/doc
+    pub unique_doc_out_dir: bool,
     // Flag to force a crate to be built with the host architecture
     pub force_host: bool,
     // Check stdout for error-pattern output as well as stderr
@@ -220,8 +225,10 @@ mod directives {
     pub const REGEX_ERROR_PATTERN: &'static str = "regex-error-pattern";
     pub const COMPILE_FLAGS: &'static str = "compile-flags";
     pub const RUN_FLAGS: &'static str = "run-flags";
+    pub const DOC_FLAGS: &'static str = "doc-flags";
     pub const SHOULD_ICE: &'static str = "should-ice";
     pub const BUILD_AUX_DOCS: &'static str = "build-aux-docs";
+    pub const UNIQUE_DOC_OUT_DIR: &'static str = "unique-doc-out-dir";
     pub const FORCE_HOST: &'static str = "force-host";
     pub const CHECK_STDOUT: &'static str = "check-stdout";
     pub const CHECK_RUN_RESULTS: &'static str = "check-run-results";
@@ -267,6 +274,7 @@ impl TestProps {
             regex_error_patterns: vec![],
             compile_flags: vec![],
             run_flags: vec![],
+            doc_flags: vec![],
             pp_exact: None,
             aux_builds: vec![],
             aux_bins: vec![],
@@ -281,6 +289,7 @@ impl TestProps {
             exec_env: vec![],
             unset_exec_env: vec![],
             build_aux_docs: false,
+            unique_doc_out_dir: false,
             force_host: false,
             check_stdout: false,
             check_run_results: false,
@@ -377,6 +386,8 @@ impl TestProps {
                         |r| r,
                     );
 
+                    config.push_name_value_directive(ln, DOC_FLAGS, &mut self.doc_flags, |r| r);
+
                     fn split_flags(flags: &str) -> Vec<String> {
                         // Individual flags can be single-quoted to preserve spaces; see
                         // <https://github.com/rust-lang/rust/pull/115948/commits/957c5db6>.
@@ -414,6 +425,8 @@ impl TestProps {
 
                     config.set_name_directive(ln, SHOULD_ICE, &mut self.should_ice);
                     config.set_name_directive(ln, BUILD_AUX_DOCS, &mut self.build_aux_docs);
+                    config.set_name_directive(ln, UNIQUE_DOC_OUT_DIR, &mut self.unique_doc_out_dir);
+
                     config.set_name_directive(ln, FORCE_HOST, &mut self.force_host);
                     config.set_name_directive(ln, CHECK_STDOUT, &mut self.check_stdout);
                     config.set_name_directive(ln, CHECK_RUN_RESULTS, &mut self.check_run_results);
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index f6e8fdd624434..2d298dfbc06f7 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -1,5 +1,17 @@
 // ignore-tidy-filelength
 
+use crate::compute_diff::{write_diff, write_filtered_diff};
+use crate::errors::{self, Error, ErrorKind};
+use crate::header::TestProps;
+use crate::json;
+use crate::read2::{read2_abbreviated, Truncated};
+use crate::util::{add_dylib_path, copy_dir_all, dylib_env_var, logv, static_regex, PathBufExt};
+use crate::ColorConfig;
+use colored::Colorize;
+use miropt_test_tools::{files_for_miropt_test, MiroptTest, MiroptTestFile};
+use regex::{Captures, Regex};
+use rustfix::{apply_suggestions, get_suggestions_from_json, Filter};
+use std::borrow::Cow;
 use std::collections::{HashMap, HashSet};
 use std::ffi::{OsStr, OsString};
 use std::fs::{self, create_dir_all, File, OpenOptions};
@@ -723,7 +735,7 @@ impl<'test> TestCx<'test> {
         self.maybe_add_external_args(&mut rustc, &self.config.target_rustcflags);
         rustc.args(&self.props.compile_flags);
 
-        self.compose_and_run_compiler(rustc, Some(src))
+        self.compose_and_run_compiler(rustc, Some(src), self.testpaths)
     }
 
     fn run_debuginfo_test(&self) {
@@ -1579,13 +1591,15 @@ impl<'test> TestCx<'test> {
             passes,
         );
 
-        self.compose_and_run_compiler(rustc, None)
+        self.compose_and_run_compiler(rustc, None, self.testpaths)
     }
 
-    fn document(&self, out_dir: &Path) -> ProcRes {
+    /// `root_out_dir` and `root_testpaths` refer to the parameters of the actual test being run.
+    /// Auxiliaries, no matter how deep, have the same root_out_dir and root_testpaths.
+    fn document(&self, root_out_dir: &Path, root_testpaths: &TestPaths) -> ProcRes {
         if self.props.build_aux_docs {
             for rel_ab in &self.props.aux_builds {
-                let aux_testpaths = self.compute_aux_test_paths(&self.testpaths, rel_ab);
+                let aux_testpaths = self.compute_aux_test_paths(root_testpaths, rel_ab);
                 let aux_props =
                     self.props.from_aux_file(&aux_testpaths.file, self.revision, self.config);
                 let aux_cx = TestCx {
@@ -1596,7 +1610,7 @@ impl<'test> TestCx<'test> {
                 };
                 // Create the directory for the stdout/stderr files.
                 create_dir_all(aux_cx.output_base_dir()).unwrap();
-                let auxres = aux_cx.document(out_dir);
+                let auxres = aux_cx.document(&root_out_dir, root_testpaths);
                 if !auxres.status.success() {
                     return auxres;
                 }
@@ -1606,21 +1620,47 @@ impl<'test> TestCx<'test> {
         let aux_dir = self.aux_output_dir_name();
 
         let rustdoc_path = self.config.rustdoc_path.as_ref().expect("--rustdoc-path not passed");
-        let mut rustdoc = Command::new(rustdoc_path);
 
+        // actual --out-dir given to the auxiliary or test, as opposed to the root out dir for the entire
+        // test
+        let out_dir: Cow<'_, Path> = if self.props.unique_doc_out_dir {
+            let file_name = self
+                .testpaths
+                .file
+                .file_name()
+                .expect("file name should not be empty")
+                .to_str()
+                .expect("file name utf8")
+                .trim_end_matches(".rs");
+            let out_dir = PathBuf::from_iter([
+                root_out_dir,
+                Path::new("docs"),
+                Path::new(file_name),
+                Path::new("doc"),
+            ]);
+            create_dir_all(&out_dir).unwrap();
+            Cow::Owned(out_dir)
+        } else {
+            Cow::Borrowed(root_out_dir)
+        };
+
+        let mut rustdoc = Command::new(rustdoc_path);
+        let current_dir = output_base_dir(self.config, root_testpaths, self.safe_revision());
+        rustdoc.current_dir(current_dir);
         rustdoc
             .arg("-L")
             .arg(self.config.run_lib_path.to_str().unwrap())
             .arg("-L")
             .arg(aux_dir)
             .arg("-o")
-            .arg(out_dir)
+            .arg(out_dir.as_ref())
             .arg("--deny")
             .arg("warnings")
             .arg(&self.testpaths.file)
             .arg("-A")
             .arg("internal_features")
-            .args(&self.props.compile_flags);
+            .args(&self.props.compile_flags)
+            .args(&self.props.doc_flags);
 
         if self.config.mode == RustdocJson {
             rustdoc.arg("--output-format").arg("json").arg("-Zunstable-options");
@@ -1630,7 +1670,7 @@ impl<'test> TestCx<'test> {
             rustdoc.arg(format!("-Clinker={}", linker));
         }
 
-        self.compose_and_run_compiler(rustdoc, None)
+        self.compose_and_run_compiler(rustdoc, None, root_testpaths)
     }
 
     fn exec_compiled_test(&self) -> ProcRes {
@@ -1828,9 +1868,16 @@ impl<'test> TestCx<'test> {
         }
     }
 
-    fn compose_and_run_compiler(&self, mut rustc: Command, input: Option<String>) -> ProcRes {
+    /// `root_testpaths` refers to the path of the original test.
+    /// the auxiliary and the test with an aux-build have the same `root_testpaths`.
+    fn compose_and_run_compiler(
+        &self,
+        mut rustc: Command,
+        input: Option<String>,
+        root_testpaths: &TestPaths,
+    ) -> ProcRes {
         let aux_dir = self.aux_output_dir();
-        self.build_all_auxiliary(&self.testpaths, &aux_dir, &mut rustc);
+        self.build_all_auxiliary(root_testpaths, &aux_dir, &mut rustc);
 
         rustc.envs(self.props.rustc_env.clone());
         self.props.unset_rustc_env.iter().fold(&mut rustc, Command::env_remove);
@@ -2545,7 +2592,7 @@ impl<'test> TestCx<'test> {
             Vec::new(),
         );
 
-        let proc_res = self.compose_and_run_compiler(rustc, None);
+        let proc_res = self.compose_and_run_compiler(rustc, None, self.testpaths);
         let output_path = self.get_filecheck_file("ll");
         (proc_res, output_path)
     }
@@ -2581,7 +2628,7 @@ impl<'test> TestCx<'test> {
             Vec::new(),
         );
 
-        let proc_res = self.compose_and_run_compiler(rustc, None);
+        let proc_res = self.compose_and_run_compiler(rustc, None, self.testpaths);
         let output_path = self.get_filecheck_file("s");
         (proc_res, output_path)
     }
@@ -2664,7 +2711,7 @@ impl<'test> TestCx<'test> {
         let out_dir = self.output_base_dir();
         remove_and_create_dir_all(&out_dir);
 
-        let proc_res = self.document(&out_dir);
+        let proc_res = self.document(&out_dir, &self.testpaths);
         if !proc_res.status.success() {
             self.fatal_proc_rec("rustdoc failed!", &proc_res);
         }
@@ -2723,7 +2770,7 @@ impl<'test> TestCx<'test> {
         let aux_dir = new_rustdoc.aux_output_dir();
         new_rustdoc.build_all_auxiliary(&new_rustdoc.testpaths, &aux_dir, &mut rustc);
 
-        let proc_res = new_rustdoc.document(&compare_dir);
+        let proc_res = new_rustdoc.document(&compare_dir, &new_rustdoc.testpaths);
         if !proc_res.status.success() {
             eprintln!("failed to run nightly rustdoc");
             return;
@@ -2846,7 +2893,7 @@ impl<'test> TestCx<'test> {
         let out_dir = self.output_base_dir();
         remove_and_create_dir_all(&out_dir);
 
-        let proc_res = self.document(&out_dir);
+        let proc_res = self.document(&out_dir, &self.testpaths);
         if !proc_res.status.success() {
             self.fatal_proc_rec("rustdoc failed!", &proc_res);
         }
@@ -2922,24 +2969,15 @@ impl<'test> TestCx<'test> {
     fn check_rustdoc_test_option(&self, res: ProcRes) {
         let mut other_files = Vec::new();
         let mut files: HashMap<String, Vec<usize>> = HashMap::new();
-        let cwd = env::current_dir().unwrap();
-        files.insert(
-            self.testpaths
-                .file
-                .strip_prefix(&cwd)
-                .unwrap_or(&self.testpaths.file)
-                .to_str()
-                .unwrap()
-                .replace('\\', "/"),
-            self.get_lines(&self.testpaths.file, Some(&mut other_files)),
-        );
+        let normalized = fs::canonicalize(&self.testpaths.file).expect("failed to canonicalize");
+        let normalized = normalized.to_str().unwrap().replace('\\', "/");
+        files.insert(normalized, self.get_lines(&self.testpaths.file, Some(&mut other_files)));
         for other_file in other_files {
             let mut path = self.testpaths.file.clone();
             path.set_file_name(&format!("{}.rs", other_file));
-            files.insert(
-                path.strip_prefix(&cwd).unwrap_or(&path).to_str().unwrap().replace('\\', "/"),
-                self.get_lines(&path, None),
-            );
+            let path = fs::canonicalize(path).expect("failed to canonicalize");
+            let normalized = path.to_str().unwrap().replace('\\', "/");
+            files.insert(normalized, self.get_lines(&path, None));
         }
 
         let mut tested = 0;
@@ -3778,7 +3816,7 @@ impl<'test> TestCx<'test> {
         if let Some(nodejs) = &self.config.nodejs {
             let out_dir = self.output_base_dir();
 
-            self.document(&out_dir);
+            self.document(&out_dir, &self.testpaths);
 
             let root = self.config.find_rust_src_root().unwrap();
             let file_stem =
@@ -4094,7 +4132,7 @@ impl<'test> TestCx<'test> {
                 rustc.arg(crate_name);
             }
 
-            let res = self.compose_and_run_compiler(rustc, None);
+            let res = self.compose_and_run_compiler(rustc, None, self.testpaths);
             if !res.status.success() {
                 self.fatal_proc_rec("failed to compile fixed code", &res);
             }
diff --git a/src/tools/compiletest/src/runtest/coverage.rs b/src/tools/compiletest/src/runtest/coverage.rs
index 6ee147da5a965..05191a159801c 100644
--- a/src/tools/compiletest/src/runtest/coverage.rs
+++ b/src/tools/compiletest/src/runtest/coverage.rs
@@ -191,7 +191,7 @@ impl<'test> TestCx<'test> {
 
         rustdoc_cmd.arg(&self.testpaths.file);
 
-        let proc_res = self.compose_and_run_compiler(rustdoc_cmd, None);
+        let proc_res = self.compose_and_run_compiler(rustdoc_cmd, None, self.testpaths);
         if !proc_res.status.success() {
             self.fatal_proc_rec("rustdoc --test failed!", &proc_res)
         }
diff --git a/tests/rustdoc/cross-crate-info/cargo-transitive-no-index/auxiliary/q.rs b/tests/rustdoc/cross-crate-info/cargo-transitive-no-index/auxiliary/q.rs
new file mode 100644
index 0000000000000..32ad96d3e8e36
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/cargo-transitive-no-index/auxiliary/q.rs
@@ -0,0 +1,4 @@
+//@ build-aux-docs
+
+
+pub struct Quebec;
diff --git a/tests/rustdoc/cross-crate-info/cargo-transitive-no-index/auxiliary/t.rs b/tests/rustdoc/cross-crate-info/cargo-transitive-no-index/auxiliary/t.rs
new file mode 100644
index 0000000000000..e03c9a1c68c3b
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/cargo-transitive-no-index/auxiliary/t.rs
@@ -0,0 +1,6 @@
+//@ aux-build:q.rs
+//@ build-aux-docs
+
+
+extern crate q;
+pub trait Tango {}
diff --git a/tests/rustdoc/cross-crate-info/cargo-transitive-no-index/s.rs b/tests/rustdoc/cross-crate-info/cargo-transitive-no-index/s.rs
new file mode 100644
index 0000000000000..af0d31c733ee1
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/cargo-transitive-no-index/s.rs
@@ -0,0 +1,17 @@
+//@ aux-build:t.rs
+//@ build-aux-docs
+
+//@ has t/trait.Tango.html
+//@ hasraw search-index.js 'Quebec'
+//@ hasraw trait.impl/t/trait.Tango.js 'struct.Sierra.html'
+//@ hasraw s/struct.Sierra.html 'Tango'
+//@ hasraw search-index.js 'Sierra'
+//@ hasraw search-index.js 'Tango'
+//@ has q/struct.Quebec.html
+//@ has s/struct.Sierra.html
+
+// We document multiple crates into the same output directory, which merges the cross-crate information. Everything is available.
+
+extern crate t;
+pub struct Sierra;
+impl t::Tango for Sierra {}
diff --git a/tests/rustdoc/cross-crate-info/cargo-transitive/auxiliary/q.rs b/tests/rustdoc/cross-crate-info/cargo-transitive/auxiliary/q.rs
new file mode 100644
index 0000000000000..e32fdbaabfe21
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/cargo-transitive/auxiliary/q.rs
@@ -0,0 +1,6 @@
+//@ build-aux-docs
+//@ doc-flags:--enable-index-page
+//@ doc-flags:-Zunstable-options
+
+
+pub struct Quebec;
diff --git a/tests/rustdoc/cross-crate-info/cargo-transitive/auxiliary/t.rs b/tests/rustdoc/cross-crate-info/cargo-transitive/auxiliary/t.rs
new file mode 100644
index 0000000000000..0a49072b68a67
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/cargo-transitive/auxiliary/t.rs
@@ -0,0 +1,8 @@
+//@ aux-build:q.rs
+//@ build-aux-docs
+//@ doc-flags:--enable-index-page
+//@ doc-flags:-Zunstable-options
+
+
+extern crate q;
+pub trait Tango {}
diff --git a/tests/rustdoc/cross-crate-info/cargo-transitive/s.rs b/tests/rustdoc/cross-crate-info/cargo-transitive/s.rs
new file mode 100644
index 0000000000000..b050fbdf67d99
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/cargo-transitive/s.rs
@@ -0,0 +1,24 @@
+//@ aux-build:t.rs
+//@ build-aux-docs
+//@ doc-flags:--enable-index-page
+//@ doc-flags:-Zunstable-options
+
+//@ has t/trait.Tango.html
+//@ hasraw search-index.js 'Quebec'
+//@ has index.html '//ul[@class="all-items"]//a[@href="q/index.html"]' 'q'
+//@ hasraw s/struct.Sierra.html 'Tango'
+//@ hasraw trait.impl/t/trait.Tango.js 'struct.Sierra.html'
+//@ has index.html '//ul[@class="all-items"]//a[@href="t/index.html"]' 't'
+//@ has index.html '//h1' 'List of all crates'
+//@ has index.html '//ul[@class="all-items"]//a[@href="s/index.html"]' 's'
+//@ hasraw search-index.js 'Sierra'
+//@ hasraw search-index.js 'Tango'
+//@ has index.html
+//@ has q/struct.Quebec.html
+//@ has s/struct.Sierra.html
+
+// We document multiple crates into the same output directory, which merges the cross-crate information. Everything is available.
+
+extern crate t;
+pub struct Sierra;
+impl t::Tango for Sierra {}
diff --git a/tests/rustdoc/cross-crate-info/cargo-two-no-index/auxiliary/f.rs b/tests/rustdoc/cross-crate-info/cargo-two-no-index/auxiliary/f.rs
new file mode 100644
index 0000000000000..1bfc785535b53
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/cargo-two-no-index/auxiliary/f.rs
@@ -0,0 +1,4 @@
+//@ build-aux-docs
+
+
+pub trait Foxtrot {}
diff --git a/tests/rustdoc/cross-crate-info/cargo-two-no-index/e.rs b/tests/rustdoc/cross-crate-info/cargo-two-no-index/e.rs
new file mode 100644
index 0000000000000..d3e42989b7770
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/cargo-two-no-index/e.rs
@@ -0,0 +1,15 @@
+//@ aux-build:f.rs
+//@ build-aux-docs
+
+//@ hasraw search-index.js 'Echo'
+//@ hasraw search-index.js 'Foxtrot'
+//@ hasraw trait.impl/f/trait.Foxtrot.js 'enum.Echo.html'
+//@ has f/trait.Foxtrot.html
+//@ has e/enum.Echo.html
+//@ hasraw e/enum.Echo.html 'Foxtrot'
+
+// document two crates in the same way that cargo does. do not provide --enable-index-page
+
+extern crate f;
+pub enum Echo {}
+impl f::Foxtrot for Echo {}
diff --git a/tests/rustdoc/cross-crate-info/cargo-two/auxiliary/f.rs b/tests/rustdoc/cross-crate-info/cargo-two/auxiliary/f.rs
new file mode 100644
index 0000000000000..e84835b3e3102
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/cargo-two/auxiliary/f.rs
@@ -0,0 +1,6 @@
+//@ build-aux-docs
+//@ doc-flags:--enable-index-page
+//@ doc-flags:-Zunstable-options
+
+
+pub trait Foxtrot {}
diff --git a/tests/rustdoc/cross-crate-info/cargo-two/e.rs b/tests/rustdoc/cross-crate-info/cargo-two/e.rs
new file mode 100644
index 0000000000000..bb845877b52f0
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/cargo-two/e.rs
@@ -0,0 +1,21 @@
+//@ aux-build:f.rs
+//@ build-aux-docs
+//@ doc-flags:--enable-index-page
+//@ doc-flags:-Zunstable-options
+
+//@ hasraw search-index.js 'Echo'
+//@ hasraw search-index.js 'Foxtrot'
+//@ has index.html '//ul[@class="all-items"]//a[@href="e/index.html"]' 'e'
+//@ has index.html '//ul[@class="all-items"]//a[@href="f/index.html"]' 'f'
+//@ hasraw trait.impl/f/trait.Foxtrot.js 'enum.Echo.html'
+//@ has f/trait.Foxtrot.html
+//@ has index.html '//h1' 'List of all crates'
+//@ has e/enum.Echo.html
+//@ has index.html
+//@ hasraw e/enum.Echo.html 'Foxtrot'
+
+// document two crates in the same way that cargo does, writing them both into the same output directory
+
+extern crate f;
+pub enum Echo {}
+impl f::Foxtrot for Echo {}
diff --git a/tests/rustdoc/cross-crate-info/index-on-last/auxiliary/f.rs b/tests/rustdoc/cross-crate-info/index-on-last/auxiliary/f.rs
new file mode 100644
index 0000000000000..1bfc785535b53
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/index-on-last/auxiliary/f.rs
@@ -0,0 +1,4 @@
+//@ build-aux-docs
+
+
+pub trait Foxtrot {}
diff --git a/tests/rustdoc/cross-crate-info/index-on-last/e.rs b/tests/rustdoc/cross-crate-info/index-on-last/e.rs
new file mode 100644
index 0000000000000..876b94464c372
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/index-on-last/e.rs
@@ -0,0 +1,21 @@
+//@ aux-build:f.rs
+//@ build-aux-docs
+//@ doc-flags:--enable-index-page
+//@ doc-flags:-Zunstable-options
+
+//@ hasraw search-index.js 'Echo'
+//@ hasraw search-index.js 'Foxtrot'
+//@ has index.html '//ul[@class="all-items"]//a[@href="e/index.html"]' 'e'
+//@ has index.html '//ul[@class="all-items"]//a[@href="f/index.html"]' 'f'
+//@ hasraw trait.impl/f/trait.Foxtrot.js 'enum.Echo.html'
+//@ has f/trait.Foxtrot.html
+//@ has index.html '//h1' 'List of all crates'
+//@ has e/enum.Echo.html
+//@ has index.html
+//@ hasraw e/enum.Echo.html 'Foxtrot'
+
+// only declare --enable-index-page to the last rustdoc invocation
+
+extern crate f;
+pub enum Echo {}
+impl f::Foxtrot for Echo {}
diff --git a/tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/q.rs b/tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/q.rs
new file mode 100644
index 0000000000000..e32fdbaabfe21
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/q.rs
@@ -0,0 +1,6 @@
+//@ build-aux-docs
+//@ doc-flags:--enable-index-page
+//@ doc-flags:-Zunstable-options
+
+
+pub struct Quebec;
diff --git a/tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/r.rs b/tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/r.rs
new file mode 100644
index 0000000000000..c0ada1aea1a85
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/r.rs
@@ -0,0 +1,8 @@
+//@ aux-build:s.rs
+//@ build-aux-docs
+//@ doc-flags:--enable-index-page
+//@ doc-flags:-Zunstable-options
+
+
+extern crate s;
+pub type Romeo = s::Sierra;
diff --git a/tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/s.rs b/tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/s.rs
new file mode 100644
index 0000000000000..6b414a289f5a8
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/s.rs
@@ -0,0 +1,9 @@
+//@ aux-build:t.rs
+//@ build-aux-docs
+//@ doc-flags:--enable-index-page
+//@ doc-flags:-Zunstable-options
+
+
+extern crate t;
+pub struct Sierra;
+impl t::Tango for Sierra {}
diff --git a/tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/t.rs b/tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/t.rs
new file mode 100644
index 0000000000000..0a49072b68a67
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/t.rs
@@ -0,0 +1,8 @@
+//@ aux-build:q.rs
+//@ build-aux-docs
+//@ doc-flags:--enable-index-page
+//@ doc-flags:-Zunstable-options
+
+
+extern crate q;
+pub trait Tango {}
diff --git a/tests/rustdoc/cross-crate-info/kitchen-sink/i.rs b/tests/rustdoc/cross-crate-info/kitchen-sink/i.rs
new file mode 100644
index 0000000000000..9df0e9779ffa7
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/kitchen-sink/i.rs
@@ -0,0 +1,32 @@
+//@ aux-build:q.rs
+//@ aux-build:r.rs
+//@ aux-build:t.rs
+//@ aux-build:s.rs
+//@ build-aux-docs
+//@ doc-flags:--enable-index-page
+//@ doc-flags:-Zunstable-options
+
+//@ hasraw search-index.js 'Quebec'
+//@ hasraw search-index.js 'Sierra'
+//@ has index.html
+//@ has s/struct.Sierra.html
+//@ hasraw s/struct.Sierra.html 'Tango'
+//@ has index.html '//ul[@class="all-items"]//a[@href="i/index.html"]' 'i'
+//@ has q/struct.Quebec.html
+//@ has type.impl/s/struct.Sierra.js
+//@ hasraw type.impl/s/struct.Sierra.js 'Romeo'
+//@ hasraw type.impl/s/struct.Sierra.js 'Tango'
+//@ has index.html '//ul[@class="all-items"]//a[@href="q/index.html"]' 'q'
+//@ has index.html '//ul[@class="all-items"]//a[@href="t/index.html"]' 't'
+//@ has index.html '//ul[@class="all-items"]//a[@href="s/index.html"]' 's'
+//@ has r/type.Romeo.html
+//@ has t/trait.Tango.html
+//@ hasraw search-index.js 'Romeo'
+//@ hasraw trait.impl/t/trait.Tango.js 'struct.Sierra.html'
+//@ has index.html '//h1' 'List of all crates'
+//@ hasraw search-index.js 'Tango'
+//@ has index.html '//ul[@class="all-items"]//a[@href="r/index.html"]' 'r'
+
+// document everything in the default mode
+
+
diff --git a/tests/rustdoc/cross-crate-info/single-crate-baseline/q.rs b/tests/rustdoc/cross-crate-info/single-crate-baseline/q.rs
new file mode 100644
index 0000000000000..a9e03852f2297
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/single-crate-baseline/q.rs
@@ -0,0 +1,13 @@
+//@ build-aux-docs
+//@ doc-flags:--enable-index-page
+//@ doc-flags:-Zunstable-options
+
+//@ hasraw search-index.js 'Quebec'
+//@ has index.html '//ul[@class="all-items"]//a[@href="q/index.html"]' 'q'
+//@ has index.html '//h1' 'List of all crates'
+//@ has index.html
+//@ has q/struct.Quebec.html
+
+// there's nothing cross-crate going on here
+
+pub struct Quebec;
diff --git a/tests/rustdoc/cross-crate-info/single-crate-no-index/q.rs b/tests/rustdoc/cross-crate-info/single-crate-no-index/q.rs
new file mode 100644
index 0000000000000..38471e02a2820
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/single-crate-no-index/q.rs
@@ -0,0 +1,8 @@
+//@ build-aux-docs
+
+//@ hasraw search-index.js 'Quebec'
+//@ has q/struct.Quebec.html
+
+// there's nothing cross-crate going on here
+
+pub struct Quebec;
diff --git a/tests/rustdoc/cross-crate-info/transitive/auxiliary/q.rs b/tests/rustdoc/cross-crate-info/transitive/auxiliary/q.rs
new file mode 100644
index 0000000000000..32ad96d3e8e36
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/transitive/auxiliary/q.rs
@@ -0,0 +1,4 @@
+//@ build-aux-docs
+
+
+pub struct Quebec;
diff --git a/tests/rustdoc/cross-crate-info/transitive/auxiliary/t.rs b/tests/rustdoc/cross-crate-info/transitive/auxiliary/t.rs
new file mode 100644
index 0000000000000..e03c9a1c68c3b
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/transitive/auxiliary/t.rs
@@ -0,0 +1,6 @@
+//@ aux-build:q.rs
+//@ build-aux-docs
+
+
+extern crate q;
+pub trait Tango {}
diff --git a/tests/rustdoc/cross-crate-info/transitive/s.rs b/tests/rustdoc/cross-crate-info/transitive/s.rs
new file mode 100644
index 0000000000000..a758bd68be22f
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/transitive/s.rs
@@ -0,0 +1,9 @@
+//@ aux-build:t.rs
+//@ build-aux-docs
+
+
+// simple test to see if we support building transitive crates
+
+extern crate t;
+pub struct Sierra;
+impl t::Tango for Sierra {}
diff --git a/tests/rustdoc/cross-crate-info/two/auxiliary/f.rs b/tests/rustdoc/cross-crate-info/two/auxiliary/f.rs
new file mode 100644
index 0000000000000..1bfc785535b53
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/two/auxiliary/f.rs
@@ -0,0 +1,4 @@
+//@ build-aux-docs
+
+
+pub trait Foxtrot {}
diff --git a/tests/rustdoc/cross-crate-info/two/e.rs b/tests/rustdoc/cross-crate-info/two/e.rs
new file mode 100644
index 0000000000000..431e73c390d4a
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/two/e.rs
@@ -0,0 +1,9 @@
+//@ aux-build:f.rs
+//@ build-aux-docs
+
+
+// simple test to assert that we can do a two-level aux-build
+
+extern crate f;
+pub enum Echo {}
+impl f::Foxtrot for Echo {}
diff --git a/tests/rustdoc/cross-crate-info/working-dir-examples/q.rs b/tests/rustdoc/cross-crate-info/working-dir-examples/q.rs
new file mode 100644
index 0000000000000..89d1349e520fa
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/working-dir-examples/q.rs
@@ -0,0 +1,10 @@
+//@ build-aux-docs
+//@ doc-flags:--scrape-examples-output-path=examples
+//@ doc-flags:--scrape-examples-target-crate=q
+//@ doc-flags:-Zunstable-options
+
+//@ has examples
+
+// where will --scrape-examples-output-path resolve the path to be? should be the root output directory
+
+pub struct Quebec;
diff --git a/tests/rustdoc/cross-crate-info/write-docs-somewhere-else/auxiliary/f.rs b/tests/rustdoc/cross-crate-info/write-docs-somewhere-else/auxiliary/f.rs
new file mode 100644
index 0000000000000..52e02e8b89715
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/write-docs-somewhere-else/auxiliary/f.rs
@@ -0,0 +1,5 @@
+//@ build-aux-docs
+//@ unique-doc-out-dir
+
+
+pub trait Foxtrot {}
diff --git a/tests/rustdoc/cross-crate-info/write-docs-somewhere-else/e.rs b/tests/rustdoc/cross-crate-info/write-docs-somewhere-else/e.rs
new file mode 100644
index 0000000000000..0480a02bf22f7
--- /dev/null
+++ b/tests/rustdoc/cross-crate-info/write-docs-somewhere-else/e.rs
@@ -0,0 +1,15 @@
+//@ aux-build:f.rs
+//@ build-aux-docs
+
+//@ hasraw search-index.js 'Echo'
+//@ !hasraw search-index.js 'Foxtrot'
+//@ hasraw trait.impl/f/trait.Foxtrot.js 'enum.Echo.html'
+//@ !has f/trait.Foxtrot.html
+//@ has e/enum.Echo.html
+//@ hasraw e/enum.Echo.html 'Foxtrot'
+
+// test the fact that our test runner will document this crate somewhere else
+
+extern crate f;
+pub enum Echo {}
+impl f::Foxtrot for Echo {}

From d8211def61d2b436d2a8335d745105e2de3ac172 Mon Sep 17 00:00:00 2001
From: EtomicBomb <ethan@ethan.ws>
Date: Wed, 24 Jul 2024 22:16:01 +0000
Subject: [PATCH 02/16] ordering and wrapping cross-crate-info tests

---
 src/tools/compiletest/src/command-list.rs     |  2 +-
 .../cargo-transitive-no-index/s.rs            | 13 +++++-----
 .../cross-crate-info/cargo-transitive/s.rs    | 21 ++++++++-------
 .../cross-crate-info/cargo-two-no-index/e.rs  | 11 ++++----
 tests/rustdoc/cross-crate-info/cargo-two/e.rs | 17 ++++++------
 .../cross-crate-info/index-on-last/e.rs       | 14 +++++-----
 .../cross-crate-info/kitchen-sink/i.rs        | 26 +++++++++----------
 .../single-crate-baseline/q.rs                |  6 ++---
 .../single-crate-no-index/q.rs                |  2 +-
 .../working-dir-examples/q.rs                 |  3 ++-
 .../write-docs-somewhere-else/e.rs            | 11 ++++----
 11 files changed, 66 insertions(+), 60 deletions(-)

diff --git a/src/tools/compiletest/src/command-list.rs b/src/tools/compiletest/src/command-list.rs
index 305bf9c228c43..68443c300fc5c 100644
--- a/src/tools/compiletest/src/command-list.rs
+++ b/src/tools/compiletest/src/command-list.rs
@@ -9,7 +9,6 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "aux-codegen-backend",
     "aux-crate",
     "build-aux-docs",
-    "unique-doc-out-dir",
     "build-fail",
     "build-pass",
     "check-fail",
@@ -227,6 +226,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "should-ice",
     "stderr-per-bitwidth",
     "test-mir-pass",
+    "unique-doc-out-dir",
     "unset-exec-env",
     "unset-rustc-env",
     // Used by the tidy check `unknown_revision`.
diff --git a/tests/rustdoc/cross-crate-info/cargo-transitive-no-index/s.rs b/tests/rustdoc/cross-crate-info/cargo-transitive-no-index/s.rs
index af0d31c733ee1..639b772374fc8 100644
--- a/tests/rustdoc/cross-crate-info/cargo-transitive-no-index/s.rs
+++ b/tests/rustdoc/cross-crate-info/cargo-transitive-no-index/s.rs
@@ -1,16 +1,17 @@
 //@ aux-build:t.rs
 //@ build-aux-docs
 
+//@ has q/struct.Quebec.html
+//@ has s/struct.Sierra.html
 //@ has t/trait.Tango.html
-//@ hasraw search-index.js 'Quebec'
-//@ hasraw trait.impl/t/trait.Tango.js 'struct.Sierra.html'
 //@ hasraw s/struct.Sierra.html 'Tango'
-//@ hasraw search-index.js 'Sierra'
+//@ hasraw trait.impl/t/trait.Tango.js 'struct.Sierra.html'
 //@ hasraw search-index.js 'Tango'
-//@ has q/struct.Quebec.html
-//@ has s/struct.Sierra.html
+//@ hasraw search-index.js 'Sierra'
+//@ hasraw search-index.js 'Quebec'
 
-// We document multiple crates into the same output directory, which merges the cross-crate information. Everything is available.
+// We document multiple crates into the same output directory, which
+// merges the cross-crate information. Everything is available.
 
 extern crate t;
 pub struct Sierra;
diff --git a/tests/rustdoc/cross-crate-info/cargo-transitive/s.rs b/tests/rustdoc/cross-crate-info/cargo-transitive/s.rs
index b050fbdf67d99..4383d089c4c73 100644
--- a/tests/rustdoc/cross-crate-info/cargo-transitive/s.rs
+++ b/tests/rustdoc/cross-crate-info/cargo-transitive/s.rs
@@ -3,21 +3,22 @@
 //@ doc-flags:--enable-index-page
 //@ doc-flags:-Zunstable-options
 
-//@ has t/trait.Tango.html
-//@ hasraw search-index.js 'Quebec'
-//@ has index.html '//ul[@class="all-items"]//a[@href="q/index.html"]' 'q'
-//@ hasraw s/struct.Sierra.html 'Tango'
-//@ hasraw trait.impl/t/trait.Tango.js 'struct.Sierra.html'
-//@ has index.html '//ul[@class="all-items"]//a[@href="t/index.html"]' 't'
+//@ has index.html
 //@ has index.html '//h1' 'List of all crates'
+//@ has index.html '//ul[@class="all-items"]//a[@href="q/index.html"]' 'q'
 //@ has index.html '//ul[@class="all-items"]//a[@href="s/index.html"]' 's'
-//@ hasraw search-index.js 'Sierra'
-//@ hasraw search-index.js 'Tango'
-//@ has index.html
+//@ has index.html '//ul[@class="all-items"]//a[@href="t/index.html"]' 't'
 //@ has q/struct.Quebec.html
 //@ has s/struct.Sierra.html
+//@ has t/trait.Tango.html
+//@ hasraw s/struct.Sierra.html 'Tango'
+//@ hasraw trait.impl/t/trait.Tango.js 'struct.Sierra.html'
+//@ hasraw search-index.js 'Tango'
+//@ hasraw search-index.js 'Sierra'
+//@ hasraw search-index.js 'Quebec'
 
-// We document multiple crates into the same output directory, which merges the cross-crate information. Everything is available.
+// We document multiple crates into the same output directory, which
+// merges the cross-crate information. Everything is available.
 
 extern crate t;
 pub struct Sierra;
diff --git a/tests/rustdoc/cross-crate-info/cargo-two-no-index/e.rs b/tests/rustdoc/cross-crate-info/cargo-two-no-index/e.rs
index d3e42989b7770..7d64f9481fb80 100644
--- a/tests/rustdoc/cross-crate-info/cargo-two-no-index/e.rs
+++ b/tests/rustdoc/cross-crate-info/cargo-two-no-index/e.rs
@@ -1,14 +1,15 @@
 //@ aux-build:f.rs
 //@ build-aux-docs
 
-//@ hasraw search-index.js 'Echo'
-//@ hasraw search-index.js 'Foxtrot'
-//@ hasraw trait.impl/f/trait.Foxtrot.js 'enum.Echo.html'
-//@ has f/trait.Foxtrot.html
 //@ has e/enum.Echo.html
+//@ has f/trait.Foxtrot.html
 //@ hasraw e/enum.Echo.html 'Foxtrot'
+//@ hasraw trait.impl/f/trait.Foxtrot.js 'enum.Echo.html'
+//@ hasraw search-index.js 'Foxtrot'
+//@ hasraw search-index.js 'Echo'
 
-// document two crates in the same way that cargo does. do not provide --enable-index-page
+// document two crates in the same way that cargo does. do not provide
+// --enable-index-page
 
 extern crate f;
 pub enum Echo {}
diff --git a/tests/rustdoc/cross-crate-info/cargo-two/e.rs b/tests/rustdoc/cross-crate-info/cargo-two/e.rs
index bb845877b52f0..29e3571e6c209 100644
--- a/tests/rustdoc/cross-crate-info/cargo-two/e.rs
+++ b/tests/rustdoc/cross-crate-info/cargo-two/e.rs
@@ -3,18 +3,19 @@
 //@ doc-flags:--enable-index-page
 //@ doc-flags:-Zunstable-options
 
-//@ hasraw search-index.js 'Echo'
-//@ hasraw search-index.js 'Foxtrot'
-//@ has index.html '//ul[@class="all-items"]//a[@href="e/index.html"]' 'e'
-//@ has index.html '//ul[@class="all-items"]//a[@href="f/index.html"]' 'f'
-//@ hasraw trait.impl/f/trait.Foxtrot.js 'enum.Echo.html'
-//@ has f/trait.Foxtrot.html
+//@ has index.html
 //@ has index.html '//h1' 'List of all crates'
+//@ has index.html '//ul[@class="all-items"]//a[@href="f/index.html"]' 'f'
+//@ has index.html '//ul[@class="all-items"]//a[@href="e/index.html"]' 'e'
 //@ has e/enum.Echo.html
-//@ has index.html
+//@ has f/trait.Foxtrot.html
 //@ hasraw e/enum.Echo.html 'Foxtrot'
+//@ hasraw trait.impl/f/trait.Foxtrot.js 'enum.Echo.html'
+//@ hasraw search-index.js 'Foxtrot'
+//@ hasraw search-index.js 'Echo'
 
-// document two crates in the same way that cargo does, writing them both into the same output directory
+// document two crates in the same way that cargo does, writing them both
+// into the same output directory
 
 extern crate f;
 pub enum Echo {}
diff --git a/tests/rustdoc/cross-crate-info/index-on-last/e.rs b/tests/rustdoc/cross-crate-info/index-on-last/e.rs
index 876b94464c372..fee9f4bcddbe4 100644
--- a/tests/rustdoc/cross-crate-info/index-on-last/e.rs
+++ b/tests/rustdoc/cross-crate-info/index-on-last/e.rs
@@ -3,16 +3,16 @@
 //@ doc-flags:--enable-index-page
 //@ doc-flags:-Zunstable-options
 
-//@ hasraw search-index.js 'Echo'
-//@ hasraw search-index.js 'Foxtrot'
-//@ has index.html '//ul[@class="all-items"]//a[@href="e/index.html"]' 'e'
-//@ has index.html '//ul[@class="all-items"]//a[@href="f/index.html"]' 'f'
-//@ hasraw trait.impl/f/trait.Foxtrot.js 'enum.Echo.html'
-//@ has f/trait.Foxtrot.html
+//@ has index.html
 //@ has index.html '//h1' 'List of all crates'
+//@ has index.html '//ul[@class="all-items"]//a[@href="f/index.html"]' 'f'
+//@ has index.html '//ul[@class="all-items"]//a[@href="e/index.html"]' 'e'
 //@ has e/enum.Echo.html
-//@ has index.html
+//@ has f/trait.Foxtrot.html
 //@ hasraw e/enum.Echo.html 'Foxtrot'
+//@ hasraw trait.impl/f/trait.Foxtrot.js 'enum.Echo.html'
+//@ hasraw search-index.js 'Foxtrot'
+//@ hasraw search-index.js 'Echo'
 
 // only declare --enable-index-page to the last rustdoc invocation
 
diff --git a/tests/rustdoc/cross-crate-info/kitchen-sink/i.rs b/tests/rustdoc/cross-crate-info/kitchen-sink/i.rs
index 9df0e9779ffa7..558f47ab7c4c9 100644
--- a/tests/rustdoc/cross-crate-info/kitchen-sink/i.rs
+++ b/tests/rustdoc/cross-crate-info/kitchen-sink/i.rs
@@ -1,31 +1,31 @@
 //@ aux-build:q.rs
 //@ aux-build:r.rs
-//@ aux-build:t.rs
 //@ aux-build:s.rs
+//@ aux-build:t.rs
 //@ build-aux-docs
 //@ doc-flags:--enable-index-page
 //@ doc-flags:-Zunstable-options
 
-//@ hasraw search-index.js 'Quebec'
-//@ hasraw search-index.js 'Sierra'
+//@ has index.html '//h1' 'List of all crates'
 //@ has index.html
-//@ has s/struct.Sierra.html
-//@ hasraw s/struct.Sierra.html 'Tango'
 //@ has index.html '//ul[@class="all-items"]//a[@href="i/index.html"]' 'i'
-//@ has q/struct.Quebec.html
-//@ has type.impl/s/struct.Sierra.js
-//@ hasraw type.impl/s/struct.Sierra.js 'Romeo'
-//@ hasraw type.impl/s/struct.Sierra.js 'Tango'
 //@ has index.html '//ul[@class="all-items"]//a[@href="q/index.html"]' 'q'
-//@ has index.html '//ul[@class="all-items"]//a[@href="t/index.html"]' 't'
+//@ has index.html '//ul[@class="all-items"]//a[@href="r/index.html"]' 'r'
 //@ has index.html '//ul[@class="all-items"]//a[@href="s/index.html"]' 's'
+//@ has index.html '//ul[@class="all-items"]//a[@href="t/index.html"]' 't'
+//@ has q/struct.Quebec.html
 //@ has r/type.Romeo.html
+//@ has s/struct.Sierra.html
 //@ has t/trait.Tango.html
-//@ hasraw search-index.js 'Romeo'
+//@ hasraw s/struct.Sierra.html 'Tango'
 //@ hasraw trait.impl/t/trait.Tango.js 'struct.Sierra.html'
-//@ has index.html '//h1' 'List of all crates'
+//@ hasraw search-index.js 'Quebec'
+//@ hasraw search-index.js 'Romeo'
+//@ hasraw search-index.js 'Sierra'
 //@ hasraw search-index.js 'Tango'
-//@ has index.html '//ul[@class="all-items"]//a[@href="r/index.html"]' 'r'
+//@ has type.impl/s/struct.Sierra.js
+//@ hasraw type.impl/s/struct.Sierra.js 'Tango'
+//@ hasraw type.impl/s/struct.Sierra.js 'Romeo'
 
 // document everything in the default mode
 
diff --git a/tests/rustdoc/cross-crate-info/single-crate-baseline/q.rs b/tests/rustdoc/cross-crate-info/single-crate-baseline/q.rs
index a9e03852f2297..aa5cff73e7463 100644
--- a/tests/rustdoc/cross-crate-info/single-crate-baseline/q.rs
+++ b/tests/rustdoc/cross-crate-info/single-crate-baseline/q.rs
@@ -2,11 +2,11 @@
 //@ doc-flags:--enable-index-page
 //@ doc-flags:-Zunstable-options
 
-//@ hasraw search-index.js 'Quebec'
-//@ has index.html '//ul[@class="all-items"]//a[@href="q/index.html"]' 'q'
-//@ has index.html '//h1' 'List of all crates'
 //@ has index.html
+//@ has index.html '//h1' 'List of all crates'
+//@ has index.html '//ul[@class="all-items"]//a[@href="q/index.html"]' 'q'
 //@ has q/struct.Quebec.html
+//@ hasraw search-index.js 'Quebec'
 
 // there's nothing cross-crate going on here
 
diff --git a/tests/rustdoc/cross-crate-info/single-crate-no-index/q.rs b/tests/rustdoc/cross-crate-info/single-crate-no-index/q.rs
index 38471e02a2820..0cd126e8247d9 100644
--- a/tests/rustdoc/cross-crate-info/single-crate-no-index/q.rs
+++ b/tests/rustdoc/cross-crate-info/single-crate-no-index/q.rs
@@ -1,7 +1,7 @@
 //@ build-aux-docs
 
-//@ hasraw search-index.js 'Quebec'
 //@ has q/struct.Quebec.html
+//@ hasraw search-index.js 'Quebec'
 
 // there's nothing cross-crate going on here
 
diff --git a/tests/rustdoc/cross-crate-info/working-dir-examples/q.rs b/tests/rustdoc/cross-crate-info/working-dir-examples/q.rs
index 89d1349e520fa..ceced1fc46dcd 100644
--- a/tests/rustdoc/cross-crate-info/working-dir-examples/q.rs
+++ b/tests/rustdoc/cross-crate-info/working-dir-examples/q.rs
@@ -5,6 +5,7 @@
 
 //@ has examples
 
-// where will --scrape-examples-output-path resolve the path to be? should be the root output directory
+// where will --scrape-examples-output-path resolve the path to be?
+// should be the root output directory
 
 pub struct Quebec;
diff --git a/tests/rustdoc/cross-crate-info/write-docs-somewhere-else/e.rs b/tests/rustdoc/cross-crate-info/write-docs-somewhere-else/e.rs
index 0480a02bf22f7..6247ed5fd9660 100644
--- a/tests/rustdoc/cross-crate-info/write-docs-somewhere-else/e.rs
+++ b/tests/rustdoc/cross-crate-info/write-docs-somewhere-else/e.rs
@@ -1,14 +1,15 @@
 //@ aux-build:f.rs
 //@ build-aux-docs
 
-//@ hasraw search-index.js 'Echo'
-//@ !hasraw search-index.js 'Foxtrot'
-//@ hasraw trait.impl/f/trait.Foxtrot.js 'enum.Echo.html'
-//@ !has f/trait.Foxtrot.html
 //@ has e/enum.Echo.html
+//@ !has f/trait.Foxtrot.html
 //@ hasraw e/enum.Echo.html 'Foxtrot'
+//@ hasraw trait.impl/f/trait.Foxtrot.js 'enum.Echo.html'
+//@ !hasraw search-index.js 'Foxtrot'
+//@ hasraw search-index.js 'Echo'
 
-// test the fact that our test runner will document this crate somewhere else
+// test the fact that our test runner will document this crate somewhere
+// else
 
 extern crate f;
 pub enum Echo {}

From f6f0ef46f5a9f368183e8c59ee82d0319af7ec9c Mon Sep 17 00:00:00 2001
From: EtomicBomb <ethan@ethan.ws>
Date: Wed, 24 Jul 2024 22:35:14 +0000
Subject: [PATCH 03/16] reformatted rustdoc/cross-crate-info, fixing trailing
 newline issue

---
 .../cargo-transitive-no-index/auxiliary/q.rs                | 2 --
 .../cargo-transitive-no-index/auxiliary/t.rs                | 2 --
 .../rustdoc/cross-crate-info/cargo-transitive-no-index/s.rs | 2 --
 .../cross-crate-info/cargo-transitive/auxiliary/q.rs        | 1 -
 .../cross-crate-info/cargo-transitive/auxiliary/t.rs        | 1 -
 tests/rustdoc/cross-crate-info/cargo-transitive/s.rs        | 1 -
 .../cross-crate-info/cargo-two-no-index/auxiliary/f.rs      | 2 --
 tests/rustdoc/cross-crate-info/cargo-two-no-index/e.rs      | 2 --
 tests/rustdoc/cross-crate-info/cargo-two/auxiliary/f.rs     | 1 -
 tests/rustdoc/cross-crate-info/cargo-two/e.rs               | 1 -
 tests/rustdoc/cross-crate-info/index-on-last/auxiliary/f.rs | 2 --
 tests/rustdoc/cross-crate-info/index-on-last/e.rs           | 1 -
 tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/q.rs  | 1 -
 tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/r.rs  | 1 -
 tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/s.rs  | 1 -
 tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/t.rs  | 1 -
 tests/rustdoc/cross-crate-info/kitchen-sink/i.rs            | 6 ++----
 tests/rustdoc/cross-crate-info/single-crate-baseline/q.rs   | 1 -
 tests/rustdoc/cross-crate-info/single-crate-no-index/q.rs   | 2 --
 tests/rustdoc/cross-crate-info/transitive/auxiliary/q.rs    | 2 --
 tests/rustdoc/cross-crate-info/transitive/auxiliary/t.rs    | 2 --
 tests/rustdoc/cross-crate-info/transitive/s.rs              | 3 ---
 tests/rustdoc/cross-crate-info/two/auxiliary/f.rs           | 2 --
 tests/rustdoc/cross-crate-info/two/e.rs                     | 3 ---
 tests/rustdoc/cross-crate-info/working-dir-examples/q.rs    | 1 -
 .../write-docs-somewhere-else/auxiliary/f.rs                | 2 --
 .../rustdoc/cross-crate-info/write-docs-somewhere-else/e.rs | 2 --
 27 files changed, 2 insertions(+), 46 deletions(-)

diff --git a/tests/rustdoc/cross-crate-info/cargo-transitive-no-index/auxiliary/q.rs b/tests/rustdoc/cross-crate-info/cargo-transitive-no-index/auxiliary/q.rs
index 32ad96d3e8e36..5d0881029cb2f 100644
--- a/tests/rustdoc/cross-crate-info/cargo-transitive-no-index/auxiliary/q.rs
+++ b/tests/rustdoc/cross-crate-info/cargo-transitive-no-index/auxiliary/q.rs
@@ -1,4 +1,2 @@
 //@ build-aux-docs
-
-
 pub struct Quebec;
diff --git a/tests/rustdoc/cross-crate-info/cargo-transitive-no-index/auxiliary/t.rs b/tests/rustdoc/cross-crate-info/cargo-transitive-no-index/auxiliary/t.rs
index e03c9a1c68c3b..fab9ec4a92b96 100644
--- a/tests/rustdoc/cross-crate-info/cargo-transitive-no-index/auxiliary/t.rs
+++ b/tests/rustdoc/cross-crate-info/cargo-transitive-no-index/auxiliary/t.rs
@@ -1,6 +1,4 @@
 //@ aux-build:q.rs
 //@ build-aux-docs
-
-
 extern crate q;
 pub trait Tango {}
diff --git a/tests/rustdoc/cross-crate-info/cargo-transitive-no-index/s.rs b/tests/rustdoc/cross-crate-info/cargo-transitive-no-index/s.rs
index 639b772374fc8..85c460ace642f 100644
--- a/tests/rustdoc/cross-crate-info/cargo-transitive-no-index/s.rs
+++ b/tests/rustdoc/cross-crate-info/cargo-transitive-no-index/s.rs
@@ -1,6 +1,5 @@
 //@ aux-build:t.rs
 //@ build-aux-docs
-
 //@ has q/struct.Quebec.html
 //@ has s/struct.Sierra.html
 //@ has t/trait.Tango.html
@@ -12,7 +11,6 @@
 
 // We document multiple crates into the same output directory, which
 // merges the cross-crate information. Everything is available.
-
 extern crate t;
 pub struct Sierra;
 impl t::Tango for Sierra {}
diff --git a/tests/rustdoc/cross-crate-info/cargo-transitive/auxiliary/q.rs b/tests/rustdoc/cross-crate-info/cargo-transitive/auxiliary/q.rs
index e32fdbaabfe21..932a0b17206d6 100644
--- a/tests/rustdoc/cross-crate-info/cargo-transitive/auxiliary/q.rs
+++ b/tests/rustdoc/cross-crate-info/cargo-transitive/auxiliary/q.rs
@@ -2,5 +2,4 @@
 //@ doc-flags:--enable-index-page
 //@ doc-flags:-Zunstable-options
 
-
 pub struct Quebec;
diff --git a/tests/rustdoc/cross-crate-info/cargo-transitive/auxiliary/t.rs b/tests/rustdoc/cross-crate-info/cargo-transitive/auxiliary/t.rs
index 0a49072b68a67..c21a59c65188f 100644
--- a/tests/rustdoc/cross-crate-info/cargo-transitive/auxiliary/t.rs
+++ b/tests/rustdoc/cross-crate-info/cargo-transitive/auxiliary/t.rs
@@ -3,6 +3,5 @@
 //@ doc-flags:--enable-index-page
 //@ doc-flags:-Zunstable-options
 
-
 extern crate q;
 pub trait Tango {}
diff --git a/tests/rustdoc/cross-crate-info/cargo-transitive/s.rs b/tests/rustdoc/cross-crate-info/cargo-transitive/s.rs
index 4383d089c4c73..68bfc34883bd8 100644
--- a/tests/rustdoc/cross-crate-info/cargo-transitive/s.rs
+++ b/tests/rustdoc/cross-crate-info/cargo-transitive/s.rs
@@ -19,7 +19,6 @@
 
 // We document multiple crates into the same output directory, which
 // merges the cross-crate information. Everything is available.
-
 extern crate t;
 pub struct Sierra;
 impl t::Tango for Sierra {}
diff --git a/tests/rustdoc/cross-crate-info/cargo-two-no-index/auxiliary/f.rs b/tests/rustdoc/cross-crate-info/cargo-two-no-index/auxiliary/f.rs
index 1bfc785535b53..abc580a388cd2 100644
--- a/tests/rustdoc/cross-crate-info/cargo-two-no-index/auxiliary/f.rs
+++ b/tests/rustdoc/cross-crate-info/cargo-two-no-index/auxiliary/f.rs
@@ -1,4 +1,2 @@
 //@ build-aux-docs
-
-
 pub trait Foxtrot {}
diff --git a/tests/rustdoc/cross-crate-info/cargo-two-no-index/e.rs b/tests/rustdoc/cross-crate-info/cargo-two-no-index/e.rs
index 7d64f9481fb80..c93298f969eab 100644
--- a/tests/rustdoc/cross-crate-info/cargo-two-no-index/e.rs
+++ b/tests/rustdoc/cross-crate-info/cargo-two-no-index/e.rs
@@ -1,6 +1,5 @@
 //@ aux-build:f.rs
 //@ build-aux-docs
-
 //@ has e/enum.Echo.html
 //@ has f/trait.Foxtrot.html
 //@ hasraw e/enum.Echo.html 'Foxtrot'
@@ -10,7 +9,6 @@
 
 // document two crates in the same way that cargo does. do not provide
 // --enable-index-page
-
 extern crate f;
 pub enum Echo {}
 impl f::Foxtrot for Echo {}
diff --git a/tests/rustdoc/cross-crate-info/cargo-two/auxiliary/f.rs b/tests/rustdoc/cross-crate-info/cargo-two/auxiliary/f.rs
index e84835b3e3102..a2a7033b13112 100644
--- a/tests/rustdoc/cross-crate-info/cargo-two/auxiliary/f.rs
+++ b/tests/rustdoc/cross-crate-info/cargo-two/auxiliary/f.rs
@@ -2,5 +2,4 @@
 //@ doc-flags:--enable-index-page
 //@ doc-flags:-Zunstable-options
 
-
 pub trait Foxtrot {}
diff --git a/tests/rustdoc/cross-crate-info/cargo-two/e.rs b/tests/rustdoc/cross-crate-info/cargo-two/e.rs
index 29e3571e6c209..00f86cbc34889 100644
--- a/tests/rustdoc/cross-crate-info/cargo-two/e.rs
+++ b/tests/rustdoc/cross-crate-info/cargo-two/e.rs
@@ -16,7 +16,6 @@
 
 // document two crates in the same way that cargo does, writing them both
 // into the same output directory
-
 extern crate f;
 pub enum Echo {}
 impl f::Foxtrot for Echo {}
diff --git a/tests/rustdoc/cross-crate-info/index-on-last/auxiliary/f.rs b/tests/rustdoc/cross-crate-info/index-on-last/auxiliary/f.rs
index 1bfc785535b53..abc580a388cd2 100644
--- a/tests/rustdoc/cross-crate-info/index-on-last/auxiliary/f.rs
+++ b/tests/rustdoc/cross-crate-info/index-on-last/auxiliary/f.rs
@@ -1,4 +1,2 @@
 //@ build-aux-docs
-
-
 pub trait Foxtrot {}
diff --git a/tests/rustdoc/cross-crate-info/index-on-last/e.rs b/tests/rustdoc/cross-crate-info/index-on-last/e.rs
index fee9f4bcddbe4..ffee898cd966d 100644
--- a/tests/rustdoc/cross-crate-info/index-on-last/e.rs
+++ b/tests/rustdoc/cross-crate-info/index-on-last/e.rs
@@ -15,7 +15,6 @@
 //@ hasraw search-index.js 'Echo'
 
 // only declare --enable-index-page to the last rustdoc invocation
-
 extern crate f;
 pub enum Echo {}
 impl f::Foxtrot for Echo {}
diff --git a/tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/q.rs b/tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/q.rs
index e32fdbaabfe21..932a0b17206d6 100644
--- a/tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/q.rs
+++ b/tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/q.rs
@@ -2,5 +2,4 @@
 //@ doc-flags:--enable-index-page
 //@ doc-flags:-Zunstable-options
 
-
 pub struct Quebec;
diff --git a/tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/r.rs b/tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/r.rs
index c0ada1aea1a85..2c0db2abc53d9 100644
--- a/tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/r.rs
+++ b/tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/r.rs
@@ -3,6 +3,5 @@
 //@ doc-flags:--enable-index-page
 //@ doc-flags:-Zunstable-options
 
-
 extern crate s;
 pub type Romeo = s::Sierra;
diff --git a/tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/s.rs b/tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/s.rs
index 6b414a289f5a8..355d3f1aaa883 100644
--- a/tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/s.rs
+++ b/tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/s.rs
@@ -3,7 +3,6 @@
 //@ doc-flags:--enable-index-page
 //@ doc-flags:-Zunstable-options
 
-
 extern crate t;
 pub struct Sierra;
 impl t::Tango for Sierra {}
diff --git a/tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/t.rs b/tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/t.rs
index 0a49072b68a67..c21a59c65188f 100644
--- a/tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/t.rs
+++ b/tests/rustdoc/cross-crate-info/kitchen-sink/auxiliary/t.rs
@@ -3,6 +3,5 @@
 //@ doc-flags:--enable-index-page
 //@ doc-flags:-Zunstable-options
 
-
 extern crate q;
 pub trait Tango {}
diff --git a/tests/rustdoc/cross-crate-info/kitchen-sink/i.rs b/tests/rustdoc/cross-crate-info/kitchen-sink/i.rs
index 558f47ab7c4c9..bcb9464795af2 100644
--- a/tests/rustdoc/cross-crate-info/kitchen-sink/i.rs
+++ b/tests/rustdoc/cross-crate-info/kitchen-sink/i.rs
@@ -1,7 +1,7 @@
-//@ aux-build:q.rs
 //@ aux-build:r.rs
-//@ aux-build:s.rs
+//@ aux-build:q.rs
 //@ aux-build:t.rs
+//@ aux-build:s.rs
 //@ build-aux-docs
 //@ doc-flags:--enable-index-page
 //@ doc-flags:-Zunstable-options
@@ -28,5 +28,3 @@
 //@ hasraw type.impl/s/struct.Sierra.js 'Romeo'
 
 // document everything in the default mode
-
-
diff --git a/tests/rustdoc/cross-crate-info/single-crate-baseline/q.rs b/tests/rustdoc/cross-crate-info/single-crate-baseline/q.rs
index aa5cff73e7463..c5e3dc0a0f4e5 100644
--- a/tests/rustdoc/cross-crate-info/single-crate-baseline/q.rs
+++ b/tests/rustdoc/cross-crate-info/single-crate-baseline/q.rs
@@ -9,5 +9,4 @@
 //@ hasraw search-index.js 'Quebec'
 
 // there's nothing cross-crate going on here
-
 pub struct Quebec;
diff --git a/tests/rustdoc/cross-crate-info/single-crate-no-index/q.rs b/tests/rustdoc/cross-crate-info/single-crate-no-index/q.rs
index 0cd126e8247d9..d3e71fa0ce35f 100644
--- a/tests/rustdoc/cross-crate-info/single-crate-no-index/q.rs
+++ b/tests/rustdoc/cross-crate-info/single-crate-no-index/q.rs
@@ -1,8 +1,6 @@
 //@ build-aux-docs
-
 //@ has q/struct.Quebec.html
 //@ hasraw search-index.js 'Quebec'
 
 // there's nothing cross-crate going on here
-
 pub struct Quebec;
diff --git a/tests/rustdoc/cross-crate-info/transitive/auxiliary/q.rs b/tests/rustdoc/cross-crate-info/transitive/auxiliary/q.rs
index 32ad96d3e8e36..5d0881029cb2f 100644
--- a/tests/rustdoc/cross-crate-info/transitive/auxiliary/q.rs
+++ b/tests/rustdoc/cross-crate-info/transitive/auxiliary/q.rs
@@ -1,4 +1,2 @@
 //@ build-aux-docs
-
-
 pub struct Quebec;
diff --git a/tests/rustdoc/cross-crate-info/transitive/auxiliary/t.rs b/tests/rustdoc/cross-crate-info/transitive/auxiliary/t.rs
index e03c9a1c68c3b..fab9ec4a92b96 100644
--- a/tests/rustdoc/cross-crate-info/transitive/auxiliary/t.rs
+++ b/tests/rustdoc/cross-crate-info/transitive/auxiliary/t.rs
@@ -1,6 +1,4 @@
 //@ aux-build:q.rs
 //@ build-aux-docs
-
-
 extern crate q;
 pub trait Tango {}
diff --git a/tests/rustdoc/cross-crate-info/transitive/s.rs b/tests/rustdoc/cross-crate-info/transitive/s.rs
index a758bd68be22f..0a4e5f646ddaa 100644
--- a/tests/rustdoc/cross-crate-info/transitive/s.rs
+++ b/tests/rustdoc/cross-crate-info/transitive/s.rs
@@ -1,9 +1,6 @@
 //@ aux-build:t.rs
 //@ build-aux-docs
-
-
 // simple test to see if we support building transitive crates
-
 extern crate t;
 pub struct Sierra;
 impl t::Tango for Sierra {}
diff --git a/tests/rustdoc/cross-crate-info/two/auxiliary/f.rs b/tests/rustdoc/cross-crate-info/two/auxiliary/f.rs
index 1bfc785535b53..abc580a388cd2 100644
--- a/tests/rustdoc/cross-crate-info/two/auxiliary/f.rs
+++ b/tests/rustdoc/cross-crate-info/two/auxiliary/f.rs
@@ -1,4 +1,2 @@
 //@ build-aux-docs
-
-
 pub trait Foxtrot {}
diff --git a/tests/rustdoc/cross-crate-info/two/e.rs b/tests/rustdoc/cross-crate-info/two/e.rs
index 431e73c390d4a..9665af62706d1 100644
--- a/tests/rustdoc/cross-crate-info/two/e.rs
+++ b/tests/rustdoc/cross-crate-info/two/e.rs
@@ -1,9 +1,6 @@
 //@ aux-build:f.rs
 //@ build-aux-docs
-
-
 // simple test to assert that we can do a two-level aux-build
-
 extern crate f;
 pub enum Echo {}
 impl f::Foxtrot for Echo {}
diff --git a/tests/rustdoc/cross-crate-info/working-dir-examples/q.rs b/tests/rustdoc/cross-crate-info/working-dir-examples/q.rs
index ceced1fc46dcd..a7ab062fd9e28 100644
--- a/tests/rustdoc/cross-crate-info/working-dir-examples/q.rs
+++ b/tests/rustdoc/cross-crate-info/working-dir-examples/q.rs
@@ -7,5 +7,4 @@
 
 // where will --scrape-examples-output-path resolve the path to be?
 // should be the root output directory
-
 pub struct Quebec;
diff --git a/tests/rustdoc/cross-crate-info/write-docs-somewhere-else/auxiliary/f.rs b/tests/rustdoc/cross-crate-info/write-docs-somewhere-else/auxiliary/f.rs
index 52e02e8b89715..f8c9adcaf9cad 100644
--- a/tests/rustdoc/cross-crate-info/write-docs-somewhere-else/auxiliary/f.rs
+++ b/tests/rustdoc/cross-crate-info/write-docs-somewhere-else/auxiliary/f.rs
@@ -1,5 +1,3 @@
 //@ build-aux-docs
 //@ unique-doc-out-dir
-
-
 pub trait Foxtrot {}
diff --git a/tests/rustdoc/cross-crate-info/write-docs-somewhere-else/e.rs b/tests/rustdoc/cross-crate-info/write-docs-somewhere-else/e.rs
index 6247ed5fd9660..9dcec211e1787 100644
--- a/tests/rustdoc/cross-crate-info/write-docs-somewhere-else/e.rs
+++ b/tests/rustdoc/cross-crate-info/write-docs-somewhere-else/e.rs
@@ -1,6 +1,5 @@
 //@ aux-build:f.rs
 //@ build-aux-docs
-
 //@ has e/enum.Echo.html
 //@ !has f/trait.Foxtrot.html
 //@ hasraw e/enum.Echo.html 'Foxtrot'
@@ -10,7 +9,6 @@
 
 // test the fact that our test runner will document this crate somewhere
 // else
-
 extern crate f;
 pub enum Echo {}
 impl f::Foxtrot for Echo {}

From 12d87ee237a1f79c00ccd22424432f5ab869ed22 Mon Sep 17 00:00:00 2001
From: EtomicBomb <ethan@ethan.ws>
Date: Thu, 25 Jul 2024 17:59:32 +0000
Subject: [PATCH 04/16] file_stem and comment per notriddle

---
 src/tools/compiletest/src/runtest.rs | 11 +++--------
 1 file changed, 3 insertions(+), 8 deletions(-)

diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 2d298dfbc06f7..2beff593f08ad 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -1610,6 +1610,8 @@ impl<'test> TestCx<'test> {
                 };
                 // Create the directory for the stdout/stderr files.
                 create_dir_all(aux_cx.output_base_dir()).unwrap();
+                // use root_testpaths here, because aux-builds should have the
+                // same --out-dir and auxiliary directory.
                 let auxres = aux_cx.document(&root_out_dir, root_testpaths);
                 if !auxres.status.success() {
                     return auxres;
@@ -1624,14 +1626,7 @@ impl<'test> TestCx<'test> {
         // actual --out-dir given to the auxiliary or test, as opposed to the root out dir for the entire
         // test
         let out_dir: Cow<'_, Path> = if self.props.unique_doc_out_dir {
-            let file_name = self
-                .testpaths
-                .file
-                .file_name()
-                .expect("file name should not be empty")
-                .to_str()
-                .expect("file name utf8")
-                .trim_end_matches(".rs");
+            let file_name = self.testpaths.file.file_stem().expect("file name should not be empty");
             let out_dir = PathBuf::from_iter([
                 root_out_dir,
                 Path::new("docs"),

From f91da72cb75f03739985926c5e5561611f783b80 Mon Sep 17 00:00:00 2001
From: EtomicBomb <ethan@ethan.ws>
Date: Mon, 29 Jul 2024 14:41:09 +0000
Subject: [PATCH 05/16] merge conflicts; fix rebase duplicating imports

---
 src/tools/compiletest/src/runtest.rs | 11 -----------
 1 file changed, 11 deletions(-)

diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 2beff593f08ad..55f676e708670 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -1,16 +1,5 @@
 // ignore-tidy-filelength
 
-use crate::compute_diff::{write_diff, write_filtered_diff};
-use crate::errors::{self, Error, ErrorKind};
-use crate::header::TestProps;
-use crate::json;
-use crate::read2::{read2_abbreviated, Truncated};
-use crate::util::{add_dylib_path, copy_dir_all, dylib_env_var, logv, static_regex, PathBufExt};
-use crate::ColorConfig;
-use colored::Colorize;
-use miropt_test_tools::{files_for_miropt_test, MiroptTest, MiroptTestFile};
-use regex::{Captures, Regex};
-use rustfix::{apply_suggestions, get_suggestions_from_json, Filter};
 use std::borrow::Cow;
 use std::collections::{HashMap, HashSet};
 use std::ffi::{OsStr, OsString};

From 4feb949617c9a24f8750d8a69197f086b0005cee Mon Sep 17 00:00:00 2001
From: binarycat <binarycat@envs.net>
Date: Wed, 24 Jul 2024 14:05:58 -0400
Subject: [PATCH 06/16] migrate fmt-write-bloat to rmake

---
 src/tools/run-make-support/src/env.rs         |  7 +++
 src/tools/run-make-support/src/lib.rs         |  1 +
 src/tools/run-make-support/src/symbols.rs     | 43 +++++++++++++++++++
 .../tidy/src/allowed_run_make_makefiles.txt   |  1 -
 tests/run-make/fmt-write-bloat/Makefile       | 25 -----------
 tests/run-make/fmt-write-bloat/rmake.rs       | 35 +++++++++++++++
 6 files changed, 86 insertions(+), 26 deletions(-)
 create mode 100644 src/tools/run-make-support/src/symbols.rs
 delete mode 100644 tests/run-make/fmt-write-bloat/Makefile
 create mode 100644 tests/run-make/fmt-write-bloat/rmake.rs

diff --git a/src/tools/run-make-support/src/env.rs b/src/tools/run-make-support/src/env.rs
index f52524e7d54cd..e6460fb93d3e9 100644
--- a/src/tools/run-make-support/src/env.rs
+++ b/src/tools/run-make-support/src/env.rs
@@ -17,3 +17,10 @@ pub fn env_var_os(name: &str) -> OsString {
         None => panic!("failed to retrieve environment variable {name:?}"),
     }
 }
+
+/// Check if `NO_DEBUG_ASSERTIONS` is set (usually this may be set in CI jobs).
+#[track_caller]
+#[must_use]
+pub fn no_debug_assertions() -> bool {
+    std::env::var_os("NO_DEBUG_ASSERTIONS").is_some()
+}
diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs
index 085120764b463..36ea6125f2780 100644
--- a/src/tools/run-make-support/src/lib.rs
+++ b/src/tools/run-make-support/src/lib.rs
@@ -21,6 +21,7 @@ pub mod run;
 pub mod scoped_run;
 pub mod string;
 pub mod targets;
+pub mod symbols;
 
 // Internally we call our fs-related support module as `fs`, but re-export its content as `rfs`
 // to tests to avoid colliding with commonly used `use std::fs;`.
diff --git a/src/tools/run-make-support/src/symbols.rs b/src/tools/run-make-support/src/symbols.rs
new file mode 100644
index 0000000000000..52119ba4991a8
--- /dev/null
+++ b/src/tools/run-make-support/src/symbols.rs
@@ -0,0 +1,43 @@
+use object::{self, Object, ObjectSymbol, SymbolIterator};
+use std::path::Path;
+
+/// Iterate through the symbols in an object file.
+///
+/// Uses a callback because `SymbolIterator` does not own its data.
+///
+/// Panics if `path` is not a valid object file readable by the current user.
+pub fn with_symbol_iter<P, F, R>(path: P, func: F) -> R
+where
+    P: AsRef<Path>,
+    F: FnOnce(&mut SymbolIterator<'_, '_>) -> R,
+{
+    let raw_bytes = crate::fs::read(path);
+    let f = object::File::parse(raw_bytes.as_slice()).expect("unable to parse file");
+    let mut iter = f.symbols();
+    func(&mut iter)
+}
+
+/// Check an object file's symbols for substrings.
+///
+/// Returns `true` if any of the symbols found in the object file at
+/// `path` contain a substring listed in `substrings`.
+///
+/// Panics if `path` is not a valid object file readable by the current user.
+pub fn any_symbol_contains(path: impl AsRef<Path>, substrings: &[&str]) -> bool {
+    with_symbol_iter(path, |syms| {
+        for sym in syms {
+            for substring in substrings {
+                if sym
+                    .name_bytes()
+                    .unwrap()
+                    .windows(substring.len())
+                    .any(|x| x == substring.as_bytes())
+                {
+                    eprintln!("{:?} contains {}", sym, substring);
+                    return true;
+                }
+            }
+        }
+        false
+    })
+}
diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt
index a84b89ff4a113..db06040de2b3a 100644
--- a/src/tools/tidy/src/allowed_run_make_makefiles.txt
+++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt
@@ -10,7 +10,6 @@ run-make/dep-info-spaces/Makefile
 run-make/dep-info/Makefile
 run-make/emit-to-stdout/Makefile
 run-make/extern-fn-reachable/Makefile
-run-make/fmt-write-bloat/Makefile
 run-make/foreign-double-unwind/Makefile
 run-make/foreign-exceptions/Makefile
 run-make/incr-add-rust-src-component/Makefile
diff --git a/tests/run-make/fmt-write-bloat/Makefile b/tests/run-make/fmt-write-bloat/Makefile
deleted file mode 100644
index 70e04b9af51fe..0000000000000
--- a/tests/run-make/fmt-write-bloat/Makefile
+++ /dev/null
@@ -1,25 +0,0 @@
-include ../tools.mk
-
-# ignore-windows
-
-ifeq ($(shell $(RUSTC) -vV | grep 'host: $(TARGET)'),)
-
-# Don't run this test when cross compiling.
-all:
-
-else
-
-NM = nm
-
-PANIC_SYMS = panic_bounds_check Debug
-
-# Allow for debug_assert!() in debug builds of std.
-ifdef NO_DEBUG_ASSERTIONS
-PANIC_SYMS += panicking panic_fmt pad_integral Display Debug
-endif
-
-all: main.rs
-	$(RUSTC) $< -O
-	$(NM) $(call RUN_BINFILE,main) | $(CGREP) -v $(PANIC_SYMS)
-
-endif
diff --git a/tests/run-make/fmt-write-bloat/rmake.rs b/tests/run-make/fmt-write-bloat/rmake.rs
new file mode 100644
index 0000000000000..120c1cbccdd3c
--- /dev/null
+++ b/tests/run-make/fmt-write-bloat/rmake.rs
@@ -0,0 +1,35 @@
+//! Before #78122, writing any `fmt::Arguments` would trigger the inclusion of `usize` formatting
+//! and padding code in the resulting binary, because indexing used in `fmt::write` would generate
+//! code using `panic_bounds_check`, which prints the index and length.
+//!
+//! These bounds checks are not necessary, as `fmt::Arguments` never contains any out-of-bounds
+//! indexes. The test is a `run-make` test, because it needs to check the result after linking. A
+//! codegen or assembly test doesn't check the parts that will be pulled in from `core` by the
+//! linker.
+//!
+//! In this test, we try to check that the `usize` formatting and padding code are not present in
+//! the final binary by checking that panic symbols such as `panic_bounds_check` are **not**
+//! present.
+//!
+//! Some CI jobs try to run faster by disabling debug assertions (through setting
+//! `NO_DEBUG_ASSERTIONS=1`). If debug assertions are disabled, then we can check for the absence of
+//! additional `usize` formatting and padding related symbols.
+
+// Reason: This test is `ignore-windows` because the `no_std` test (using `#[link(name = "c")])`
+// doesn't link on windows.
+//@ ignore-windows
+//@ ignore-cross-compile
+
+use run_make_support::{env::no_debug_assertions, rustc, symbols::any_symbol_contains};
+
+fn main() {
+    rustc().input("main.rs").opt().run();
+    // panic machinery identifiers, these should not appear in the final binary
+    let mut panic_syms = vec!["panic_bounds_check", "Debug"];
+    if no_debug_assertions() {
+        // if debug assertions are allowed, we need to allow these,
+        // otherwise, add them to the list of symbols to deny.
+        panic_syms.extend_from_slice(&["panicking", "panic_fmt", "pad_integral", "Display"]);
+    }
+    assert!(!any_symbol_contains("main", &panic_syms));
+}

From ebd6718218642b8b63584214e35f7d69d24ae845 Mon Sep 17 00:00:00 2001
From: binarycat <binarycat@envs.net>
Date: Tue, 30 Jul 2024 13:33:25 -0400
Subject: [PATCH 07/16] tidy

this commit cannot easily be squashed, since there is already a
PR based on the previous commit.
---
 src/tools/run-make-support/src/symbols.rs | 3 ++-
 tests/run-make/fmt-write-bloat/rmake.rs   | 4 +++-
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/src/tools/run-make-support/src/symbols.rs b/src/tools/run-make-support/src/symbols.rs
index 52119ba4991a8..fd0c866bcc927 100644
--- a/src/tools/run-make-support/src/symbols.rs
+++ b/src/tools/run-make-support/src/symbols.rs
@@ -1,6 +1,7 @@
-use object::{self, Object, ObjectSymbol, SymbolIterator};
 use std::path::Path;
 
+use object::{self, Object, ObjectSymbol, SymbolIterator};
+
 /// Iterate through the symbols in an object file.
 ///
 /// Uses a callback because `SymbolIterator` does not own its data.
diff --git a/tests/run-make/fmt-write-bloat/rmake.rs b/tests/run-make/fmt-write-bloat/rmake.rs
index 120c1cbccdd3c..4ae226ec0e234 100644
--- a/tests/run-make/fmt-write-bloat/rmake.rs
+++ b/tests/run-make/fmt-write-bloat/rmake.rs
@@ -20,7 +20,9 @@
 //@ ignore-windows
 //@ ignore-cross-compile
 
-use run_make_support::{env::no_debug_assertions, rustc, symbols::any_symbol_contains};
+use run_make_support::env::no_debug_assertions;
+use run_make_support::rustc;
+use run_make_support::symbols::any_symbol_contains;
 
 fn main() {
     rustc().input("main.rs").opt().run();

From bd23e0eb2694752f573eafd0d894a2b16d10fd7b Mon Sep 17 00:00:00 2001
From: EtomicBomb <ethan@ethan.ws>
Date: Wed, 31 Jul 2024 00:20:56 +0000
Subject: [PATCH 08/16] canonicalize path in another place to fix #128411

---
 src/tools/compiletest/src/runtest.rs | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 55f676e708670..2b7601a7c73b5 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -2968,7 +2968,9 @@ impl<'test> TestCx<'test> {
         for _ in res.stdout.split('\n').filter(|s| s.starts_with("test ")).inspect(|s| {
             if let Some((left, right)) = s.split_once(" - ") {
                 let path = left.rsplit("test ").next().unwrap();
-                if let Some(ref mut v) = files.get_mut(&path.replace('\\', "/")) {
+                let path = fs::canonicalize(&path).expect("failed to canonicalize");
+                let path = path.to_str().unwrap().replace('\\', "/");
+                if let Some(ref mut v) = files.get_mut(&path) {
                     tested += 1;
                     let mut iter = right.split("(line ");
                     iter.next();

From 281c2fd5bf1604392bde16e32c7d57d2772406e6 Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote <n.nethercote@gmail.com>
Date: Wed, 31 Jul 2024 12:08:55 +1000
Subject: [PATCH 09/16] Inline and remove `parse_local_mk`.

It has a single use. This makes the `let` handling case in
`parse_stmt_without_recovery` more similar to the statement path and
statement expression cases.
---
 compiler/rustc_parse/src/parser/stmt.rs | 22 ++++++----------------
 1 file changed, 6 insertions(+), 16 deletions(-)

diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs
index 7b0daaa14335f..28122a3ae294a 100644
--- a/compiler/rustc_parse/src/parser/stmt.rs
+++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -66,7 +66,12 @@ impl<'a> Parser<'a> {
         }
 
         Ok(Some(if self.token.is_keyword(kw::Let) {
-            self.parse_local_mk(lo, attrs, capture_semi, force_collect)?
+            self.collect_tokens_trailing_token(attrs, force_collect, |this, attrs| {
+                this.expect_keyword(kw::Let)?;
+                let local = this.parse_local(attrs)?;
+                let trailing = capture_semi && this.token.kind == token::Semi;
+                Ok((this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Let(local)), trailing))
+            })?
         } else if self.is_kw_followed_by_ident(kw::Mut) && self.may_recover() {
             self.recover_stmt_local_after_let(
                 lo,
@@ -247,21 +252,6 @@ impl<'a> Parser<'a> {
         Ok(stmt)
     }
 
-    fn parse_local_mk(
-        &mut self,
-        lo: Span,
-        attrs: AttrWrapper,
-        capture_semi: bool,
-        force_collect: ForceCollect,
-    ) -> PResult<'a, Stmt> {
-        self.collect_tokens_trailing_token(attrs, force_collect, |this, attrs| {
-            this.expect_keyword(kw::Let)?;
-            let local = this.parse_local(attrs)?;
-            let trailing = capture_semi && this.token.kind == token::Semi;
-            Ok((this.mk_stmt(lo.to(this.prev_token.span), StmtKind::Let(local)), trailing))
-        })
-    }
-
     /// Parses a local variable declaration.
     fn parse_local(&mut self, attrs: AttrVec) -> PResult<'a, P<Local>> {
         let lo = self.prev_token.span;

From fe647f053882f5745dfb339f22f13435bf8c4ca5 Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote <n.nethercote@gmail.com>
Date: Wed, 31 Jul 2024 12:56:25 +1000
Subject: [PATCH 10/16] Remove `LhsExpr`.

`parse_expr_assoc_with` has an awkward structure -- sometimes the lhs is
already parsed. This commit splits the post-lhs part into a new method
`parse_expr_assoc_rest_with`, which makes everything shorter and
simpler.
---
 compiler/rustc_parse/src/parser/expr.rs | 57 ++++++++++---------------
 compiler/rustc_parse/src/parser/pat.rs  |  7 +--
 compiler/rustc_parse/src/parser/stmt.rs |  6 +--
 3 files changed, 28 insertions(+), 42 deletions(-)

diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index a242dc5cd582d..24e56c5bdee66 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -40,14 +40,6 @@ use super::{
 };
 use crate::{errors, maybe_recover_from_interpolated_ty_qpath};
 
-#[derive(Debug)]
-pub(super) enum LhsExpr {
-    // Already parsed just the outer attributes.
-    Unparsed { attrs: AttrWrapper },
-    // Already parsed the expression.
-    Parsed { expr: P<Expr>, starts_statement: bool },
-}
-
 #[derive(Debug)]
 enum DestructuredFloat {
     /// 1e2
@@ -113,30 +105,31 @@ impl<'a> Parser<'a> {
         r: Restrictions,
         attrs: AttrWrapper,
     ) -> PResult<'a, P<Expr>> {
-        self.with_res(r, |this| this.parse_expr_assoc_with(0, LhsExpr::Unparsed { attrs }))
+        self.with_res(r, |this| this.parse_expr_assoc_with(0, attrs))
     }
 
     /// Parses an associative expression with operators of at least `min_prec` precedence.
     pub(super) fn parse_expr_assoc_with(
         &mut self,
         min_prec: usize,
-        lhs: LhsExpr,
+        attrs: AttrWrapper,
     ) -> PResult<'a, P<Expr>> {
-        let mut starts_stmt = false;
-        let mut lhs = match lhs {
-            LhsExpr::Parsed { expr, starts_statement } => {
-                starts_stmt = starts_statement;
-                expr
-            }
-            LhsExpr::Unparsed { attrs } => {
-                if self.token.is_range_separator() {
-                    return self.parse_expr_prefix_range(attrs);
-                } else {
-                    self.parse_expr_prefix(attrs)?
-                }
-            }
+        let lhs = if self.token.is_range_separator() {
+            return self.parse_expr_prefix_range(attrs);
+        } else {
+            self.parse_expr_prefix(attrs)?
         };
+        self.parse_expr_assoc_rest_with(min_prec, false, lhs)
+    }
 
+    /// Parses the rest of an associative expression (i.e. the part after the lhs) with operators
+    /// of at least `min_prec` precedence.
+    pub(super) fn parse_expr_assoc_rest_with(
+        &mut self,
+        min_prec: usize,
+        starts_stmt: bool,
+        mut lhs: P<Expr>,
+    ) -> PResult<'a, P<Expr>> {
         if !self.should_continue_as_assoc_expr(&lhs) {
             return Ok(lhs);
         }
@@ -272,7 +265,7 @@ impl<'a> Parser<'a> {
             };
             let rhs = self.with_res(restrictions - Restrictions::STMT_EXPR, |this| {
                 let attrs = this.parse_outer_attributes()?;
-                this.parse_expr_assoc_with(prec + prec_adjustment, LhsExpr::Unparsed { attrs })
+                this.parse_expr_assoc_with(prec + prec_adjustment, attrs)
             })?;
 
             let span = self.mk_expr_sp(&lhs, lhs_span, rhs.span);
@@ -447,7 +440,7 @@ impl<'a> Parser<'a> {
             let maybe_lt = self.token.clone();
             let attrs = self.parse_outer_attributes()?;
             Some(
-                self.parse_expr_assoc_with(prec + 1, LhsExpr::Unparsed { attrs })
+                self.parse_expr_assoc_with(prec + 1, attrs)
                     .map_err(|err| self.maybe_err_dotdotlt_syntax(maybe_lt, err))?,
             )
         } else {
@@ -504,12 +497,9 @@ impl<'a> Parser<'a> {
             let (span, opt_end) = if this.is_at_start_of_range_notation_rhs() {
                 // RHS must be parsed with more associativity than the dots.
                 let attrs = this.parse_outer_attributes()?;
-                this.parse_expr_assoc_with(
-                    op.unwrap().precedence() + 1,
-                    LhsExpr::Unparsed { attrs },
-                )
-                .map(|x| (lo.to(x.span), Some(x)))
-                .map_err(|err| this.maybe_err_dotdotlt_syntax(maybe_lt, err))?
+                this.parse_expr_assoc_with(op.unwrap().precedence() + 1, attrs)
+                    .map(|x| (lo.to(x.span), Some(x)))
+                    .map_err(|err| this.maybe_err_dotdotlt_syntax(maybe_lt, err))?
             } else {
                 (lo, None)
             };
@@ -2645,10 +2635,7 @@ impl<'a> Parser<'a> {
             self.expect(&token::Eq)?;
         }
         let attrs = self.parse_outer_attributes()?;
-        let expr = self.parse_expr_assoc_with(
-            1 + prec_let_scrutinee_needs_par(),
-            LhsExpr::Unparsed { attrs },
-        )?;
+        let expr = self.parse_expr_assoc_with(1 + prec_let_scrutinee_needs_par(), attrs)?;
         let span = lo.to(expr.span);
         Ok(self.mk_expr(span, ExprKind::Let(pat, expr, span, recovered)))
     }
diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index b6f85cc90324c..5bfb8bdf776a0 100644
--- a/compiler/rustc_parse/src/parser/pat.rs
+++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -25,7 +25,7 @@ use crate::errors::{
     UnexpectedParenInRangePat, UnexpectedParenInRangePatSugg,
     UnexpectedVertVertBeforeFunctionParam, UnexpectedVertVertInPattern, WrapInParens,
 };
-use crate::parser::expr::{could_be_unclosed_char_literal, LhsExpr};
+use crate::parser::expr::could_be_unclosed_char_literal;
 use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
 
 #[derive(PartialEq, Copy, Clone)]
@@ -403,8 +403,9 @@ impl<'a> Parser<'a> {
 
             // Parse an associative expression such as `+ expr`, `% expr`, ...
             // Assignements, ranges and `|` are disabled by [`Restrictions::IS_PAT`].
-            let lhs = LhsExpr::Parsed { expr, starts_statement: false };
-            if let Ok(expr) = snapshot.parse_expr_assoc_with(0, lhs).map_err(|err| err.cancel()) {
+            if let Ok(expr) =
+                snapshot.parse_expr_assoc_rest_with(0, false, expr).map_err(|err| err.cancel())
+            {
                 // We got a valid expression.
                 self.restore_snapshot(snapshot);
                 self.restrictions.remove(Restrictions::IS_PAT);
diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs
index 28122a3ae294a..e0676b72634e7 100644
--- a/compiler/rustc_parse/src/parser/stmt.rs
+++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -17,7 +17,6 @@ use thin_vec::{thin_vec, ThinVec};
 
 use super::attr::InnerAttrForbiddenReason;
 use super::diagnostics::AttemptLocalParseRecovery;
-use super::expr::LhsExpr;
 use super::pat::{PatternLocation, RecoverComma};
 use super::path::PathStyle;
 use super::{
@@ -178,7 +177,7 @@ impl<'a> Parser<'a> {
             // Perform this outside of the `collect_tokens_trailing_token` closure,
             // since our outer attributes do not apply to this part of the expression
             let expr = self.with_res(Restrictions::STMT_EXPR, |this| {
-                this.parse_expr_assoc_with(0, LhsExpr::Parsed { expr, starts_statement: true })
+                this.parse_expr_assoc_rest_with(0, true, expr)
             })?;
             Ok(self.mk_stmt(lo.to(self.prev_token.span), StmtKind::Expr(expr)))
         } else {
@@ -211,8 +210,7 @@ impl<'a> Parser<'a> {
             let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac));
             let e = self.maybe_recover_from_bad_qpath(e)?;
             let e = self.parse_expr_dot_or_call_with(attrs, e, lo)?;
-            let e = self
-                .parse_expr_assoc_with(0, LhsExpr::Parsed { expr: e, starts_statement: false })?;
+            let e = self.parse_expr_assoc_rest_with(0, false, e)?;
             StmtKind::Expr(e)
         };
         Ok(self.mk_stmt(lo.to(hi), kind))

From 2eb2ef1684e4df67389432fe0c1cc2776c790cd7 Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote <n.nethercote@gmail.com>
Date: Wed, 31 Jul 2024 19:16:09 +1000
Subject: [PATCH 11/16] Streamline attribute stitching on AST nodes.

It can be done more concisely.
---
 .../rustc_parse/src/parser/attr_wrapper.rs     |  7 +++----
 compiler/rustc_parse/src/parser/expr.rs        | 18 +++++++-----------
 2 files changed, 10 insertions(+), 15 deletions(-)

diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs
index 611dbc0535c61..ba20bd9daf708 100644
--- a/compiler/rustc_parse/src/parser/attr_wrapper.rs
+++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs
@@ -53,10 +53,9 @@ impl AttrWrapper {
 
     /// Prepend `self.attrs` to `attrs`.
     // FIXME: require passing an NT to prevent misuse of this method
-    pub(crate) fn prepend_to_nt_inner(self, attrs: &mut AttrVec) {
-        let mut self_attrs = self.attrs;
-        mem::swap(attrs, &mut self_attrs);
-        attrs.extend(self_attrs);
+    pub(crate) fn prepend_to_nt_inner(mut self, attrs: &mut AttrVec) {
+        mem::swap(attrs, &mut self.attrs);
+        attrs.extend(self.attrs);
     }
 
     pub fn is_empty(&self) -> bool {
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 24e56c5bdee66..ae5c8af7c2d0d 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -877,7 +877,7 @@ impl<'a> Parser<'a> {
         mut e: P<Expr>,
         lo: Span,
     ) -> PResult<'a, P<Expr>> {
-        let res = ensure_sufficient_stack(|| {
+        let mut res = ensure_sufficient_stack(|| {
             loop {
                 let has_question =
                     if self.prev_token.kind == TokenKind::Ident(kw::Return, IdentIsRaw::No) {
@@ -924,17 +924,13 @@ impl<'a> Parser<'a> {
 
         // Stitch the list of outer attributes onto the return value. A little
         // bit ugly, but the best way given the current code structure.
-        if attrs.is_empty() {
-            res
-        } else {
-            res.map(|expr| {
-                expr.map(|mut expr| {
-                    attrs.extend(expr.attrs);
-                    expr.attrs = attrs;
-                    expr
-                })
-            })
+        if !attrs.is_empty()
+            && let Ok(expr) = &mut res
+        {
+            mem::swap(&mut expr.attrs, &mut attrs);
+            expr.attrs.extend(attrs)
         }
+        res
     }
 
     pub(super) fn parse_dot_suffix_expr(

From 9d77d17f7127def53c6a3555ae9a6a1f271a8e37 Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote <n.nethercote@gmail.com>
Date: Thu, 1 Aug 2024 15:41:51 +1000
Subject: [PATCH 12/16] Move a comment to a better spot.

---
 compiler/rustc_parse/src/parser/stmt.rs | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs
index e0676b72634e7..b3efb87a4a26a 100644
--- a/compiler/rustc_parse/src/parser/stmt.rs
+++ b/compiler/rustc_parse/src/parser/stmt.rs
@@ -116,13 +116,12 @@ impl<'a> Parser<'a> {
                 }
             }
         } else if let Some(item) = self.parse_item_common(
-            attrs.clone(),
+            attrs.clone(), // FIXME: unwanted clone of attrs
             false,
             true,
             FnParseMode { req_name: |_| true, req_body: true },
             force_collect,
         )? {
-            // FIXME: Bad copy of attrs
             self.mk_stmt(lo.to(item.span), StmtKind::Item(P(item)))
         } else if self.eat(&token::Semi) {
             // Do not attempt to parse an expression if we're done here.

From d1f05fd1848fc68ed89d17f7937e358dacd8aed4 Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote <n.nethercote@gmail.com>
Date: Thu, 1 Aug 2024 06:44:39 +1000
Subject: [PATCH 13/16] Distinguish the two kinds of token range.

When collecting tokens there are two kinds of range:
- a range relative to the parser's full token stream (which we get when
  we are parsing);
- a range relative to a single AST node's token stream (which we use
  within `LazyAttrTokenStreamImpl` when replacing tokens).

These are currently both represented with `Range<u32>` and it's easy to
mix them up -- until now I hadn't properly understood the difference.

This commit introduces `ParserRange` and `NodeRange` to distinguish
them. This also requires splitting `ReplaceRange` in two, giving the new
types `ParserReplacement` and `NodeReplacement`. (These latter two names
reduce the overloading of the word "range".)

The commit also rewrites some comments to be clearer.

The end result is a little more verbose, but much clearer.
---
 compiler/rustc_builtin_macros/src/cfg_eval.rs |   2 +-
 compiler/rustc_parse/src/parser/attr.rs       |   6 +-
 .../rustc_parse/src/parser/attr_wrapper.rs    | 124 ++++++++++--------
 compiler/rustc_parse/src/parser/mod.rs        |  70 +++++++---
 4 files changed, 124 insertions(+), 78 deletions(-)

diff --git a/compiler/rustc_builtin_macros/src/cfg_eval.rs b/compiler/rustc_builtin_macros/src/cfg_eval.rs
index dc1874bfecbda..4b05c144d37d7 100644
--- a/compiler/rustc_builtin_macros/src/cfg_eval.rs
+++ b/compiler/rustc_builtin_macros/src/cfg_eval.rs
@@ -202,7 +202,7 @@ impl CfgEval<'_> {
         }
 
         // Now that we have our re-parsed `AttrTokenStream`, recursively configuring
-        // our attribute target will correctly the tokens as well.
+        // our attribute target will correctly configure the tokens as well.
         flat_map_annotatable(self, annotatable)
     }
 }
diff --git a/compiler/rustc_parse/src/parser/attr.rs b/compiler/rustc_parse/src/parser/attr.rs
index 12b9414d1f760..a98382efc3eba 100644
--- a/compiler/rustc_parse/src/parser/attr.rs
+++ b/compiler/rustc_parse/src/parser/attr.rs
@@ -8,7 +8,7 @@ use rustc_span::{sym, BytePos, Span};
 use thin_vec::ThinVec;
 use tracing::debug;
 
-use super::{AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, PathStyle};
+use super::{AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, ParserRange, PathStyle};
 use crate::{errors, fluent_generated as fluent, maybe_whole};
 
 // Public for rustfmt usage
@@ -307,8 +307,8 @@ impl<'a> Parser<'a> {
                 // inner attribute, for possible later processing in a `LazyAttrTokenStream`.
                 if let Capturing::Yes = self.capture_state.capturing {
                     let end_pos = self.num_bump_calls;
-                    let range = start_pos..end_pos;
-                    self.capture_state.inner_attr_ranges.insert(attr.id, range);
+                    let parser_range = ParserRange(start_pos..end_pos);
+                    self.capture_state.inner_attr_parser_ranges.insert(attr.id, parser_range);
                 }
                 attrs.push(attr);
             } else {
diff --git a/compiler/rustc_parse/src/parser/attr_wrapper.rs b/compiler/rustc_parse/src/parser/attr_wrapper.rs
index ba20bd9daf708..abf61036c2deb 100644
--- a/compiler/rustc_parse/src/parser/attr_wrapper.rs
+++ b/compiler/rustc_parse/src/parser/attr_wrapper.rs
@@ -10,7 +10,10 @@ use rustc_errors::PResult;
 use rustc_session::parse::ParseSess;
 use rustc_span::{sym, Span, DUMMY_SP};
 
-use super::{Capturing, FlatToken, ForceCollect, Parser, ReplaceRange, TokenCursor};
+use super::{
+    Capturing, FlatToken, ForceCollect, NodeRange, NodeReplacement, Parser, ParserRange,
+    TokenCursor,
+};
 
 /// A wrapper type to ensure that the parser handles outer attributes correctly.
 /// When we parse outer attributes, we need to ensure that we capture tokens
@@ -28,8 +31,8 @@ use super::{Capturing, FlatToken, ForceCollect, Parser, ReplaceRange, TokenCurso
 #[derive(Debug, Clone)]
 pub struct AttrWrapper {
     attrs: AttrVec,
-    // The start of the outer attributes in the token cursor.
-    // This allows us to create a `ReplaceRange` for the entire attribute
+    // The start of the outer attributes in the parser's token stream.
+    // This lets us create a `NodeReplacement` for the entire attribute
     // target, including outer attributes.
     start_pos: u32,
 }
@@ -88,7 +91,7 @@ struct LazyAttrTokenStreamImpl {
     cursor_snapshot: TokenCursor,
     num_calls: u32,
     break_last_token: bool,
-    replace_ranges: Box<[ReplaceRange]>,
+    node_replacements: Box<[NodeReplacement]>,
 }
 
 impl ToAttrTokenStream for LazyAttrTokenStreamImpl {
@@ -103,21 +106,24 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl {
             .chain(iter::repeat_with(|| FlatToken::Token(cursor_snapshot.next())))
             .take(self.num_calls as usize);
 
-        if self.replace_ranges.is_empty() {
+        if self.node_replacements.is_empty() {
             make_attr_token_stream(tokens, self.break_last_token)
         } else {
             let mut tokens: Vec<_> = tokens.collect();
-            let mut replace_ranges = self.replace_ranges.to_vec();
-            replace_ranges.sort_by_key(|(range, _)| range.start);
+            let mut node_replacements = self.node_replacements.to_vec();
+            node_replacements.sort_by_key(|(range, _)| range.0.start);
 
             #[cfg(debug_assertions)]
-            for [(range, tokens), (next_range, next_tokens)] in replace_ranges.array_windows() {
+            for [(node_range, tokens), (next_node_range, next_tokens)] in
+                node_replacements.array_windows()
+            {
                 assert!(
-                    range.end <= next_range.start || range.end >= next_range.end,
-                    "Replace ranges should either be disjoint or nested: ({:?}, {:?}) ({:?}, {:?})",
-                    range,
+                    node_range.0.end <= next_node_range.0.start
+                        || node_range.0.end >= next_node_range.0.end,
+                    "Node ranges should be disjoint or nested: ({:?}, {:?}) ({:?}, {:?})",
+                    node_range,
                     tokens,
-                    next_range,
+                    next_node_range,
                     next_tokens,
                 );
             }
@@ -135,20 +141,23 @@ impl ToAttrTokenStream for LazyAttrTokenStreamImpl {
             // start position, we ensure that any (outer) replace range which
             // encloses another (inner) replace range will fully overwrite the
             // inner range's replacement.
-            for (range, target) in replace_ranges.into_iter().rev() {
-                assert!(!range.is_empty(), "Cannot replace an empty range: {range:?}");
+            for (node_range, target) in node_replacements.into_iter().rev() {
+                assert!(
+                    !node_range.0.is_empty(),
+                    "Cannot replace an empty node range: {:?}",
+                    node_range.0
+                );
 
                 // Replace the tokens in range with zero or one `FlatToken::AttrsTarget`s, plus
                 // enough `FlatToken::Empty`s to fill up the rest of the range. This keeps the
                 // total length of `tokens` constant throughout the replacement process, allowing
-                // us to use all of the `ReplaceRanges` entries without adjusting indices.
+                // us to do all replacements without adjusting indices.
                 let target_len = target.is_some() as usize;
                 tokens.splice(
-                    (range.start as usize)..(range.end as usize),
-                    target
-                        .into_iter()
-                        .map(|target| FlatToken::AttrsTarget(target))
-                        .chain(iter::repeat(FlatToken::Empty).take(range.len() - target_len)),
+                    (node_range.0.start as usize)..(node_range.0.end as usize),
+                    target.into_iter().map(|target| FlatToken::AttrsTarget(target)).chain(
+                        iter::repeat(FlatToken::Empty).take(node_range.0.len() - target_len),
+                    ),
                 );
             }
             make_attr_token_stream(tokens.into_iter(), self.break_last_token)
@@ -215,7 +224,7 @@ impl<'a> Parser<'a> {
         let cursor_snapshot = self.token_cursor.clone();
         let start_pos = self.num_bump_calls;
         let has_outer_attrs = !attrs.attrs.is_empty();
-        let replace_ranges_start = self.capture_state.replace_ranges.len();
+        let parser_replacements_start = self.capture_state.parser_replacements.len();
 
         // We set and restore `Capturing::Yes` on either side of the call to
         // `f`, so we can distinguish the outermost call to
@@ -270,7 +279,7 @@ impl<'a> Parser<'a> {
             return Ok(ret);
         }
 
-        let replace_ranges_end = self.capture_state.replace_ranges.len();
+        let parser_replacements_end = self.capture_state.parser_replacements.len();
 
         assert!(
             !(self.break_last_token && capture_trailing),
@@ -287,15 +296,16 @@ impl<'a> Parser<'a> {
 
         let num_calls = end_pos - start_pos;
 
-        // Take the captured ranges for any inner attributes that we parsed in
-        // `Parser::parse_inner_attributes`, and pair them in a `ReplaceRange`
-        // with `None`, which means the relevant tokens will be removed. (More
-        // details below.)
-        let mut inner_attr_replace_ranges = Vec::new();
+        // Take the captured `ParserRange`s for any inner attributes that we parsed in
+        // `Parser::parse_inner_attributes`, and pair them in a `ParserReplacement` with `None`,
+        // which means the relevant tokens will be removed. (More details below.)
+        let mut inner_attr_parser_replacements = Vec::new();
         for attr in ret.attrs() {
             if attr.style == ast::AttrStyle::Inner {
-                if let Some(attr_range) = self.capture_state.inner_attr_ranges.remove(&attr.id) {
-                    inner_attr_replace_ranges.push((attr_range, None));
+                if let Some(inner_attr_parser_range) =
+                    self.capture_state.inner_attr_parser_ranges.remove(&attr.id)
+                {
+                    inner_attr_parser_replacements.push((inner_attr_parser_range, None));
                 } else {
                     self.dcx().span_delayed_bug(attr.span, "Missing token range for attribute");
                 }
@@ -304,37 +314,41 @@ impl<'a> Parser<'a> {
 
         // This is hot enough for `deep-vector` that checking the conditions for an empty iterator
         // is measurably faster than actually executing the iterator.
-        let replace_ranges: Box<[ReplaceRange]> =
-            if replace_ranges_start == replace_ranges_end && inner_attr_replace_ranges.is_empty() {
-                Box::new([])
-            } else {
-                // Grab any replace ranges that occur *inside* the current AST node. We will
-                // perform the actual replacement only when we convert the `LazyAttrTokenStream` to
-                // an `AttrTokenStream`.
-                self.capture_state.replace_ranges[replace_ranges_start..replace_ranges_end]
-                    .iter()
-                    .cloned()
-                    .chain(inner_attr_replace_ranges.iter().cloned())
-                    .map(|(range, data)| ((range.start - start_pos)..(range.end - start_pos), data))
-                    .collect()
-            };
+        let node_replacements: Box<[_]> = if parser_replacements_start == parser_replacements_end
+            && inner_attr_parser_replacements.is_empty()
+        {
+            Box::new([])
+        } else {
+            // Grab any replace ranges that occur *inside* the current AST node. Convert them
+            // from `ParserRange` form to `NodeRange` form. We will perform the actual
+            // replacement only when we convert the `LazyAttrTokenStream` to an
+            // `AttrTokenStream`.
+            self.capture_state.parser_replacements
+                [parser_replacements_start..parser_replacements_end]
+                .iter()
+                .cloned()
+                .chain(inner_attr_parser_replacements.iter().cloned())
+                .map(|(parser_range, data)| (NodeRange::new(parser_range, start_pos), data))
+                .collect()
+        };
 
         // What is the status here when parsing the example code at the top of this method?
         //
         // When parsing `g`:
         // - `start_pos..end_pos` is `12..33` (`fn g { ... }`, excluding the outer attr).
-        // - `inner_attr_replace_ranges` has one entry (`5..15`, when counting from `fn`), to
+        // - `inner_attr_parser_replacements` has one entry (`ParserRange(17..27)`), to
         //   delete the inner attr's tokens.
-        //   - This entry is put into the lazy tokens for `g`, i.e. deleting the inner attr from
-        //     those tokens (if they get evaluated).
+        //   - This entry is converted to `NodeRange(5..15)` (relative to the `fn`) and put into
+        //     the lazy tokens for `g`, i.e. deleting the inner attr from those tokens (if they get
+        //     evaluated).
         //   - Those lazy tokens are also put into an `AttrsTarget` that is appended to `self`'s
         //     replace ranges at the bottom of this function, for processing when parsing `m`.
-        // - `replace_ranges_start..replace_ranges_end` is empty.
+        // - `parser_replacements_start..parser_replacements_end` is empty.
         //
         // When parsing `m`:
         // - `start_pos..end_pos` is `0..34` (`mod m`, excluding the `#[cfg_eval]` attribute).
-        // - `inner_attr_replace_ranges` is empty.
-        // - `replace_range_start..replace_ranges_end` has one entry.
+        // - `inner_attr_parser_replacements` is empty.
+        // - `parser_replacements_start..parser_replacements_end` has one entry.
         //   - One `AttrsTarget` (added below when parsing `g`) to replace all of `g` (`3..33`,
         //     including its outer attribute), with:
         //     - `attrs`: includes the outer and the inner attr.
@@ -345,7 +359,7 @@ impl<'a> Parser<'a> {
             num_calls,
             cursor_snapshot,
             break_last_token: self.break_last_token,
-            replace_ranges,
+            node_replacements,
         });
 
         // If we support tokens and don't already have them, store the newly captured tokens.
@@ -366,7 +380,7 @@ impl<'a> Parser<'a> {
             // What is the status here when parsing the example code at the top of this method?
             //
             // When parsing `g`, we add one entry:
-            // - The `start_pos..end_pos` (`3..33`) entry has a new `AttrsTarget` with:
+            // - The pushed entry (`ParserRange(3..33)`) has a new `AttrsTarget` with:
             //   - `attrs`: includes the outer and the inner attr.
             //   - `tokens`: lazy tokens for `g` (with its inner attr deleted).
             //
@@ -377,12 +391,14 @@ impl<'a> Parser<'a> {
             // cfg-expand this AST node.
             let start_pos = if has_outer_attrs { attrs.start_pos } else { start_pos };
             let target = AttrsTarget { attrs: ret.attrs().iter().cloned().collect(), tokens };
-            self.capture_state.replace_ranges.push((start_pos..end_pos, Some(target)));
+            self.capture_state
+                .parser_replacements
+                .push((ParserRange(start_pos..end_pos), Some(target)));
         } else if matches!(self.capture_state.capturing, Capturing::No) {
             // Only clear the ranges once we've finished capturing entirely, i.e. we've finished
             // the outermost call to this method.
-            self.capture_state.replace_ranges.clear();
-            self.capture_state.inner_attr_ranges.clear();
+            self.capture_state.parser_replacements.clear();
+            self.capture_state.inner_attr_parser_ranges.clear();
         }
         Ok(ret)
     }
diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs
index 26ee5bfdee42c..722fb41cd81ec 100644
--- a/compiler/rustc_parse/src/parser/mod.rs
+++ b/compiler/rustc_parse/src/parser/mod.rs
@@ -192,24 +192,54 @@ struct ClosureSpans {
     body: Span,
 }
 
-/// Indicates a range of tokens that should be replaced by
-/// the tokens in the provided `AttrsTarget`. This is used in two
-/// places during token collection:
+/// A token range within a `Parser`'s full token stream.
+#[derive(Clone, Debug)]
+struct ParserRange(Range<u32>);
+
+/// A token range within an individual AST node's (lazy) token stream, i.e.
+/// relative to that node's first token. Distinct from `ParserRange` so the two
+/// kinds of range can't be mixed up.
+#[derive(Clone, Debug)]
+struct NodeRange(Range<u32>);
+
+/// Indicates a range of tokens that should be replaced by an `AttrsTarget`
+/// (replacement) or be replaced by nothing (deletion). This is used in two
+/// places during token collection.
+///
+/// 1. Replacement. During the parsing of an AST node that may have a
+///    `#[derive]` attribute, when we parse a nested AST node that has `#[cfg]`
+///    or `#[cfg_attr]`, we replace the entire inner AST node with
+///    `FlatToken::AttrsTarget`. This lets us perform eager cfg-expansion on an
+///    `AttrTokenStream`.
 ///
-/// 1. During the parsing of an AST node that may have a `#[derive]`
-/// attribute, we parse a nested AST node that has `#[cfg]` or `#[cfg_attr]`
-/// In this case, we use a `ReplaceRange` to replace the entire inner AST node
-/// with `FlatToken::AttrsTarget`, allowing us to perform eager cfg-expansion
-/// on an `AttrTokenStream`.
+/// 2. Deletion. We delete inner attributes from all collected token streams,
+///    and instead track them through the `attrs` field on the AST node. This
+///    lets us manipulate them similarly to outer attributes. When we create a
+///    `TokenStream`, the inner attributes are inserted into the proper place
+///    in the token stream.
 ///
-/// 2. When we parse an inner attribute while collecting tokens. We
-/// remove inner attributes from the token stream entirely, and
-/// instead track them through the `attrs` field on the AST node.
-/// This allows us to easily manipulate them (for example, removing
-/// the first macro inner attribute to invoke a proc-macro).
-/// When create a `TokenStream`, the inner attributes get inserted
-/// into the proper place in the token stream.
-type ReplaceRange = (Range<u32>, Option<AttrsTarget>);
+/// Each replacement starts off in `ParserReplacement` form but is converted to
+/// `NodeReplacement` form when it is attached to a single AST node, via
+/// `LazyAttrTokenStreamImpl`.
+type ParserReplacement = (ParserRange, Option<AttrsTarget>);
+
+/// See the comment on `ParserReplacement`.
+type NodeReplacement = (NodeRange, Option<AttrsTarget>);
+
+impl NodeRange {
+    // Converts a range within a parser's tokens to a range within a
+    // node's tokens beginning at `start_pos`.
+    //
+    // For example, imagine a parser with 50 tokens in its token stream, a
+    // function that spans `ParserRange(20..40)` and an inner attribute within
+    // that function that spans `ParserRange(30..35)`. We would find the inner
+    // attribute's range within the function's tokens by subtracting 20, which
+    // is the position of the function's start token. This gives
+    // `NodeRange(10..15)`.
+    fn new(ParserRange(parser_range): ParserRange, start_pos: u32) -> NodeRange {
+        NodeRange((parser_range.start - start_pos)..(parser_range.end - start_pos))
+    }
+}
 
 /// Controls how we capture tokens. Capturing can be expensive,
 /// so we try to avoid performing capturing in cases where
@@ -226,8 +256,8 @@ enum Capturing {
 #[derive(Clone, Debug)]
 struct CaptureState {
     capturing: Capturing,
-    replace_ranges: Vec<ReplaceRange>,
-    inner_attr_ranges: FxHashMap<AttrId, Range<u32>>,
+    parser_replacements: Vec<ParserReplacement>,
+    inner_attr_parser_ranges: FxHashMap<AttrId, ParserRange>,
 }
 
 /// Iterator over a `TokenStream` that produces `Token`s. It's a bit odd that
@@ -417,8 +447,8 @@ impl<'a> Parser<'a> {
             subparser_name,
             capture_state: CaptureState {
                 capturing: Capturing::No,
-                replace_ranges: Vec::new(),
-                inner_attr_ranges: Default::default(),
+                parser_replacements: Vec::new(),
+                inner_attr_parser_ranges: Default::default(),
             },
             current_closure: None,
             recovery: Recovery::Allowed,

From 6d312d7bd17598153c018e4879ccaa657eb48853 Mon Sep 17 00:00:00 2001
From: Ralf Jung <post@ralfj.de>
Date: Thu, 1 Aug 2024 14:01:17 +0200
Subject: [PATCH 14/16] MIR required_consts, mentioned_items: ensure we do not
 forget to fill these lists

---
 .../src/interpret/eval_context.rs             |  2 +-
 compiler/rustc_middle/src/mir/mod.rs          | 48 ++++++++++++++++---
 compiler/rustc_middle/src/mir/visit.rs        |  8 ++--
 .../rustc_mir_build/src/build/custom/mod.rs   |  4 +-
 compiler/rustc_mir_transform/src/inline.rs    | 13 +++--
 compiler/rustc_mir_transform/src/lib.rs       | 36 +++++++++-----
 .../src/mentioned_items.rs                    |  3 +-
 .../rustc_mir_transform/src/promote_consts.rs | 14 ++++--
 .../src/required_consts.rs                    | 13 ++++-
 compiler/rustc_mir_transform/src/shim.rs      | 12 +++--
 compiler/rustc_monomorphize/src/collector.rs  |  4 +-
 11 files changed, 113 insertions(+), 44 deletions(-)

diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs
index 85f9b2341d91c..9b4a437bf775c 100644
--- a/compiler/rustc_const_eval/src/interpret/eval_context.rs
+++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs
@@ -888,7 +888,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
         body: &'tcx mir::Body<'tcx>,
     ) -> InterpResult<'tcx> {
         // Make sure all the constants required by this frame evaluate successfully (post-monomorphization check).
-        for &const_ in &body.required_consts {
+        for &const_ in body.required_consts() {
             let c =
                 self.instantiate_from_current_frame_and_normalize_erasing_regions(const_.const_)?;
             c.eval(*self.tcx, self.param_env, const_.span).map_err(|err| {
diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs
index b9c93edcd80e4..46c4d586f6ad9 100644
--- a/compiler/rustc_middle/src/mir/mod.rs
+++ b/compiler/rustc_middle/src/mir/mod.rs
@@ -383,15 +383,17 @@ pub struct Body<'tcx> {
 
     /// Constants that are required to evaluate successfully for this MIR to be well-formed.
     /// We hold in this field all the constants we are not able to evaluate yet.
+    /// `None` indicates that the list has not been computed yet.
     ///
     /// This is soundness-critical, we make a guarantee that all consts syntactically mentioned in a
     /// function have successfully evaluated if the function ever gets executed at runtime.
-    pub required_consts: Vec<ConstOperand<'tcx>>,
+    pub required_consts: Option<Vec<ConstOperand<'tcx>>>,
 
     /// Further items that were mentioned in this function and hence *may* become monomorphized,
     /// depending on optimizations. We use this to avoid optimization-dependent compile errors: the
     /// collector recursively traverses all "mentioned" items and evaluates all their
     /// `required_consts`.
+    /// `None` indicates that the list has not been computed yet.
     ///
     /// This is *not* soundness-critical and the contents of this list are *not* a stable guarantee.
     /// All that's relevant is that this set is optimization-level-independent, and that it includes
@@ -399,7 +401,7 @@ pub struct Body<'tcx> {
     /// set after drop elaboration, so some drop calls that can never be reached are not considered
     /// "mentioned".) See the documentation of `CollectionMode` in
     /// `compiler/rustc_monomorphize/src/collector.rs` for more context.
-    pub mentioned_items: Vec<Spanned<MentionedItem<'tcx>>>,
+    pub mentioned_items: Option<Vec<Spanned<MentionedItem<'tcx>>>>,
 
     /// Does this body use generic parameters. This is used for the `ConstEvaluatable` check.
     ///
@@ -477,8 +479,8 @@ impl<'tcx> Body<'tcx> {
             spread_arg: None,
             var_debug_info,
             span,
-            required_consts: Vec::new(),
-            mentioned_items: Vec::new(),
+            required_consts: None,
+            mentioned_items: None,
             is_polymorphic: false,
             injection_phase: None,
             tainted_by_errors,
@@ -507,8 +509,8 @@ impl<'tcx> Body<'tcx> {
             arg_count: 0,
             spread_arg: None,
             span: DUMMY_SP,
-            required_consts: Vec::new(),
-            mentioned_items: Vec::new(),
+            required_consts: None,
+            mentioned_items: None,
             var_debug_info: Vec::new(),
             is_polymorphic: false,
             injection_phase: None,
@@ -785,6 +787,40 @@ impl<'tcx> Body<'tcx> {
         // No inlined `SourceScope`s, or all of them were `#[track_caller]`.
         caller_location.unwrap_or_else(|| from_span(source_info.span))
     }
+
+    #[track_caller]
+    pub fn set_required_consts(&mut self, required_consts: Vec<ConstOperand<'tcx>>) {
+        assert!(
+            self.required_consts.is_none(),
+            "required_consts for {:?} have already been set",
+            self.source.def_id()
+        );
+        self.required_consts = Some(required_consts);
+    }
+    #[track_caller]
+    pub fn required_consts(&self) -> &[ConstOperand<'tcx>] {
+        match &self.required_consts {
+            Some(l) => l,
+            None => panic!("required_consts for {:?} have not yet been set", self.source.def_id()),
+        }
+    }
+
+    #[track_caller]
+    pub fn set_mentioned_items(&mut self, mentioned_items: Vec<Spanned<MentionedItem<'tcx>>>) {
+        assert!(
+            self.mentioned_items.is_none(),
+            "mentioned_items for {:?} have already been set",
+            self.source.def_id()
+        );
+        self.mentioned_items = Some(mentioned_items);
+    }
+    #[track_caller]
+    pub fn mentioned_items(&self) -> &[Spanned<MentionedItem<'tcx>>] {
+        match &self.mentioned_items {
+            Some(l) => l,
+            None => panic!("mentioned_items for {:?} have not yet been set", self.source.def_id()),
+        }
+    }
 }
 
 impl<'tcx> Index<BasicBlock> for Body<'tcx> {
diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs
index 0031ded244062..3921176873c81 100644
--- a/compiler/rustc_middle/src/mir/visit.rs
+++ b/compiler/rustc_middle/src/mir/visit.rs
@@ -1066,9 +1066,11 @@ macro_rules! super_body {
 
         $self.visit_span($(& $mutability)? $body.span);
 
-        for const_ in &$($mutability)? $body.required_consts {
-            let location = Location::START;
-            $self.visit_const_operand(const_, location);
+        if let Some(required_consts) = &$($mutability)? $body.required_consts {
+            for const_ in required_consts {
+                let location = Location::START;
+                $self.visit_const_operand(const_, location);
+            }
         }
     }
 }
diff --git a/compiler/rustc_mir_build/src/build/custom/mod.rs b/compiler/rustc_mir_build/src/build/custom/mod.rs
index 38acb6f44f95c..28477e527c721 100644
--- a/compiler/rustc_mir_build/src/build/custom/mod.rs
+++ b/compiler/rustc_mir_build/src/build/custom/mod.rs
@@ -54,8 +54,8 @@ pub(super) fn build_custom_mir<'tcx>(
         spread_arg: None,
         var_debug_info: Vec::new(),
         span,
-        required_consts: Vec::new(),
-        mentioned_items: Vec::new(),
+        required_consts: None,
+        mentioned_items: None,
         is_polymorphic: false,
         tainted_by_errors: None,
         injection_phase: None,
diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs
index 36b2b3b7c4466..f30732e6aaf3b 100644
--- a/compiler/rustc_mir_transform/src/inline.rs
+++ b/compiler/rustc_mir_transform/src/inline.rs
@@ -744,8 +744,8 @@ impl<'tcx> Inliner<'tcx> {
         // Copy required constants from the callee_body into the caller_body. Although we are only
         // pushing unevaluated consts to `required_consts`, here they may have been evaluated
         // because we are calling `instantiate_and_normalize_erasing_regions` -- so we filter again.
-        caller_body.required_consts.extend(
-            callee_body.required_consts.into_iter().filter(|ct| ct.const_.is_required_const()),
+        caller_body.required_consts.as_mut().unwrap().extend(
+            callee_body.required_consts().into_iter().filter(|ct| ct.const_.is_required_const()),
         );
         // Now that we incorporated the callee's `required_consts`, we can remove the callee from
         // `mentioned_items` -- but we have to take their `mentioned_items` in return. This does
@@ -755,12 +755,11 @@ impl<'tcx> Inliner<'tcx> {
         // We need to reconstruct the `required_item` for the callee so that we can find and
         // remove it.
         let callee_item = MentionedItem::Fn(func.ty(caller_body, self.tcx));
-        if let Some(idx) =
-            caller_body.mentioned_items.iter().position(|item| item.node == callee_item)
-        {
+        let caller_mentioned_items = caller_body.mentioned_items.as_mut().unwrap();
+        if let Some(idx) = caller_mentioned_items.iter().position(|item| item.node == callee_item) {
             // We found the callee, so remove it and add its items instead.
-            caller_body.mentioned_items.remove(idx);
-            caller_body.mentioned_items.extend(callee_body.mentioned_items);
+            caller_mentioned_items.remove(idx);
+            caller_mentioned_items.extend(callee_body.mentioned_items());
         } else {
             // If we can't find the callee, there's no point in adding its items. Probably it
             // already got removed by being inlined elsewhere in the same function, so we already
diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs
index ac3a44c803a5f..1f214bc42cbe6 100644
--- a/compiler/rustc_mir_transform/src/lib.rs
+++ b/compiler/rustc_mir_transform/src/lib.rs
@@ -28,11 +28,10 @@ use rustc_hir::def::DefKind;
 use rustc_hir::def_id::LocalDefId;
 use rustc_hir::intravisit::{self, Visitor};
 use rustc_index::IndexVec;
-use rustc_middle::mir::visit::Visitor as _;
 use rustc_middle::mir::{
-    traversal, AnalysisPhase, Body, CallSource, ClearCrossCrate, ConstOperand, ConstQualifs,
-    LocalDecl, MirPass, MirPhase, Operand, Place, ProjectionElem, Promoted, RuntimePhase, Rvalue,
-    SourceInfo, Statement, StatementKind, TerminatorKind, START_BLOCK,
+    AnalysisPhase, Body, CallSource, ClearCrossCrate, ConstOperand, ConstQualifs, LocalDecl,
+    MirPass, MirPhase, Operand, Place, ProjectionElem, Promoted, RuntimePhase, Rvalue, SourceInfo,
+    Statement, StatementKind, TerminatorKind, START_BLOCK,
 };
 use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt};
 use rustc_middle::util::Providers;
@@ -339,12 +338,15 @@ fn mir_promoted(
 
     // Collect `required_consts` *before* promotion, so if there are any consts being promoted
     // we still add them to the list in the outer MIR body.
-    let mut required_consts = Vec::new();
-    let mut required_consts_visitor = RequiredConstsVisitor::new(&mut required_consts);
-    for (bb, bb_data) in traversal::reverse_postorder(&body) {
-        required_consts_visitor.visit_basic_block_data(bb, bb_data);
+    RequiredConstsVisitor::compute_required_consts(&mut body);
+    // If this has an associated by-move async closure body, that doesn't get run through these
+    // passes itself, it gets "tagged along" by the pass manager. `RequiredConstsVisitor` is not
+    // a regular pass so we have to also apply it manually to the other body.
+    if let Some(coroutine) = body.coroutine.as_mut() {
+        if let Some(by_move_body) = coroutine.by_move_body.as_mut() {
+            RequiredConstsVisitor::compute_required_consts(by_move_body);
+        }
     }
-    body.required_consts = required_consts;
 
     // What we need to run borrowck etc.
     let promote_pass = promote_consts::PromoteTemps::default();
@@ -561,9 +563,6 @@ fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
         tcx,
         body,
         &[
-            // Before doing anything, remember which items are being mentioned so that the set of items
-            // visited does not depend on the optimization level.
-            &mentioned_items::MentionedItems,
             // Add some UB checks before any UB gets optimized away.
             &check_alignment::CheckAlignment,
             // Before inlining: trim down MIR with passes to reduce inlining work.
@@ -657,6 +656,19 @@ fn inner_optimized_mir(tcx: TyCtxt<'_>, did: LocalDefId) -> Body<'_> {
         return body;
     }
 
+    // Before doing anything, remember which items are being mentioned so that the set of items
+    // visited does not depend on the optimization level.
+    // We do not use `run_passes` for this as that might skip the pass if `injection_phase` is set.
+    mentioned_items::MentionedItems.run_pass(tcx, &mut body);
+    // If this has an associated by-move async closure body, that doesn't get run through these
+    // passes itself, it gets "tagged along" by the pass manager. Since we're not using the pass
+    // manager we have to do this by hand.
+    if let Some(coroutine) = body.coroutine.as_mut() {
+        if let Some(by_move_body) = coroutine.by_move_body.as_mut() {
+            mentioned_items::MentionedItems.run_pass(tcx, by_move_body);
+        }
+    }
+
     // If `mir_drops_elaborated_and_const_checked` found that the current body has unsatisfiable
     // predicates, it will shrink the MIR to a single `unreachable` terminator.
     // More generally, if MIR is a lone `unreachable`, there is nothing to optimize.
diff --git a/compiler/rustc_mir_transform/src/mentioned_items.rs b/compiler/rustc_mir_transform/src/mentioned_items.rs
index e33bdd9942192..32c8064ebca50 100644
--- a/compiler/rustc_mir_transform/src/mentioned_items.rs
+++ b/compiler/rustc_mir_transform/src/mentioned_items.rs
@@ -23,10 +23,9 @@ impl<'tcx> MirPass<'tcx> for MentionedItems {
     }
 
     fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut mir::Body<'tcx>) {
-        debug_assert!(body.mentioned_items.is_empty());
         let mut mentioned_items = Vec::new();
         MentionedItemsVisitor { tcx, body, mentioned_items: &mut mentioned_items }.visit_body(body);
-        body.mentioned_items = mentioned_items;
+        body.set_mentioned_items(mentioned_items);
     }
 }
 
diff --git a/compiler/rustc_mir_transform/src/promote_consts.rs b/compiler/rustc_mir_transform/src/promote_consts.rs
index f8971387ea4d7..dc8e50ac8cd28 100644
--- a/compiler/rustc_mir_transform/src/promote_consts.rs
+++ b/compiler/rustc_mir_transform/src/promote_consts.rs
@@ -702,6 +702,9 @@ struct Promoter<'a, 'tcx> {
     temps: &'a mut IndexVec<Local, TempState>,
     extra_statements: &'a mut Vec<(Location, Statement<'tcx>)>,
 
+    /// Used to assemble the required_consts list while building the promoted.
+    required_consts: Vec<ConstOperand<'tcx>>,
+
     /// If true, all nested temps are also kept in the
     /// source MIR, not moved to the promoted MIR.
     keep_original: bool,
@@ -924,11 +927,14 @@ impl<'a, 'tcx> Promoter<'a, 'tcx> {
         let span = self.promoted.span;
         self.assign(RETURN_PLACE, rvalue, span);
 
-        // Now that we did promotion, we know whether we'll want to add this to `required_consts`.
+        // Now that we did promotion, we know whether we'll want to add this to `required_consts` of
+        // the surrounding MIR body.
         if self.add_to_required {
-            self.source.required_consts.push(promoted_op);
+            self.source.required_consts.as_mut().unwrap().push(promoted_op);
         }
 
+        self.promoted.set_required_consts(self.required_consts);
+
         self.promoted
     }
 }
@@ -947,7 +953,7 @@ impl<'a, 'tcx> MutVisitor<'tcx> for Promoter<'a, 'tcx> {
 
     fn visit_const_operand(&mut self, constant: &mut ConstOperand<'tcx>, _location: Location) {
         if constant.const_.is_required_const() {
-            self.promoted.required_consts.push(*constant);
+            self.required_consts.push(*constant);
         }
 
         // Skipping `super_constant` as the visitor is otherwise only looking for locals.
@@ -1011,9 +1017,9 @@ fn promote_candidates<'tcx>(
             extra_statements: &mut extra_statements,
             keep_original: false,
             add_to_required: false,
+            required_consts: Vec::new(),
         };
 
-        // `required_consts` of the promoted itself gets filled while building the MIR body.
         let mut promoted = promoter.promote_candidate(candidate, promotions.len());
         promoted.source.promoted = Some(promotions.next_index());
         promotions.push(promoted);
diff --git a/compiler/rustc_mir_transform/src/required_consts.rs b/compiler/rustc_mir_transform/src/required_consts.rs
index 00bfb5e66008b..50637e2ac03bb 100644
--- a/compiler/rustc_mir_transform/src/required_consts.rs
+++ b/compiler/rustc_mir_transform/src/required_consts.rs
@@ -1,14 +1,23 @@
 use rustc_middle::mir::visit::Visitor;
-use rustc_middle::mir::{ConstOperand, Location};
+use rustc_middle::mir::{traversal, Body, ConstOperand, Location};
 
 pub struct RequiredConstsVisitor<'a, 'tcx> {
     required_consts: &'a mut Vec<ConstOperand<'tcx>>,
 }
 
 impl<'a, 'tcx> RequiredConstsVisitor<'a, 'tcx> {
-    pub fn new(required_consts: &'a mut Vec<ConstOperand<'tcx>>) -> Self {
+    fn new(required_consts: &'a mut Vec<ConstOperand<'tcx>>) -> Self {
         RequiredConstsVisitor { required_consts }
     }
+
+    pub fn compute_required_consts(body: &mut Body<'tcx>) {
+        let mut required_consts = Vec::new();
+        let mut required_consts_visitor = RequiredConstsVisitor::new(&mut required_consts);
+        for (bb, bb_data) in traversal::reverse_postorder(&body) {
+            required_consts_visitor.visit_basic_block_data(bb, bb_data);
+        }
+        body.set_required_consts(required_consts);
+    }
 }
 
 impl<'tcx> Visitor<'tcx> for RequiredConstsVisitor<'_, 'tcx> {
diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs
index e2fafa3a1a30b..f41f3ef656c51 100644
--- a/compiler/rustc_mir_transform/src/shim.rs
+++ b/compiler/rustc_mir_transform/src/shim.rs
@@ -305,7 +305,7 @@ fn new_body<'tcx>(
     arg_count: usize,
     span: Span,
 ) -> Body<'tcx> {
-    Body::new(
+    let mut body = Body::new(
         source,
         basic_blocks,
         IndexVec::from_elem_n(
@@ -326,7 +326,10 @@ fn new_body<'tcx>(
         None,
         // FIXME(compiler-errors): is this correct?
         None,
-    )
+    );
+    // Shims do not directly mention any consts.
+    body.set_required_consts(Vec::new());
+    body
 }
 
 pub struct DropShimElaborator<'a, 'tcx> {
@@ -969,13 +972,16 @@ pub fn build_adt_ctor(tcx: TyCtxt<'_>, ctor_id: DefId) -> Body<'_> {
     };
 
     let source = MirSource::item(ctor_id);
-    let body = new_body(
+    let mut body = new_body(
         source,
         IndexVec::from_elem_n(start_block, 1),
         local_decls,
         sig.inputs().len(),
         span,
     );
+    // A constructor doesn't mention any other items (and we don't run the usual optimization passes
+    // so this would otherwise not get filled).
+    body.set_mentioned_items(Vec::new());
 
     crate::pass_manager::dump_mir_for_phase_change(tcx, &body);
 
diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs
index 99cac67f5b1c1..df2abf6dc9cc4 100644
--- a/compiler/rustc_monomorphize/src/collector.rs
+++ b/compiler/rustc_monomorphize/src/collector.rs
@@ -1229,7 +1229,7 @@ fn collect_items_of_instance<'tcx>(
 
     // Always visit all `required_consts`, so that we evaluate them and abort compilation if any of
     // them errors.
-    for const_op in &body.required_consts {
+    for const_op in body.required_consts() {
         if let Some(val) = collector.eval_constant(const_op) {
             collect_const_value(tcx, val, mentioned_items);
         }
@@ -1237,7 +1237,7 @@ fn collect_items_of_instance<'tcx>(
 
     // Always gather mentioned items. We try to avoid processing items that we have already added to
     // `used_items` above.
-    for item in &body.mentioned_items {
+    for item in body.mentioned_items() {
         if !collector.used_mentioned_items.contains(&item.node) {
             let item_mono = collector.monomorphize(item.node);
             visit_mentioned_item(tcx, &item_mono, item.span, mentioned_items);

From f373ff3c5633cb82929905ef2f46da4df42164eb Mon Sep 17 00:00:00 2001
From: sayantn <sayantan.chakraborty@students.iiserpune.ac.in>
Date: Thu, 1 Aug 2024 21:55:18 +0530
Subject: [PATCH 15/16] Update stdarch

---
 library/stdarch | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/library/stdarch b/library/stdarch
index df3618d9f3516..fb90dfa348265 160000
--- a/library/stdarch
+++ b/library/stdarch
@@ -1 +1 @@
-Subproject commit df3618d9f35165f4bc548114e511c49c29e1fd9b
+Subproject commit fb90dfa348265f5039dc7dbba24ee9adf6046507

From e3b98510a75c1985f437262d6bf13042ab8f83de Mon Sep 17 00:00:00 2001
From: sayantn <sayantan.chakraborty@students.iiserpune.ac.in>
Date: Thu, 1 Aug 2024 21:56:27 +0530
Subject: [PATCH 16/16] Delete issue-120720-reduce-nan.rs

This file tests for UB in `reduce_add`, but those are reimplemented by explicitly using the associativity specified by Intel
---
 tests/codegen/simd/issue-120720-reduce-nan.rs | 21 -------------------
 1 file changed, 21 deletions(-)
 delete mode 100644 tests/codegen/simd/issue-120720-reduce-nan.rs

diff --git a/tests/codegen/simd/issue-120720-reduce-nan.rs b/tests/codegen/simd/issue-120720-reduce-nan.rs
deleted file mode 100644
index 13af0bb076e6a..0000000000000
--- a/tests/codegen/simd/issue-120720-reduce-nan.rs
+++ /dev/null
@@ -1,21 +0,0 @@
-//@ compile-flags: -C opt-level=3 -C target-cpu=cannonlake
-//@ only-x86_64
-
-// In a previous implementation, _mm512_reduce_add_pd did the reduction with all fast-math flags
-// enabled, making it UB to reduce a vector containing a NaN.
-
-#![crate_type = "lib"]
-#![feature(stdarch_x86_avx512, avx512_target_feature)]
-use std::arch::x86_64::*;
-
-// CHECK-LABEL: @demo(
-#[no_mangle]
-#[target_feature(enable = "avx512f")] // Function-level target feature mismatches inhibit inlining
-pub unsafe fn demo() -> bool {
-    // CHECK: %0 = tail call reassoc double @llvm.vector.reduce.fadd.v8f64(
-    // CHECK: %_0.i = fcmp uno double %0, 0.000000e+00
-    // CHECK: ret i1 %_0.i
-    let res =
-        unsafe { _mm512_reduce_add_pd(_mm512_set_pd(0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, f64::NAN)) };
-    res.is_nan()
-}