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() -}