From c8b0e5b1a4698576b3d3637783cc3aeb09c74b1f Mon Sep 17 00:00:00 2001 From: Tobias Bucher Date: Mon, 20 May 2024 20:37:28 +0200 Subject: [PATCH 1/7] The number of tests does not depend on the architecture's pointer width Use `u32` instead of `usize` for counting them. --- src/tools/tidy/src/ui_tests.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tools/tidy/src/ui_tests.rs b/src/tools/tidy/src/ui_tests.rs index 37324639edfca..055d620361fb8 100644 --- a/src/tools/tidy/src/ui_tests.rs +++ b/src/tools/tidy/src/ui_tests.rs @@ -12,10 +12,10 @@ use std::path::{Path, PathBuf}; // should all be 1000 or lower. Limits significantly smaller than 1000 are also // desirable, because large numbers of files are unwieldy in general. See issue // #73494. -const ENTRY_LIMIT: usize = 900; +const ENTRY_LIMIT: u32 = 900; // FIXME: The following limits should be reduced eventually. -const ISSUES_ENTRY_LIMIT: usize = 1676; +const ISSUES_ENTRY_LIMIT: u32 = 1676; const EXPECTED_TEST_FILE_EXTENSIONS: &[&str] = &[ "rs", // test source files @@ -53,7 +53,7 @@ const EXTENSION_EXCEPTION_PATHS: &[&str] = &[ ]; fn check_entries(tests_path: &Path, bad: &mut bool) { - let mut directories: HashMap = HashMap::new(); + let mut directories: HashMap = HashMap::new(); for dir in Walk::new(&tests_path.join("ui")) { if let Ok(entry) = dir { @@ -62,7 +62,7 @@ fn check_entries(tests_path: &Path, bad: &mut bool) { } } - let (mut max, mut max_issues) = (0usize, 0usize); + let (mut max, mut max_issues) = (0, 0); for (dir_path, count) in directories { let is_issues_dir = tests_path.join("ui/issues") == dir_path; let (limit, maxcnt) = if is_issues_dir { From 90fec5a0873c82674890a9f67b73a4f0a35e9d0b Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 25 May 2024 13:03:23 +0200 Subject: [PATCH 2/7] Add `copy_dir_all` and `recursive_diff` functions to `run-make-support` --- src/tools/run-make-support/src/lib.rs | 67 +++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index 9854d91e19e33..d96c8b891278b 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -12,6 +12,8 @@ pub mod rustc; pub mod rustdoc; use std::env; +use std::fs; +use std::io; use std::path::{Path, PathBuf}; use std::process::{Command, Output}; @@ -201,6 +203,71 @@ pub fn set_host_rpath(cmd: &mut Command) { }); } +/// Copy a directory into another. +pub fn copy_dir_all(src: impl AsRef, dst: impl AsRef) { + fn copy_dir_all_inner(src: impl AsRef, dst: impl AsRef) -> io::Result<()> { + let dst = dst.as_ref(); + if !dst.is_dir() { + fs::create_dir_all(&dst)?; + } + for entry in fs::read_dir(src)? { + let entry = entry?; + let ty = entry.file_type()?; + if ty.is_dir() { + copy_dir_all_inner(entry.path(), dst.join(entry.file_name()))?; + } else { + fs::copy(entry.path(), dst.join(entry.file_name()))?; + } + } + Ok(()) + } + + if let Err(e) = copy_dir_all_inner(&src, &dst) { + // Trying to give more context about what exactly caused the failure + panic!( + "failed to copy `{}` to `{}`: {:?}", + src.as_ref().display(), + dst.as_ref().display(), + e + ); + } +} + +/// Check that all files in `dir1` exist and have the same content in `dir2`. Panic otherwise. +pub fn recursive_diff(dir1: impl AsRef, dir2: impl AsRef) { + fn read_file(path: &Path) -> Vec { + match fs::read(path) { + Ok(c) => c, + Err(e) => panic!("Failed to read `{}`: {:?}", path.display(), e), + } + } + + let dir2 = dir2.as_ref(); + for entry in fs::read_dir(dir1).unwrap() { + let entry = entry.unwrap(); + let entry_name = entry.file_name(); + let path = entry.path(); + + if path.is_dir() { + recursive_diff(&path, &dir2.join(entry_name)); + } else { + let path2 = dir2.join(entry_name); + let file1 = read_file(&path); + let file2 = read_file(&path2); + + // We don't use `assert_eq!` because they are `Vec`, so not great for display. + // Why not using String? Because there might be minified files or even potentially + // binary ones, so that would display useless output. + assert!( + file1 == file2, + "`{}` and `{}` have different content", + path.display(), + path2.display(), + ); + } + } +} + /// Implement common helpers for command wrappers. This assumes that the command wrapper is a struct /// containing a `cmd: Command` field and a `output` function. The provided helpers are: /// From 1551fd12023e3b84d844bde2e376c407af1e9a04 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 25 May 2024 13:03:53 +0200 Subject: [PATCH 3/7] Add file path in case it cannot be read in `Diff::actual_file` --- src/tools/run-make-support/src/diff/mod.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/tools/run-make-support/src/diff/mod.rs b/src/tools/run-make-support/src/diff/mod.rs index 332126939c084..d864ddf4eb175 100644 --- a/src/tools/run-make-support/src/diff/mod.rs +++ b/src/tools/run-make-support/src/diff/mod.rs @@ -51,7 +51,10 @@ impl Diff { /// Specify the actual output for the diff from a file. pub fn actual_file>(&mut self, path: P) -> &mut Self { let path = path.as_ref(); - let content = std::fs::read_to_string(path).expect("failed to read file"); + let content = match std::fs::read_to_string(path) { + Ok(c) => c, + Err(e) => panic!("failed to read `{}`: {:?}", path.display(), e), + }; let name = path.to_string_lossy().to_string(); self.actual = Some(content); From f0ab814aec56418ec0792d41db27e7d94c7d4380 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 25 May 2024 13:04:08 +0200 Subject: [PATCH 4/7] Add `Rustdoc::output_format` --- src/tools/run-make-support/src/rustdoc.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/tools/run-make-support/src/rustdoc.rs b/src/tools/run-make-support/src/rustdoc.rs index c4f4e9f9bd23b..9c77f1ca4624e 100644 --- a/src/tools/run-make-support/src/rustdoc.rs +++ b/src/tools/run-make-support/src/rustdoc.rs @@ -151,6 +151,13 @@ impl Rustdoc { self } + /// Specify the output format. + pub fn output_format(&mut self, format: &str) -> &mut Self { + self.cmd.arg("--output-format"); + self.cmd.arg(format); + self + } + #[track_caller] pub fn run_fail_assert_exit_code(&mut self, code: i32) -> Output { let caller_location = std::panic::Location::caller(); From bdf3864d51f7141d9e0fd14c754c7c3b5650af60 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 25 May 2024 13:04:41 +0200 Subject: [PATCH 5/7] Migrate `run-make/rustdoc-verify-output-files` to `rmake.rs` --- .../tidy/src/allowed_run_make_makefiles.txt | 1 - .../rustdoc-verify-output-files/Makefile | 32 ------------ .../rustdoc-verify-output-files/rmake.rs | 49 +++++++++++++++++++ 3 files changed, 49 insertions(+), 33 deletions(-) delete mode 100644 tests/run-make/rustdoc-verify-output-files/Makefile create mode 100644 tests/run-make/rustdoc-verify-output-files/rmake.rs diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt index 9a6ae18abeade..d3f712258c48e 100644 --- a/src/tools/tidy/src/allowed_run_make_makefiles.txt +++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt @@ -227,7 +227,6 @@ run-make/rlib-format-packed-bundled-libs/Makefile run-make/rmeta-preferred/Makefile run-make/rustc-macro-dep-files/Makefile run-make/rustdoc-io-error/Makefile -run-make/rustdoc-verify-output-files/Makefile run-make/sanitizer-cdylib-link/Makefile run-make/sanitizer-dylib-link/Makefile run-make/sanitizer-staticlib-link/Makefile diff --git a/tests/run-make/rustdoc-verify-output-files/Makefile b/tests/run-make/rustdoc-verify-output-files/Makefile deleted file mode 100644 index 76f233ab445d0..0000000000000 --- a/tests/run-make/rustdoc-verify-output-files/Makefile +++ /dev/null @@ -1,32 +0,0 @@ -include ../tools.mk - -OUTPUT_DIR := "$(TMPDIR)/rustdoc" -TMP_OUTPUT_DIR := "$(TMPDIR)/tmp-rustdoc" - -all: - # Generate html docs - $(RUSTDOC) src/lib.rs --crate-name foobar --crate-type lib --out-dir $(OUTPUT_DIR) - - # Copy first output for to check if it's exactly same after second compilation - cp -R $(OUTPUT_DIR) $(TMP_OUTPUT_DIR) - - # Generate html docs once again on same output - $(RUSTDOC) src/lib.rs --crate-name foobar --crate-type lib --out-dir $(OUTPUT_DIR) - - # Check if everything exactly same - $(DIFF) -r $(OUTPUT_DIR) $(TMP_OUTPUT_DIR) - - # Generate json doc on the same output - $(RUSTDOC) src/lib.rs --crate-name foobar --crate-type lib --out-dir $(OUTPUT_DIR) -Z unstable-options --output-format json - - # Check if expected json file is generated - [ -e $(OUTPUT_DIR)/foobar.json ] - - # Copy first json output to check if it's exactly same after second compilation - cp -R $(OUTPUT_DIR)/foobar.json $(TMP_OUTPUT_DIR)/foobar.json - - # Generate json doc on the same output - $(RUSTDOC) src/lib.rs --crate-name foobar --crate-type lib --out-dir $(OUTPUT_DIR) -Z unstable-options --output-format json - - # Check if all docs(including both json and html formats) are still the same after multiple compilations - $(DIFF) -r $(OUTPUT_DIR) $(TMP_OUTPUT_DIR) diff --git a/tests/run-make/rustdoc-verify-output-files/rmake.rs b/tests/run-make/rustdoc-verify-output-files/rmake.rs new file mode 100644 index 0000000000000..212e7eaba2d68 --- /dev/null +++ b/tests/run-make/rustdoc-verify-output-files/rmake.rs @@ -0,0 +1,49 @@ +use std::fs::copy; +use std::path::{Path, PathBuf}; + +use run_make_support::{copy_dir_all, recursive_diff, rustdoc, tmp_dir}; + +#[derive(PartialEq)] +enum JsonOutput { + Yes, + No, +} + +fn generate_docs(out_dir: &Path, json_output: JsonOutput) { + let mut rustdoc = rustdoc(); + rustdoc.input("src/lib.rs").crate_name("foobar").crate_type("lib").out_dir(&out_dir); + if json_output == JsonOutput::Yes { + rustdoc.arg("-Zunstable-options").output_format("json"); + } + rustdoc.run(); +} + +fn main() { + let out_dir = tmp_dir().join("rustdoc"); + let tmp_out_dir = tmp_dir().join("tmp-rustdoc"); + + // Generate HTML docs. + generate_docs(&out_dir, JsonOutput::No); + + // Copy first output for to check if it's exactly same after second compilation. + copy_dir_all(&out_dir, &tmp_out_dir); + + // Generate html docs once again on same output. + generate_docs(&out_dir, JsonOutput::No); + + // Generate json doc on the same output. + generate_docs(&out_dir, JsonOutput::Yes); + + // Check if expected json file is generated. + assert!(out_dir.join("foobar.json").is_file()); + + // Copy first json output to check if it's exactly same after second compilation. + copy(out_dir.join("foobar.json"), tmp_out_dir.join("foobar.json")).unwrap(); + + // Generate json doc on the same output. + generate_docs(&out_dir, JsonOutput::Yes); + + // Check if all docs(including both json and html formats) are still the same after multiple + // compilations. + recursive_diff(&out_dir, &tmp_out_dir); +} From 7d24f8706823e2f56f42ab7597f50d1b679f0eea Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 27 May 2024 15:28:51 +0200 Subject: [PATCH 6/7] MIR validation: ensure that downcast projection is followed by field projection --- compiler/rustc_middle/src/mir/syntax.rs | 4 +-- compiler/rustc_mir_transform/src/validate.rs | 26 ++++++++++++++++--- .../miri/tests/panic/mir-validation.stderr | 2 +- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 2b28496faec73..2d4852114332b 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -1008,8 +1008,8 @@ pub type AssertMessage<'tcx> = AssertKind>; /// element: /// /// - [`Downcast`](ProjectionElem::Downcast): This projection sets the place's variant index to the -/// given one, and makes no other changes. A `Downcast` projection on a place with its variant -/// index already set is not well-formed. +/// given one, and makes no other changes. A `Downcast` projection must always be followed +/// immediately by a `Field` projection. /// - [`Field`](ProjectionElem::Field): `Field` projections take their parent place and create a /// place referring to one of the fields of the type. The resulting address is the parent /// address, plus the offset of the field. The type becomes the type of the field. If the parent diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 66cc65de64709..6df32169eecb0 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -689,8 +689,10 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { if Some(adt_def.did()) == self.tcx.lang_items().dyn_metadata() { self.fail( location, - format!("You can't project to field {f:?} of `DynMetadata` because \ - layout is weird and thinks it doesn't have fields."), + format!( + "You can't project to field {f:?} of `DynMetadata` because \ + layout is weird and thinks it doesn't have fields." + ), ); } @@ -839,7 +841,25 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { && cntxt != PlaceContext::NonUse(NonUseContext::VarDebugInfo) && place.projection[1..].contains(&ProjectionElem::Deref) { - self.fail(location, format!("{place:?}, has deref at the wrong place")); + self.fail( + location, + format!("place {place:?} has deref as a later projection (it is only permitted as the first projection)"), + ); + } + + // Ensure all downcast projections are followed by field projections. + let mut projections_iter = place.projection.iter(); + while let Some(proj) = projections_iter.next() { + if matches!(proj, ProjectionElem::Downcast(..)) { + if !matches!(projections_iter.next(), Some(ProjectionElem::Field(..))) { + self.fail( + location, + format!( + "place {place:?} has `Downcast` projection not followed by `Field`" + ), + ); + } + } } self.super_place(place, cntxt, location); diff --git a/src/tools/miri/tests/panic/mir-validation.stderr b/src/tools/miri/tests/panic/mir-validation.stderr index d5dd53d7b4e99..534e2d5881ffc 100644 --- a/src/tools/miri/tests/panic/mir-validation.stderr +++ b/src/tools/miri/tests/panic/mir-validation.stderr @@ -1,6 +1,6 @@ thread 'rustc' panicked at compiler/rustc_mir_transform/src/validate.rs:LL:CC: broken MIR in Item(DefId) (after phase change to runtime-optimized) at bb0[1]: -(*(_2.0: *mut i32)), has deref at the wrong place +place (*(_2.0: *mut i32)) has deref as a later projection (it is only permitted as the first projection) stack backtrace: error: the compiler unexpectedly panicked. this is a bug. From 7a847fc4fb6266332754af4d07acd037b51dee01 Mon Sep 17 00:00:00 2001 From: Chris Denton Date: Mon, 27 May 2024 16:35:22 +0000 Subject: [PATCH 7/7] Use grep to implement verify-line-endings --- src/ci/scripts/verify-line-endings.sh | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/ci/scripts/verify-line-endings.sh b/src/ci/scripts/verify-line-endings.sh index f3cac13ea4802..5f4b4aeb0e4eb 100755 --- a/src/ci/scripts/verify-line-endings.sh +++ b/src/ci/scripts/verify-line-endings.sh @@ -4,21 +4,21 @@ # We check both in rust-lang/rust and in a submodule to make sure both are # accurate. Submodules are checked out significantly later than the main # repository in this script, so settings can (and do!) change between then. -# -# Linux (and maybe macOS) builders don't currently have dos2unix so just only -# run this step on Windows. set -euo pipefail IFS=$'\n\t' source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" -if isWindows; then - # print out the git configuration so we can better investigate failures in - # the following - git config --list --show-origin - dos2unix -ih Cargo.lock src/tools/rust-installer/install-template.sh - endings=$(dos2unix -ic Cargo.lock src/tools/rust-installer/install-template.sh) - # if endings has non-zero length, error out - if [ -n "$endings" ]; then exit 1 ; fi +# print out the git configuration so we can better investigate failures in +# the following +git config --list --show-origin +# -U is necessary on Windows to stop grep automatically converting the line ending +endings=$(grep -Ul $(printf '\r$') Cargo.lock src/tools/cargo/Cargo.lock) || true +# if endings has non-zero length, error out +if [[ -n $endings ]]; then + echo "Error: found DOS line endings" + # Print the files with DOS line endings + echo "$endings" + exit 1 fi