diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-07 05:48:48 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-07 05:48:48 +0000 |
commit | ef24de24a82fe681581cc130f342363c47c0969a (patch) | |
tree | 0d494f7e1a38b95c92426f58fe6eaa877303a86c /vendor/ui_test-0.20.0 | |
parent | Releasing progress-linux version 1.74.1+dfsg1-1~progress7.99u1. (diff) | |
download | rustc-ef24de24a82fe681581cc130f342363c47c0969a.tar.xz rustc-ef24de24a82fe681581cc130f342363c47c0969a.zip |
Merging upstream version 1.75.0+dfsg1.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/ui_test-0.20.0')
20 files changed, 0 insertions, 5538 deletions
diff --git a/vendor/ui_test-0.20.0/.cargo-checksum.json b/vendor/ui_test-0.20.0/.cargo-checksum.json deleted file mode 100644 index 27886f95b..000000000 --- a/vendor/ui_test-0.20.0/.cargo-checksum.json +++ /dev/null @@ -1 +0,0 @@ -{"files":{"CONTRIBUTING.md":"e030432e8f8830a0c6e6fb783dcae14c19d20f770d4e2e274a48693748d7bd68","Cargo.toml":"cbd19cfe012839028720b9337016bc3733e5ad39abe8ef2410acbd23e7352b59","README.md":"7bed6721a527e43182edc6b6a038fd8fe038f939c79def1cfba9976a917a3b03","src/cmd.rs":"a5d978fd5987488cd99a92d2561b70f122a3c3e8e93d0b8a89094a91bdcbbf94","src/config.rs":"2513f4f8125dfe8700c295973a316caa7216a54323ed600a49cff4d9b09eebbf","src/config/args.rs":"50e1ffc9eed4f94edf2e4bacf1d07e827c7a4fc60c1cb16d2003360b72945c6f","src/dependencies.rs":"4ddfada523b48f5053e464cd77b8c59d440fbf0c79481543405a57b6aa613d21","src/diff.rs":"b05d708188e82d7eaa36957b4a8d273e16954d304c99b58b3655087a26ddff39","src/error.rs":"14dd6d90542e3b6b2dc7ce706be73b8025f3e224639ffcfb965af5c3d210cae5","src/github_actions.rs":"f3fffae4629a3f27798f8d62a7627967e82396b36e951bb0db7596ed6b59a5cb","src/lib.rs":"743da0c1b327fe3434e89fc93fb449a22c7936bdedb2830c9c52d5895a125f65","src/mode.rs":"bcf256b2f5feefe14400b19f1b0176b2f01e3143197005a26d5f3dbf4455150a","src/parser.rs":"f2c473d8fd5c19ca54caa6a6f623acbf01389df405cbedf4d113caa0406e3582","src/parser/spanned.rs":"efebe51e6f2e8671903959230002fa71f7afbe65fbd24b3fba97607380364770","src/parser/tests.rs":"0850b18327af3a2ad97e7808541ae7616c483ff3fa1a059495e272ee5b4a1a05","src/rustc_stderr.rs":"92f93e47cc1f8dc26a854ddd51f0dbf92ca06503e3c82ba6fff5894a7bf4fffb","src/status_emitter.rs":"3cd65a96d774a62f3100398e080894fc5a670ab3e1f9d7c7bb60801452f00c37","src/tests.rs":"c5d91016280c8e6614f7244a008888a3fbb8f2c2775b347f420aa77d95134e62","tests/integration.rs":"8c755d022d019390cfa8b1cda06ee1e40052917118e9d30f9dc3c31160497cb3"},"package":"bfd8fb9b15c8332cf51bfc2dc4830063b2446a9c9d732421b56f2478024a3971"}
\ No newline at end of file diff --git a/vendor/ui_test-0.20.0/CONTRIBUTING.md b/vendor/ui_test-0.20.0/CONTRIBUTING.md deleted file mode 100644 index d5165080c..000000000 --- a/vendor/ui_test-0.20.0/CONTRIBUTING.md +++ /dev/null @@ -1,7 +0,0 @@ -## Running the test suite - -Running `cargo test` will automatically update the `.stderr` -and `.stdout` files. - -If you only want to check that the output files match and not -update them, use `cargo test -- -- --check` diff --git a/vendor/ui_test-0.20.0/Cargo.toml b/vendor/ui_test-0.20.0/Cargo.toml deleted file mode 100644 index 903a924c3..000000000 --- a/vendor/ui_test-0.20.0/Cargo.toml +++ /dev/null @@ -1,92 +0,0 @@ -# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO -# -# When uploading crates to the registry Cargo will automatically -# "normalize" Cargo.toml files for maximal compatibility -# with all versions of Cargo and also rewrite `path` dependencies -# to registry (e.g., crates.io) dependencies. -# -# If you are reading this file be aware that the original Cargo.toml -# will likely look very different (and much more reasonable). -# See Cargo.toml.orig for the original contents. - -[package] -edition = "2021" -rust-version = "1.63" -name = "ui_test" -version = "0.20.0" -description = "A test framework for testing rustc diagnostics output" -readme = "README.md" -license = "MIT OR Apache-2.0" -repository = "https://github.com/oli-obk/ui_test" - -[lib] -test = true -doctest = false - -[[test]] -name = "integration" -harness = false - -[dependencies.annotate-snippets] -version = "0.9.1" -features = ["color"] - -[dependencies.anyhow] -version = "1.0.6" - -[dependencies.bstr] -version = "1.0.1" - -[dependencies.cargo-platform] -version = "0.1.2" - -[dependencies.cargo_metadata] -version = "0.15" - -[dependencies.color-eyre] -version = "0.6.1" -features = ["capture-spantrace"] -default-features = false - -[dependencies.colored] -version = "2" - -[dependencies.comma] -version = "1.0.0" - -[dependencies.crossbeam-channel] -version = "0.5.6" - -[dependencies.indicatif] -version = "0.17.6" - -[dependencies.lazy_static] -version = "1.4.0" - -[dependencies.levenshtein] -version = "1.0.5" - -[dependencies.prettydiff] -version = "0.6.4" -default_features = false - -[dependencies.regex] -version = "1.5.5" -features = ["unicode-gencat"] -default-features = false - -[dependencies.rustc_version] -version = "0.4" - -[dependencies.rustfix] -version = "0.6.1" - -[dependencies.serde] -version = "1.0" -features = ["derive"] - -[dependencies.serde_json] -version = "1.0" - -[dependencies.tempfile] -version = "3.3.0" diff --git a/vendor/ui_test-0.20.0/README.md b/vendor/ui_test-0.20.0/README.md deleted file mode 100644 index 40dcc5ec4..000000000 --- a/vendor/ui_test-0.20.0/README.md +++ /dev/null @@ -1,64 +0,0 @@ -A smaller version of compiletest-rs - -## Magic behavior - -* Tests are run in order of their filenames (files first, then recursing into folders). - So if you have any slow tests, prepend them with a small integral number to make them get run first, taking advantage of parallelism as much as possible (instead of waiting for the slow tests at the end). -* `cargo test --test your_test_name -- --help` lists the commands you can specify for filtering, blessing and making your tests less verbose. - * Since `cargo test` on its own runs all tests, using `cargo test -- --check` will not work on its own, but `cargo test -- --quiet` and `cargo test -- some_test_name` will work just fine, as the CLI matches. - -## Supported magic comment annotations - -If your test tests for failure, you need to add a `//~` annotation where the error is happening -to make sure that the test will always keep failing with a specific message at the annotated line. - -`//~ ERROR: XXX` make sure the stderr output contains `XXX` for an error in the line where this comment is written - -* Also supports `HELP`, `WARN` or `NOTE` for different kind of message - * if one of those levels is specified explicitly, *all* diagnostics of this level or higher need an annotation. If you want to avoid this, just leave out the all caps level note entirely. -* If the all caps note is left out, a message of any level is matched. Leaving it out is not allowed for `ERROR` levels. -* This checks the output *before* normalization, so you can check things that get normalized away, but need to - be careful not to accidentally have a pattern that differs between platforms. -* if `XXX` is of the form `/XXX/` it is treated as a regex instead of a substring and will succeed if the regex matches. - -In order to change how a single test is tested, you can add various `//@` comments to the test. -Any other comments will be ignored, and all `//@` comments must be formatted precisely as -their command specifies, or the test will fail without even being run. - -* `//@ignore-C` avoids running the test when condition `C` is met. - * `C` can be `target-XXX`, which checks whether the target triple contains `XXX`. - * `C` can also be one of `64bit`, `32bit` or `16bit`. - * `C` can also be `on-host`, which will only run the test during cross compilation testing. -* `//@only-C` **only** runs the test when condition `C` is met. The conditions are the same as with `ignore`. -* `//@needs-asm-support` **only** runs the test when the target supports `asm!`. -* `//@stderr-per-bitwidth` produces one stderr file per bitwidth, as they may differ significantly sometimes -* `//@error-in-other-file: XXX` can be used to check for errors that can't have `//~` patterns due to being reported in other files. -* `//@revisions: XXX YYY` runs the test once for each space separated name in the list - * emits one stderr file per revision - * `//~` comments can be restricted to specific revisions by adding the revision name after the `~` in square brackets: `//~[XXX]` - * `//@` comments can be restricted to specific revisions by adding the revision name after the `@` in square brackets: `//@[XXX]` - * Note that you cannot add revisions to the `revisions` command. -* `//@compile-flags: XXX` appends `XXX` to the command line arguments passed to the rustc driver - * you can specify this multiple times, and all the flags will accumulate -* `//@rustc-env: XXX=YYY` sets the env var `XXX` to `YYY` for the rustc driver execution. - * for Miri these env vars are used during compilation via rustc and during the emulation of the program - * you can specify this multiple times, accumulating all the env vars -* `//@normalize-stderr-test: "REGEX" -> "REPLACEMENT"` replaces all matches of `REGEX` in the stderr with `REPLACEMENT`. The replacement may specify `$1` and similar backreferences to paste captures. - * you can specify multiple such commands, there is no need to create a single regex that handles multiple replacements that you want to perform. -* `//@require-annotations-for-level: LEVEL` can be used to change the level of diagnostics that require a corresponding annotation. - * this is only useful if there are any annotations like `HELP`, `WARN` or `NOTE`, as these would automatically require annotations for all other diagnostics of the same or higher level. -* `//@check-pass` requires that a test has no error annotations, emits no errors, and exits successfully with exit/status code 0. -* `//@edition: EDITION` overwrites the default edition (2021) to the given edition. -* `//@no-rustfix` do not run [rustfix] on tests that have machine applicable suggestions. -* `//@aux-build: filename` looks for a file in the `auxiliary` directory (within the directory of the test), compiles it as a library and links the current crate against it. This allows you import the crate with `extern crate` or just via `use` statements. This will automatically detect aux files that are proc macros and build them as proc macros. -* `//@run` compiles the test and runs the resulting binary. The resulting binary must exit successfully. Stdout and stderr are taken from the resulting binary. Any warnings during compilation are ignored. - * You can also specify a different exit code/status that is expected via e.g. `//@run: 1` or `//@run: 101` (the latter is the standard Rust exit code for panics). - -[rustfix]: https://github.com/rust-lang/rustfix - -## Significant differences to compiletest-rs - -* `ignore-target-*` and `only-target-*` operate solely on the triple, instead of supporting things like `macos` -* only supports `ui` tests -* tests are run in named order, so you can prefix slow tests with `0` in order to make them get run first -* `aux-build`s require specifying nested aux builds explicitly and will not allow you to reference sibling `aux-build`s' artifacts. diff --git a/vendor/ui_test-0.20.0/src/cmd.rs b/vendor/ui_test-0.20.0/src/cmd.rs deleted file mode 100644 index 53fa3e0f4..000000000 --- a/vendor/ui_test-0.20.0/src/cmd.rs +++ /dev/null @@ -1,120 +0,0 @@ -use std::{ - ffi::OsString, - path::{Path, PathBuf}, - process::Command, -}; - -#[derive(Debug, Clone)] -/// A command, its args and its environment. Used for -/// the main command, the dependency builder and the cfg-reader. -pub struct CommandBuilder { - /// Path to the binary. - pub program: PathBuf, - /// Arguments to the binary. - pub args: Vec<OsString>, - /// A flag to prefix before the path to where output files should be written. - pub out_dir_flag: Option<OsString>, - /// A flag to set as the last flag in the command, so the `build` caller can - /// append the filename themselves. - pub input_file_flag: Option<OsString>, - /// Environment variables passed to the binary that is executed. - /// The environment variable is removed if the second tuple field is `None` - pub envs: Vec<(OsString, Option<OsString>)>, -} - -impl CommandBuilder { - /// Uses the `CARGO` env var or just a program named `cargo` and the argument `build`. - pub fn cargo() -> Self { - Self { - program: PathBuf::from(std::env::var_os("CARGO").unwrap_or_else(|| "cargo".into())), - args: vec!["build".into()], - out_dir_flag: Some("--target-dir".into()), - input_file_flag: Some("--manifest-path".into()), - envs: vec![], - } - } - - /// Uses the `RUSTC` env var or just a program named `rustc` and the argument `--error-format=json`. - /// - /// Take care to only append unless you actually meant to overwrite the defaults. - /// Overwriting the defaults may make `//~ ERROR` style comments stop working. - pub fn rustc() -> Self { - Self { - program: PathBuf::from(std::env::var_os("RUSTC").unwrap_or_else(|| "rustc".into())), - args: vec!["--error-format=json".into()], - out_dir_flag: Some("--out-dir".into()), - input_file_flag: None, - envs: vec![], - } - } - - /// Same as [`CommandBuilder::rustc`], but with arguments for obtaining the cfgs. - pub fn cfgs() -> Self { - Self { - args: vec!["--print".into(), "cfg".into()], - ..Self::rustc() - } - } - - /// Build a `CommandBuilder` for a command without any argumemnts. - /// You can still add arguments later. - pub fn cmd(cmd: impl Into<PathBuf>) -> Self { - Self { - program: cmd.into(), - args: vec![], - out_dir_flag: None, - input_file_flag: None, - envs: vec![], - } - } - - /// Render the command like you'd use it on a command line. - pub fn display(&self) -> impl std::fmt::Display + '_ { - struct Display<'a>(&'a CommandBuilder); - impl std::fmt::Display for Display<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - for (var, val) in &self.0.envs { - if let Some(val) = val { - write!(f, "{var:?}={val:?} ")?; - } - } - self.0.program.display().fmt(f)?; - for arg in &self.0.args { - write!(f, " {arg:?}")?; - } - if let Some(flag) = &self.0.out_dir_flag { - write!(f, " {flag:?} OUT_DIR")?; - } - if let Some(flag) = &self.0.input_file_flag { - write!(f, " {flag:?}")?; - } - Ok(()) - } - } - Display(self) - } - - /// Create a command with the given settings. - pub fn build(&self, out_dir: &Path) -> Command { - let mut cmd = Command::new(&self.program); - cmd.args(self.args.iter()); - if let Some(flag) = &self.out_dir_flag { - cmd.arg(flag).arg(out_dir); - } - if let Some(flag) = &self.input_file_flag { - cmd.arg(flag); - } - self.apply_env(&mut cmd); - cmd - } - - pub(crate) fn apply_env(&self, cmd: &mut Command) { - for (var, val) in self.envs.iter() { - if let Some(val) = val { - cmd.env(var, val); - } else { - cmd.env_remove(var); - } - } - } -} diff --git a/vendor/ui_test-0.20.0/src/config.rs b/vendor/ui_test-0.20.0/src/config.rs deleted file mode 100644 index 6779bab7d..000000000 --- a/vendor/ui_test-0.20.0/src/config.rs +++ /dev/null @@ -1,272 +0,0 @@ -use regex::bytes::Regex; - -use crate::{dependencies::build_dependencies, CommandBuilder, Filter, Match, Mode, RustfixMode}; -pub use color_eyre; -use color_eyre::eyre::Result; -use std::{ - ffi::OsString, - num::NonZeroUsize, - path::{Path, PathBuf}, -}; - -mod args; -pub use args::Args; - -#[derive(Debug, Clone)] -/// Central datastructure containing all information to run the tests. -pub struct Config { - /// Host triple; usually will be auto-detected. - pub host: Option<String>, - /// `None` to run on the host, otherwise a target triple - pub target: Option<String>, - /// Filters applied to stderr output before processing it. - /// By default contains a filter for replacing backslashes in paths with - /// regular slashes. - /// On windows, contains a filter to remove `\r`. - pub stderr_filters: Filter, - /// Filters applied to stdout output before processing it. - /// On windows, contains a filter to remove `\r`. - pub stdout_filters: Filter, - /// The folder in which to start searching for .rs files - pub root_dir: PathBuf, - /// The mode in which to run the tests. - pub mode: Mode, - /// The binary to actually execute. - pub program: CommandBuilder, - /// The command to run to obtain the cfgs that the output is supposed to - pub cfgs: CommandBuilder, - /// What to do in case the stdout/stderr output differs from the expected one. - pub output_conflict_handling: OutputConflictHandling, - /// Path to a `Cargo.toml` that describes which dependencies the tests can access. - pub dependencies_crate_manifest_path: Option<PathBuf>, - /// The command to run can be changed from `cargo` to any custom command to build the - /// dependencies in `dependencies_crate_manifest_path`. - pub dependency_builder: CommandBuilder, - /// Where to dump files like the binaries compiled from tests. - /// Defaults to `target/ui` in the current directory. - pub out_dir: PathBuf, - /// The default edition to use on all tests. - pub edition: Option<String>, - /// Skip test files whose names contain any of these entries. - pub skip_files: Vec<String>, - /// Only test files whose names contain any of these entries. - pub filter_files: Vec<String>, - /// Override the number of threads to use. - pub threads: Option<NonZeroUsize>, -} - -impl Config { - /// Create a configuration for testing the output of running - /// `rustc` on the test files. - pub fn rustc(root_dir: impl Into<PathBuf>) -> Self { - Self { - host: None, - target: None, - stderr_filters: vec![ - (Match::PathBackslash, b"/"), - #[cfg(windows)] - (Match::Exact(vec![b'\r']), b""), - #[cfg(windows)] - (Match::Exact(br"\\?\".to_vec()), b""), - ], - stdout_filters: vec![ - (Match::PathBackslash, b"/"), - #[cfg(windows)] - (Match::Exact(vec![b'\r']), b""), - #[cfg(windows)] - (Match::Exact(br"\\?\".to_vec()), b""), - ], - root_dir: root_dir.into(), - mode: Mode::Fail { - require_patterns: true, - rustfix: RustfixMode::MachineApplicable, - }, - program: CommandBuilder::rustc(), - cfgs: CommandBuilder::cfgs(), - output_conflict_handling: OutputConflictHandling::Bless, - dependencies_crate_manifest_path: None, - dependency_builder: CommandBuilder::cargo(), - out_dir: std::env::var_os("CARGO_TARGET_DIR") - .map(PathBuf::from) - .unwrap_or_else(|| std::env::current_dir().unwrap().join("target")) - .join("ui"), - edition: Some("2021".into()), - skip_files: Vec::new(), - filter_files: Vec::new(), - threads: None, - } - } - - /// Create a configuration for testing the output of running - /// `cargo` on the test `Cargo.toml` files. - pub fn cargo(root_dir: impl Into<PathBuf>) -> Self { - Self { - program: CommandBuilder::cargo(), - edition: None, - mode: Mode::Fail { - require_patterns: true, - rustfix: RustfixMode::Disabled, - }, - ..Self::rustc(root_dir) - } - } - - /// Populate the config with the values from parsed command line arguments. - /// If neither `--bless` or `--check` are provided `default_bless` is used. - /// - /// The default output conflict handling command suggests adding `--bless` - /// to the end of the current command. - pub fn with_args(&mut self, args: &Args, default_bless: bool) { - let Args { - ref filters, - quiet: _, - check, - bless, - threads, - ref skip, - } = *args; - - self.threads = threads.or(self.threads); - - self.filter_files.extend_from_slice(filters); - self.skip_files.extend_from_slice(skip); - - let bless = match (bless, check) { - (_, true) => false, - (true, _) => true, - _ => default_bless, - }; - self.output_conflict_handling = if bless { - OutputConflictHandling::Bless - } else { - OutputConflictHandling::Error(format!( - "{} --bless", - std::env::args() - .map(|s| format!("{s:?}")) - .collect::<Vec<_>>() - .join(" ") - )) - }; - } - - /// Replace all occurrences of a path in stderr/stdout with a byte string. - pub fn path_filter(&mut self, path: &Path, replacement: &'static (impl AsRef<[u8]> + ?Sized)) { - self.path_stderr_filter(path, replacement); - self.path_stdout_filter(path, replacement); - } - - /// Replace all occurrences of a path in stderr with a byte string. - pub fn path_stderr_filter( - &mut self, - path: &Path, - replacement: &'static (impl AsRef<[u8]> + ?Sized), - ) { - let pattern = path.canonicalize().unwrap(); - self.stderr_filters - .push((pattern.parent().unwrap().into(), replacement.as_ref())); - } - - /// Replace all occurrences of a path in stdout with a byte string. - pub fn path_stdout_filter( - &mut self, - path: &Path, - replacement: &'static (impl AsRef<[u8]> + ?Sized), - ) { - let pattern = path.canonicalize().unwrap(); - self.stdout_filters - .push((pattern.parent().unwrap().into(), replacement.as_ref())); - } - - /// Replace all occurrences of a regex pattern in stderr/stdout with a byte string. - pub fn filter(&mut self, pattern: &str, replacement: &'static (impl AsRef<[u8]> + ?Sized)) { - self.stderr_filter(pattern, replacement); - self.stdout_filter(pattern, replacement); - } - - /// Replace all occurrences of a regex pattern in stderr with a byte string. - pub fn stderr_filter( - &mut self, - pattern: &str, - replacement: &'static (impl AsRef<[u8]> + ?Sized), - ) { - self.stderr_filters - .push((Regex::new(pattern).unwrap().into(), replacement.as_ref())); - } - - /// Replace all occurrences of a regex pattern in stdout with a byte string. - pub fn stdout_filter( - &mut self, - pattern: &str, - replacement: &'static (impl AsRef<[u8]> + ?Sized), - ) { - self.stdout_filters - .push((Regex::new(pattern).unwrap().into(), replacement.as_ref())); - } - - /// Compile dependencies and return the right flags - /// to find the dependencies. - pub fn build_dependencies(&self) -> Result<Vec<OsString>> { - let dependencies = build_dependencies(self)?; - let mut args = vec![]; - for (name, artifacts) in dependencies.dependencies { - for dependency in artifacts { - args.push("--extern".into()); - let mut dep = OsString::from(&name); - dep.push("="); - dep.push(dependency); - args.push(dep); - } - } - for import_path in dependencies.import_paths { - args.push("-L".into()); - args.push(import_path.into()); - } - Ok(args) - } - - /// Make sure we have the host and target triples. - pub fn fill_host_and_target(&mut self) -> Result<()> { - if self.host.is_none() { - self.host = Some( - rustc_version::VersionMeta::for_command(std::process::Command::new( - &self.program.program, - )) - .map_err(|err| { - color_eyre::eyre::Report::new(err).wrap_err(format!( - "failed to parse rustc version info: {}", - self.program.display() - )) - })? - .host, - ); - } - if self.target.is_none() { - self.target = Some(self.host.clone().unwrap()); - } - Ok(()) - } - - pub(crate) fn has_asm_support(&self) -> bool { - static ASM_SUPPORTED_ARCHS: &[&str] = &[ - "x86", "x86_64", "arm", "aarch64", "riscv32", - "riscv64", - // These targets require an additional asm_experimental_arch feature. - // "nvptx64", "hexagon", "mips", "mips64", "spirv", "wasm32", - ]; - ASM_SUPPORTED_ARCHS - .iter() - .any(|arch| self.target.as_ref().unwrap().contains(arch)) - } -} - -#[derive(Debug, Clone)] -/// The different options for what to do when stdout/stderr files differ from the actual output. -pub enum OutputConflictHandling { - /// The string should be a command that can be executed to bless all tests. - Error(String), - /// Ignore mismatches in the stderr/stdout files. - Ignore, - /// Instead of erroring if the stderr/stdout differs from the expected - /// automatically replace it with the found output (after applying filters). - Bless, -} diff --git a/vendor/ui_test-0.20.0/src/config/args.rs b/vendor/ui_test-0.20.0/src/config/args.rs deleted file mode 100644 index 2d65dc7d5..000000000 --- a/vendor/ui_test-0.20.0/src/config/args.rs +++ /dev/null @@ -1,90 +0,0 @@ -//! Default argument processing when `ui_test` is used -//! as a test driver. - -use std::{borrow::Cow, num::NonZeroUsize}; - -use color_eyre::eyre::{bail, ensure, Result}; - -/// Plain arguments if `ui_test` is used as a binary. -#[derive(Debug, Default)] -pub struct Args { - /// Filters that will be used to match on individual tests - pub filters: Vec<String>, - - /// Whether to minimize output given to the user. - pub quiet: bool, - - /// Whether to error on mismatches between `.stderr` files and actual - /// output. - pub check: bool, - - /// Whether to overwrite `.stderr` files on mismtach with the actual - /// output. - pub bless: bool, - - /// The number of threads to use - pub threads: Option<NonZeroUsize>, - - /// Skip tests whose names contain any of these entries. - pub skip: Vec<String>, -} - -impl Args { - /// Parse the program arguments. - /// This is meant to be used if `ui_test` is used as a `harness=false` test, called from `cargo test`. - pub fn test() -> Result<Self> { - Self::default().parse_args(std::env::args().skip(1)) - } - - /// Parse arguments into an existing `Args` struct. - pub fn parse_args(mut self, mut iter: impl Iterator<Item = String>) -> Result<Self> { - while let Some(arg) = iter.next() { - if arg == "--" { - continue; - } - if arg == "--quiet" { - self.quiet = true; - } else if arg == "--check" { - self.check = true; - } else if arg == "--bless" { - self.bless = true; - } else if let Some(skip) = parse_value("--skip", &arg, &mut iter)? { - self.skip.push(skip.into_owned()); - } else if arg == "--help" { - bail!("available flags: --quiet, --check, --bless, --test-threads=n, --skip") - } else if let Some(n) = parse_value("--test-threads", &arg, &mut iter)? { - self.threads = Some(n.parse()?); - } else if arg.starts_with("--") { - bail!( - "unknown command line flag `{arg}`: {:?}", - iter.collect::<Vec<_>>() - ); - } else { - self.filters.push(arg); - } - } - Ok(self) - } -} - -fn parse_value<'a>( - name: &str, - arg: &'a str, - iter: &mut impl Iterator<Item = String>, -) -> Result<Option<Cow<'a, str>>> { - let with_eq = match arg.strip_prefix(name) { - Some(s) => s, - None => return Ok(None), - }; - if let Some(n) = with_eq.strip_prefix('=') { - Ok(Some(n.into())) - } else { - ensure!(with_eq.is_empty(), "`{name}` can only be followed by `=`"); - - if let Some(next) = iter.next() { - Ok(Some(next.into())) - } else { - bail!("`name` must be followed by a value") - } - } -} diff --git a/vendor/ui_test-0.20.0/src/dependencies.rs b/vendor/ui_test-0.20.0/src/dependencies.rs deleted file mode 100644 index 0e95e0f84..000000000 --- a/vendor/ui_test-0.20.0/src/dependencies.rs +++ /dev/null @@ -1,320 +0,0 @@ -use cargo_metadata::{camino::Utf8PathBuf, DependencyKind}; -use cargo_platform::Cfg; -use color_eyre::eyre::{bail, eyre, Result}; -use std::{ - collections::{hash_map::Entry, HashMap, HashSet}, - ffi::OsString, - path::PathBuf, - process::Command, - str::FromStr, - sync::{Arc, OnceLock, RwLock}, -}; - -use crate::{ - build_aux, status_emitter::StatusEmitter, Config, Errored, Mode, OutputConflictHandling, -}; - -#[derive(Default, Debug)] -pub struct Dependencies { - /// All paths that must be imported with `-L dependency=`. This is for - /// finding proc macros run on the host and dependencies for the target. - pub import_paths: Vec<PathBuf>, - /// The name as chosen in the `Cargo.toml` and its corresponding rmeta file. - pub dependencies: Vec<(String, Vec<Utf8PathBuf>)>, -} - -fn cfgs(config: &Config) -> Result<Vec<Cfg>> { - let mut cmd = config.cfgs.build(&config.out_dir); - cmd.arg("--target").arg(config.target.as_ref().unwrap()); - let output = cmd.output()?; - let stdout = String::from_utf8(output.stdout)?; - - if !output.status.success() { - let stderr = String::from_utf8(output.stderr)?; - bail!( - "failed to obtain `cfg` information from {cmd:?}:\nstderr:\n{stderr}\n\nstdout:{stdout}" - ); - } - let mut cfgs = vec![]; - - for line in stdout.lines() { - cfgs.push(Cfg::from_str(line)?); - } - - Ok(cfgs) -} - -/// Compiles dependencies and returns the crate names and corresponding rmeta files. -pub(crate) fn build_dependencies(config: &Config) -> Result<Dependencies> { - let manifest_path = match &config.dependencies_crate_manifest_path { - Some(path) => path.to_owned(), - None => return Ok(Default::default()), - }; - let manifest_path = &manifest_path; - let mut build = config.dependency_builder.build(&config.out_dir); - build.arg(manifest_path); - - if let Some(target) = &config.target { - build.arg(format!("--target={target}")); - } - - // Reusable closure for setting up the environment both for artifact generation and `cargo_metadata` - let set_locking = |cmd: &mut Command| match (&config.output_conflict_handling, &config.mode) { - (_, Mode::Yolo { .. }) => {} - (OutputConflictHandling::Error(_), _) => { - cmd.arg("--locked"); - } - _ => {} - }; - - set_locking(&mut build); - build.arg("--message-format=json"); - - let output = build.output()?; - - if !output.status.success() { - let stdout = String::from_utf8(output.stdout)?; - let stderr = String::from_utf8(output.stderr)?; - bail!("failed to compile dependencies:\ncommand: {build:?}\nstderr:\n{stderr}\n\nstdout:{stdout}"); - } - - // Collect all artifacts generated - let artifact_output = output.stdout; - let artifact_output = String::from_utf8(artifact_output)?; - let mut import_paths: HashSet<PathBuf> = HashSet::new(); - let mut artifacts = HashMap::new(); - for line in artifact_output.lines() { - let Ok(message) = serde_json::from_str::<cargo_metadata::Message>(line) else { - continue; - }; - if let cargo_metadata::Message::CompilerArtifact(artifact) = message { - if artifact - .filenames - .iter() - .any(|f| f.ends_with("build-script-build")) - { - continue; - } - // Check that we only collect rmeta and rlib crates, not build script crates - if artifact - .filenames - .iter() - .any(|f| !matches!(f.extension(), Some("rlib" | "rmeta"))) - { - continue; - } - for filename in &artifact.filenames { - import_paths.insert(filename.parent().unwrap().into()); - } - let package_id = artifact.package_id; - if let Some(prev) = artifacts.insert(package_id.clone(), Ok(artifact.filenames)) { - artifacts.insert( - package_id.clone(), - Err(format!("{prev:#?} vs {:#?}", artifacts[&package_id])), - ); - } - } - } - - // Check which crates are mentioned in the crate itself - let mut metadata = cargo_metadata::MetadataCommand::new().cargo_command(); - metadata.arg("--manifest-path").arg(manifest_path); - config.dependency_builder.apply_env(&mut metadata); - set_locking(&mut metadata); - let output = metadata.output()?; - - if !output.status.success() { - let stdout = String::from_utf8(output.stdout)?; - let stderr = String::from_utf8(output.stderr)?; - bail!("failed to run cargo-metadata:\nstderr:\n{stderr}\n\nstdout:{stdout}"); - } - - let output = output.stdout; - let output = String::from_utf8(output)?; - - let cfg = cfgs(config)?; - - for line in output.lines() { - if !line.starts_with('{') { - continue; - } - let metadata: cargo_metadata::Metadata = serde_json::from_str(line)?; - // Only take artifacts that are defined in the Cargo.toml - - // First, find the root artifact - let root = metadata - .packages - .iter() - .find(|package| { - package.manifest_path.as_std_path().canonicalize().unwrap() - == manifest_path.canonicalize().unwrap() - }) - .unwrap(); - - // Then go over all of its dependencies - let dependencies = root - .dependencies - .iter() - .filter(|dep| matches!(dep.kind, DependencyKind::Normal)) - // Only consider dependencies that are enabled on the current target - .filter(|dep| match &dep.target { - Some(platform) => platform.matches(config.target.as_ref().unwrap(), &cfg), - None => true, - }) - .map(|dep| { - let package = metadata - .packages - .iter() - .find(|&p| p.name == dep.name && dep.req.matches(&p.version)) - .expect("dependency does not exist"); - ( - package, - dep.rename.clone().unwrap_or_else(|| package.name.clone()), - ) - }) - // Also expose the root crate - .chain(std::iter::once((root, root.name.clone()))) - .filter_map(|(package, name)| { - // Get the id for the package matching the version requirement of the dep - let id = &package.id; - // Return the name chosen in `Cargo.toml` and the path to the corresponding artifact - match artifacts.remove(id) { - Some(Ok(artifacts)) => Some(Ok((name.replace('-', "_"), artifacts))), - Some(Err(what)) => Some(Err(eyre!("`ui_test` does not support crates that appear as both build-dependencies and core dependencies: {id}: {what}"))), - None => { - if name == root.name { - // If there are no artifacts, this is the root crate and it is being built as a binary/test - // instead of a library. We simply add no artifacts, meaning you can't depend on functions - // and types declared in the root crate. - None - } else { - panic!("no artifact found for `{name}`(`{id}`):`\n{artifact_output}") - } - } - } - }) - .collect::<Result<Vec<_>>>()?; - let import_paths = import_paths.into_iter().collect(); - return Ok(Dependencies { - dependencies, - import_paths, - }); - } - - bail!("no json found in cargo-metadata output") -} - -#[derive(PartialEq, Eq, Debug, Hash, Clone)] -pub enum Build { - /// Build the dependencies. - Dependencies, - /// Build an aux-build. - Aux { aux_file: PathBuf }, -} -impl Build { - fn description(&self) -> String { - match self { - Build::Dependencies => "Building dependencies".into(), - Build::Aux { aux_file } => format!("Building aux file {}", aux_file.display()), - } - } -} - -pub struct BuildManager<'a> { - #[allow(clippy::type_complexity)] - cache: RwLock<HashMap<Build, Arc<OnceLock<Result<Vec<OsString>, ()>>>>>, - status_emitter: &'a dyn StatusEmitter, -} - -impl<'a> BuildManager<'a> { - pub fn new(status_emitter: &'a dyn StatusEmitter) -> Self { - Self { - cache: Default::default(), - status_emitter, - } - } - /// This function will block until the build is done and then return the arguments - /// that need to be passed in order to build the dependencies. - /// The error is only reported once, all follow up invocations of the same build will - /// have a generic error about a previous build failing. - pub fn build(&self, what: Build, config: &Config) -> Result<Vec<OsString>, Errored> { - // Fast path without much contention. - if let Some(res) = self.cache.read().unwrap().get(&what).and_then(|o| o.get()) { - return res.clone().map_err(|()| Errored { - command: Command::new(format!("{what:?}")), - errors: vec![], - stderr: b"previous build failed".to_vec(), - stdout: vec![], - }); - } - let mut lock = self.cache.write().unwrap(); - let once = match lock.entry(what.clone()) { - Entry::Occupied(entry) => { - if let Some(res) = entry.get().get() { - return res.clone().map_err(|()| Errored { - command: Command::new(format!("{what:?}")), - errors: vec![], - stderr: b"previous build failed".to_vec(), - stdout: vec![], - }); - } - entry.get().clone() - } - Entry::Vacant(entry) => { - let once = Arc::new(OnceLock::new()); - entry.insert(once.clone()); - once - } - }; - drop(lock); - - let mut err = None; - once.get_or_init(|| { - let build = self - .status_emitter - .register_test(what.description().into()) - .for_revision(""); - let res = match &what { - Build::Dependencies => match config.build_dependencies() { - Ok(args) => Ok(args), - Err(e) => { - err = Some(Errored { - command: Command::new(format!("{what:?}")), - errors: vec![], - stderr: format!("{e:?}").into_bytes(), - stdout: vec![], - }); - Err(()) - } - }, - Build::Aux { aux_file } => match build_aux(aux_file, config, self) { - Ok(args) => Ok(args.iter().map(Into::into).collect()), - Err(e) => { - err = Some(e); - Err(()) - } - }, - }; - build.done( - &res.as_ref() - .map(|_| crate::TestOk::Ok) - .map_err(|()| Errored { - command: Command::new(what.description()), - errors: vec![], - stderr: vec![], - stdout: vec![], - }), - ); - res - }) - .clone() - .map_err(|()| { - err.unwrap_or_else(|| Errored { - command: Command::new(what.description()), - errors: vec![], - stderr: b"previous build failed".to_vec(), - stdout: vec![], - }) - }) - } -} diff --git a/vendor/ui_test-0.20.0/src/diff.rs b/vendor/ui_test-0.20.0/src/diff.rs deleted file mode 100644 index b0346c250..000000000 --- a/vendor/ui_test-0.20.0/src/diff.rs +++ /dev/null @@ -1,164 +0,0 @@ -use colored::*; -use prettydiff::{basic::DiffOp, basic::DiffOp::*, diff_lines, diff_words}; - -/// How many lines of context are displayed around the actual diffs -const CONTEXT: usize = 2; - -fn skip(skipped_lines: &[&str]) { - // When the amount of skipped lines is exactly `CONTEXT * 2`, we already - // print all the context and don't actually skip anything. - match skipped_lines.len().checked_sub(CONTEXT * 2) { - Some(skipped @ 2..) => { - // Print an initial `CONTEXT` amount of lines. - for line in &skipped_lines[..CONTEXT] { - println!(" {line}"); - } - println!("... {skipped} lines skipped ..."); - // Print `... n lines skipped ...` followed by the last `CONTEXT` lines. - for line in &skipped_lines[skipped + CONTEXT..] { - println!(" {line}"); - } - } - _ => { - // Print all the skipped lines if the amount of context desired is less than the amount of lines - for line in skipped_lines { - println!(" {line}"); - } - } - } -} - -fn row(row: DiffOp<'_, &str>) { - match row { - Remove(l) => { - for l in l { - println!("{}{}", "-".red(), l.red()); - } - } - Equal(l) => { - skip(l); - } - Replace(l, r) => { - for (l, r) in l.iter().zip(r) { - print_line_diff(l, r); - } - } - Insert(r) => { - for r in r { - println!("{}{}", "+".green(), r.green()); - } - } - } -} - -fn print_line_diff(l: &str, r: &str) { - let diff = diff_words(l, r); - let diff = diff.diff(); - if has_both_insertions_and_deletions(&diff) - || !colored::control::SHOULD_COLORIZE.should_colorize() - { - // The line both adds and removes chars, print both lines, but highlight their differences instead of - // drawing the entire line in red/green. - print!("{}", "-".red()); - for char in &diff { - match *char { - Replace(l, _) | Remove(l) => { - for l in l { - print!("{}", l.to_string().on_red()) - } - } - Insert(_) => {} - Equal(l) => { - for l in l { - print!("{l}") - } - } - } - } - println!(); - print!("{}", "+".green()); - for char in diff { - match char { - Remove(_) => {} - Replace(_, r) | Insert(r) => { - for r in r { - print!("{}", r.to_string().on_green()) - } - } - Equal(r) => { - for r in r { - print!("{r}") - } - } - } - } - println!(); - } else { - // The line only adds or only removes chars, print a single line highlighting their differences. - print!("{}", "~".yellow()); - for char in diff { - match char { - Remove(l) => { - for l in l { - print!("{}", l.to_string().on_red()) - } - } - Equal(w) => { - for w in w { - print!("{w}") - } - } - Insert(r) => { - for r in r { - print!("{}", r.to_string().on_green()) - } - } - Replace(l, r) => { - for l in l { - print!("{}", l.to_string().on_red()) - } - for r in r { - print!("{}", r.to_string().on_green()) - } - } - } - } - println!(); - } -} - -fn has_both_insertions_and_deletions(diff: &[DiffOp<'_, &str>]) -> bool { - let mut seen_l = false; - let mut seen_r = false; - for char in diff { - let is_whitespace = |s: &[&str]| s.iter().any(|s| s.chars().any(|s| s.is_whitespace())); - match char { - Insert(l) if !is_whitespace(l) => seen_l = true, - Remove(r) if !is_whitespace(r) => seen_r = true, - Replace(l, r) if !is_whitespace(l) && !is_whitespace(r) => return true, - _ => {} - } - } - seen_l && seen_r -} - -pub fn print_diff(expected: &[u8], actual: &[u8]) { - let expected_str = String::from_utf8_lossy(expected); - let actual_str = String::from_utf8_lossy(actual); - - if expected_str.as_bytes() != expected || actual_str.as_bytes() != actual { - println!( - "{}", - "Non-UTF8 characters in output, diff may be imprecise.".red() - ); - } - - let pat = |c: char| c.is_whitespace() && c != ' ' && c != '\n' && c != '\r'; - let expected_str = expected_str.replace(pat, "â–‘"); - let actual_str = actual_str.replace(pat, "â–‘"); - - for r in diff_lines(&expected_str, &actual_str).diff() { - row(r); - } - println!() -} diff --git a/vendor/ui_test-0.20.0/src/error.rs b/vendor/ui_test-0.20.0/src/error.rs deleted file mode 100644 index 7298a699e..000000000 --- a/vendor/ui_test-0.20.0/src/error.rs +++ /dev/null @@ -1,81 +0,0 @@ -use crate::{ - parser::{Pattern, Spanned}, - rustc_stderr::{Message, Span}, - Mode, -}; -use std::{num::NonZeroUsize, path::PathBuf, process::ExitStatus}; - -/// All the ways in which a test can fail. -#[derive(Debug)] -#[must_use] -pub enum Error { - /// Got an invalid exit status for the given mode. - ExitStatus { - /// The expected mode. - mode: Mode, - /// The exit status of the command. - status: ExitStatus, - /// The expected exit status as set in the file or derived from the mode. - expected: i32, - }, - /// A pattern was declared but had no matching error. - PatternNotFound(Spanned<Pattern>), - /// A ui test checking for failure does not have any failure patterns - NoPatternsFound, - /// A ui test checking for success has failure patterns - PatternFoundInPassTest, - /// Stderr/Stdout differed from the `.stderr`/`.stdout` file present. - OutputDiffers { - /// The file containing the expected output that differs from the actual output. - path: PathBuf, - /// The output from the command. - actual: Vec<u8>, - /// The contents of the file. - expected: Vec<u8>, - /// A command, that when run, causes the output to get blessed instead of erroring. - bless_command: String, - }, - /// There were errors that don't have a pattern. - ErrorsWithoutPattern { - /// The main message of the error. - msgs: Vec<Message>, - /// File and line information of the error. - path: Option<Spanned<PathBuf>>, - }, - /// A comment failed to parse. - InvalidComment { - /// The comment - msg: String, - /// The character range in which it was defined. - span: Span, - }, - /// Conflicting comments - MultipleRevisionsWithResults { - /// The comment being looked for - kind: String, - /// The lines where conflicts happened - lines: Vec<NonZeroUsize>, - }, - /// A subcommand (e.g. rustfix) of a test failed. - Command { - /// The name of the subcommand (e.g. "rustfix"). - kind: String, - /// The exit status of the command. - status: ExitStatus, - }, - /// This catches crashes of ui tests and reports them along the failed test. - Bug(String), - /// An auxiliary build failed with its own set of errors. - Aux { - /// Path to the aux file. - path: PathBuf, - /// The errors that occurred during the build of the aux file. - errors: Vec<Error>, - /// The line in which the aux file was requested to be built. - line: NonZeroUsize, - }, - /// An error occured applying [`rustfix`] suggestions - Rustfix(anyhow::Error), -} - -pub(crate) type Errors = Vec<Error>; diff --git a/vendor/ui_test-0.20.0/src/github_actions.rs b/vendor/ui_test-0.20.0/src/github_actions.rs deleted file mode 100644 index 01e1d3bb2..000000000 --- a/vendor/ui_test-0.20.0/src/github_actions.rs +++ /dev/null @@ -1,96 +0,0 @@ -//! An interface to github actions workflow commands. - -use std::{ - fmt::{Debug, Write}, - num::NonZeroUsize, -}; - -/// Shows an error message directly in a github diff view on drop. -pub struct Error { - file: String, - line: usize, - title: String, - message: String, -} -impl Error { - /// Set a line for this error. By default the message is shown at the top of the file. - pub fn line(mut self, line: NonZeroUsize) -> Self { - self.line = line.get(); - self - } -} - -/// Create an error to be shown for the given file and with the given title. -pub fn error(file: impl std::fmt::Display, title: impl Into<String>) -> Error { - Error { - file: file.to_string(), - line: 0, - title: title.into(), - message: String::new(), - } -} - -impl Write for Error { - fn write_str(&mut self, s: &str) -> std::fmt::Result { - self.message.write_str(s) - } -} - -impl Drop for Error { - fn drop(&mut self) { - if std::env::var_os("GITHUB_ACTION").is_some() { - let Error { - file, - line, - title, - message, - } = self; - let message = message.trim(); - let message = if message.is_empty() { - "::no message".into() - } else { - format!("::{}", github_action_multiline_escape(message)) - }; - println!("::error file={file},line={line},title={title}{message}"); - } - } -} - -/// Append to the summary file that will be shown for the entire CI run. -pub fn summary() -> Option<impl std::io::Write> { - let path = std::env::var_os("GITHUB_STEP_SUMMARY")?; - Some(std::fs::OpenOptions::new().append(true).open(path).unwrap()) -} - -fn github_action_multiline_escape(s: &str) -> String { - s.replace('%', "%25") - .replace('\n', "%0A") - .replace('\r', "%0D") -} - -/// All github actions log messages from this call to the Drop of the return value -/// will be grouped and hidden by default in logs. Note that nesting these does -/// not really work. -pub fn group(name: impl std::fmt::Display) -> Group { - if std::env::var_os("GITHUB_ACTION").is_some() { - println!("::group::{name}"); - } - Group(()) -} - -/// A guard that closes the current github actions log group on drop. -pub struct Group(()); - -impl Debug for Group { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str("a handle that will close the github action group on drop") - } -} - -impl Drop for Group { - fn drop(&mut self) { - if std::env::var_os("GITHUB_ACTION").is_some() { - println!("::endgroup::"); - } - } -} diff --git a/vendor/ui_test-0.20.0/src/lib.rs b/vendor/ui_test-0.20.0/src/lib.rs deleted file mode 100644 index ba051be8c..000000000 --- a/vendor/ui_test-0.20.0/src/lib.rs +++ /dev/null @@ -1,1270 +0,0 @@ -#![allow( - clippy::enum_variant_names, - clippy::useless_format, - clippy::too_many_arguments, - rustc::internal -)] -#![deny(missing_docs)] - -//! A crate to run the Rust compiler (or other binaries) and test their command line output. - -use bstr::ByteSlice; -pub use color_eyre; -use color_eyre::eyre::{eyre, Result}; -use crossbeam_channel::{unbounded, Receiver, Sender}; -use dependencies::{Build, BuildManager}; -use lazy_static::lazy_static; -use parser::{ErrorMatch, MaybeSpanned, OptWithLine, Revisioned, Spanned}; -use regex::bytes::{Captures, Regex}; -use rustc_stderr::{Level, Message, Span}; -use status_emitter::{StatusEmitter, TestStatus}; -use std::borrow::Cow; -use std::collections::{HashSet, VecDeque}; -use std::ffi::OsString; -use std::num::NonZeroUsize; -use std::path::{Component, Path, PathBuf, Prefix}; -use std::process::{Command, ExitStatus}; -use std::thread; - -use crate::parser::{Comments, Condition}; - -mod cmd; -mod config; -mod dependencies; -mod diff; -mod error; -pub mod github_actions; -mod mode; -mod parser; -mod rustc_stderr; -pub mod status_emitter; -#[cfg(test)] -mod tests; - -pub use cmd::*; -pub use config::*; -pub use error::*; -pub use mode::*; - -/// A filter's match rule. -#[derive(Clone, Debug)] -pub enum Match { - /// If the regex matches, the filter applies - Regex(Regex), - /// If the exact byte sequence is found, the filter applies - Exact(Vec<u8>), - /// Uses a heuristic to find backslashes in windows style paths - PathBackslash, -} -impl Match { - fn replace_all<'a>(&self, text: &'a [u8], replacement: &[u8]) -> Cow<'a, [u8]> { - match self { - Match::Regex(regex) => regex.replace_all(text, replacement), - Match::Exact(needle) => text.replace(needle, replacement).into(), - Match::PathBackslash => { - lazy_static! { - static ref PATH_RE: Regex = Regex::new( - r"(?x) - (?: - # Match paths to files with extensions that don't include spaces - \\(?:[\pL\pN.\-_']+[/\\])*[\pL\pN.\-_']+\.\pL+ - | - # Allow spaces in absolute paths - [A-Z]:\\(?:[\pL\pN.\-_'\ ]+[/\\])+ - )", - ) - .unwrap(); - } - - PATH_RE.replace_all(text, |caps: &Captures<'_>| { - caps[0].replace(r"\", replacement) - }) - } - } - } -} - -impl From<&'_ Path> for Match { - fn from(v: &Path) -> Self { - let mut v = v.display().to_string(); - // Normalize away windows canonicalized paths. - if v.starts_with(r"\\?\") { - v.drain(0..4); - } - let mut v = v.into_bytes(); - // Normalize paths on windows to use slashes instead of backslashes, - // So that paths are rendered the same on all systems. - for c in &mut v { - if *c == b'\\' { - *c = b'/'; - } - } - Self::Exact(v) - } -} - -impl From<Regex> for Match { - fn from(v: Regex) -> Self { - Self::Regex(v) - } -} - -/// Replacements to apply to output files. -pub type Filter = Vec<(Match, &'static [u8])>; - -/// Run all tests as described in the config argument. -/// Will additionally process command line arguments. -pub fn run_tests(mut config: Config) -> Result<()> { - let args = Args::test()?; - if !args.quiet { - println!("Compiler: {}", config.program.display()); - } - - let name = config.root_dir.display().to_string(); - - let text = if args.quiet { - status_emitter::Text::quiet() - } else { - status_emitter::Text::verbose() - }; - config.with_args(&args, true); - - run_tests_generic( - vec![config], - default_file_filter, - default_per_file_config, - (text, status_emitter::Gha::<true> { name }), - ) -} - -/// The filter used by `run_tests` to only run on `.rs` files that are -/// specified by [`Config::filter_files`] and [`Config::skip_files`]. -pub fn default_file_filter(path: &Path, config: &Config) -> bool { - path.extension().is_some_and(|ext| ext == "rs") && default_any_file_filter(path, config) -} - -/// Run on all files that are specified by [`Config::filter_files`] and -/// [`Config::skip_files`]. -/// -/// To only include rust files see [`default_file_filter`]. -pub fn default_any_file_filter(path: &Path, config: &Config) -> bool { - let path = path.display().to_string(); - let contains_path = |files: &[String]| files.iter().any(|f| path.contains(f)); - - if contains_path(&config.skip_files) { - return false; - } - - config.filter_files.is_empty() || contains_path(&config.filter_files) -} - -/// The default per-file config used by `run_tests`. -pub fn default_per_file_config(config: &mut Config, _path: &Path, file_contents: &[u8]) { - // Heuristic: - // * if the file contains `#[test]`, automatically pass `--cfg test`. - // * if the file does not contain `fn main()` or `#[start]`, automatically pass `--crate-type=lib`. - // This avoids having to spam `fn main() {}` in almost every test. - if file_contents.find(b"#[proc_macro").is_some() { - config.program.args.push("--crate-type=proc-macro".into()) - } else if file_contents.find(b"#[test]").is_some() { - config.program.args.push("--test".into()); - } else if file_contents.find(b"fn main()").is_none() - && file_contents.find(b"#[start]").is_none() - { - config.program.args.push("--crate-type=lib".into()); - } -} - -/// Create a command for running a single file, with the settings from the `config` argument. -/// Ignores various settings from `Config` that relate to finding test files. -pub fn test_command(mut config: Config, path: &Path) -> Result<Command> { - config.fill_host_and_target()?; - let extra_args = config.build_dependencies()?; - - let comments = - Comments::parse_file(path)?.map_err(|errors| color_eyre::eyre::eyre!("{errors:#?}"))?; - let mut result = build_command(path, &config, "", &comments).unwrap(); - result.args(extra_args); - - Ok(result) -} - -/// The possible non-failure results a single test can have. -pub enum TestOk { - /// The test passed - Ok, - /// The test was ignored due to a rule (`//@only-*` or `//@ignore-*`) - Ignored, - /// The test was filtered with the `file_filter` argument. - Filtered, -} - -/// The possible results a single test can have. -pub type TestResult = Result<TestOk, Errored>; - -/// Information about a test failure. -#[derive(Debug)] -pub struct Errored { - /// Command that failed - command: Command, - /// The errors that were encountered. - errors: Vec<Error>, - /// The full stderr of the test run. - stderr: Vec<u8>, - /// The full stdout of the test run. - stdout: Vec<u8>, -} - -struct TestRun { - result: TestResult, - status: Box<dyn status_emitter::TestStatus>, -} - -/// A version of `run_tests` that allows more fine-grained control over running tests. -/// -/// If multiple configs are provided only the first [`Config::threads`] value is used -pub fn run_tests_generic( - mut configs: Vec<Config>, - file_filter: impl Fn(&Path, &Config) -> bool + Sync, - per_file_config: impl Fn(&mut Config, &Path, &[u8]) + Sync, - status_emitter: impl StatusEmitter + Send, -) -> Result<()> { - for config in &mut configs { - config.fill_host_and_target()?; - } - - let build_manager = BuildManager::new(&status_emitter); - - let mut results = vec![]; - - let num_threads = match configs.first().and_then(|config| config.threads) { - Some(threads) => threads, - None => match std::env::var_os("RUST_TEST_THREADS") { - Some(n) => n - .to_str() - .ok_or_else(|| eyre!("could not parse RUST_TEST_THREADS env var"))? - .parse()?, - None => std::thread::available_parallelism()?, - }, - }; - - run_and_collect( - num_threads, - |submit| { - let mut todo = VecDeque::new(); - for config in &configs { - todo.push_back((config.root_dir.clone(), config)); - } - while let Some((path, config)) = todo.pop_front() { - if path.is_dir() { - if path.file_name().unwrap() == "auxiliary" { - continue; - } - // Enqueue everything inside this directory. - // We want it sorted, to have some control over scheduling of slow tests. - let mut entries = std::fs::read_dir(path) - .unwrap() - .map(|e| e.unwrap().path()) - .collect::<Vec<_>>(); - entries.sort_by(|a, b| a.file_name().cmp(&b.file_name())); - for entry in entries { - todo.push_back((entry, config)); - } - } else if file_filter(&path, config) { - let status = status_emitter.register_test(path); - // Forward .rs files to the test workers. - submit.send((status, config)).unwrap(); - } - } - }, - |receive, finished_files_sender| -> Result<()> { - for (status, config) in receive { - let path = status.path(); - let file_contents = std::fs::read(path).unwrap(); - let mut config = config.clone(); - per_file_config(&mut config, path, &file_contents); - let result = match std::panic::catch_unwind(|| { - parse_and_test_file(&build_manager, &status, config, file_contents) - }) { - Ok(Ok(res)) => res, - Ok(Err(err)) => { - finished_files_sender.send(TestRun { - result: Err(err), - status, - })?; - continue; - } - Err(err) => { - finished_files_sender.send(TestRun { - result: Err(Errored { - command: Command::new("<unknown>"), - errors: vec![Error::Bug( - *Box::<dyn std::any::Any + Send + 'static>::downcast::<String>( - err, - ) - .unwrap(), - )], - stderr: vec![], - stdout: vec![], - }), - status, - })?; - continue; - } - }; - for result in result { - finished_files_sender.send(result)?; - } - } - Ok(()) - }, - |finished_files_recv| { - for run in finished_files_recv { - run.status.done(&run.result); - - results.push(run); - } - }, - )?; - - let mut failures = vec![]; - let mut succeeded = 0; - let mut ignored = 0; - let mut filtered = 0; - - for run in results { - match run.result { - Ok(TestOk::Ok) => succeeded += 1, - Ok(TestOk::Ignored) => ignored += 1, - Ok(TestOk::Filtered) => filtered += 1, - Err(errored) => failures.push((run.status, errored)), - } - } - - let mut failure_emitter = status_emitter.finalize(failures.len(), succeeded, ignored, filtered); - for ( - status, - Errored { - command, - errors, - stderr, - stdout, - }, - ) in &failures - { - let _guard = status.failed_test(command, stderr, stdout); - failure_emitter.test_failure(status, errors); - } - - if failures.is_empty() { - Ok(()) - } else { - Err(eyre!("tests failed")) - } -} - -/// A generic multithreaded runner that has a thread for producing work, -/// a thread for collecting work, and `num_threads` threads for doing the work. -pub fn run_and_collect<SUBMISSION: Send, RESULT: Send>( - num_threads: NonZeroUsize, - submitter: impl FnOnce(Sender<SUBMISSION>) + Send, - runner: impl Sync + Fn(&Receiver<SUBMISSION>, Sender<RESULT>) -> Result<()>, - collector: impl FnOnce(Receiver<RESULT>) + Send, -) -> Result<()> { - // A channel for files to process - let (submit, receive) = unbounded(); - - thread::scope(|s| { - // Create a thread that is in charge of walking the directory and submitting jobs. - // It closes the channel when it is done. - s.spawn(|| submitter(submit)); - - // A channel for the messages emitted by the individual test threads. - // Used to produce live updates while running the tests. - let (finished_files_sender, finished_files_recv) = unbounded(); - - s.spawn(|| collector(finished_files_recv)); - - let mut threads = vec![]; - - // Create N worker threads that receive files to test. - for _ in 0..num_threads.get() { - let finished_files_sender = finished_files_sender.clone(); - threads.push(s.spawn(|| runner(&receive, finished_files_sender))); - } - - for thread in threads { - thread.join().unwrap()?; - } - Ok(()) - }) -} - -fn parse_and_test_file( - build_manager: &BuildManager<'_>, - status: &dyn TestStatus, - mut config: Config, - file_contents: Vec<u8>, -) -> Result<Vec<TestRun>, Errored> { - let comments = parse_comments(&file_contents)?; - const EMPTY: &[String] = &[String::new()]; - // Run the test for all revisions - let revisions = comments.revisions.as_deref().unwrap_or(EMPTY); - let mut built_deps = false; - Ok(revisions - .iter() - .map(|revision| { - let status = status.for_revision(revision); - // Ignore file if only/ignore rules do (not) apply - if !status.test_file_conditions(&comments, &config) { - return TestRun { - result: Ok(TestOk::Ignored), - status, - }; - } - - if !built_deps { - status.update_status("waiting for dependencies to finish building".into()); - match build_manager.build(Build::Dependencies, &config) { - Ok(extra_args) => config.program.args.extend(extra_args), - Err(err) => { - return TestRun { - result: Err(err), - status, - } - } - } - status.update_status(String::new()); - built_deps = true; - } - - let result = status.run_test(build_manager, &config, &comments); - TestRun { result, status } - }) - .collect()) -} - -fn parse_comments(file_contents: &[u8]) -> Result<Comments, Errored> { - match Comments::parse(file_contents) { - Ok(comments) => Ok(comments), - Err(errors) => Err(Errored { - command: Command::new("parse comments"), - errors, - stderr: vec![], - stdout: vec![], - }), - } -} - -fn build_command( - path: &Path, - config: &Config, - revision: &str, - comments: &Comments, -) -> Result<Command, Errored> { - let mut cmd = config.program.build(&config.out_dir); - cmd.arg(path); - if !revision.is_empty() { - cmd.arg(format!("--cfg={revision}")); - } - for arg in comments - .for_revision(revision) - .flat_map(|r| r.compile_flags.iter()) - { - cmd.arg(arg); - } - let edition = comments.edition(revision, config)?; - - if let Some(edition) = edition { - cmd.arg("--edition").arg(&*edition); - } - - cmd.envs( - comments - .for_revision(revision) - .flat_map(|r| r.env_vars.iter()) - .map(|(k, v)| (k, v)), - ); - - Ok(cmd) -} - -fn build_aux( - aux_file: &Path, - config: &Config, - build_manager: &BuildManager<'_>, -) -> std::result::Result<Vec<OsString>, Errored> { - let file_contents = std::fs::read(aux_file).map_err(|err| Errored { - command: Command::new(format!("reading aux file `{}`", aux_file.display())), - errors: vec![], - stderr: err.to_string().into_bytes(), - stdout: vec![], - })?; - let comments = parse_comments(&file_contents)?; - assert_eq!( - comments.revisions, None, - "aux builds cannot specify revisions" - ); - - let mut config = config.clone(); - - // Strip any `crate-type` flags from the args, as we need to set our own, - // and they may conflict (e.g. `lib` vs `proc-macro`); - let mut prev_was_crate_type = false; - config.program.args.retain(|arg| { - if prev_was_crate_type { - prev_was_crate_type = false; - return false; - } - if arg == "--test" { - false - } else if arg == "--crate-type" { - prev_was_crate_type = true; - false - } else if let Some(arg) = arg.to_str() { - !arg.starts_with("--crate-type=") - } else { - true - } - }); - - default_per_file_config(&mut config, aux_file, &file_contents); - - // Put aux builds into a separate directory per path so that multiple aux files - // from different directories (but with the same file name) don't collide. - let relative = strip_path_prefix(aux_file.parent().unwrap(), &config.out_dir); - - config.out_dir.extend(relative); - - let mut aux_cmd = build_command(aux_file, &config, "", &comments)?; - - let mut extra_args = build_aux_files( - aux_file.parent().unwrap(), - &comments, - "", - &config, - build_manager, - )?; - // Make sure we see our dependencies - aux_cmd.args(extra_args.iter()); - - aux_cmd.arg("--emit=link"); - let filename = aux_file.file_stem().unwrap().to_str().unwrap(); - let output = aux_cmd.output().unwrap(); - if !output.status.success() { - let error = Error::Command { - kind: "compilation of aux build failed".to_string(), - status: output.status, - }; - return Err(Errored { - command: aux_cmd, - errors: vec![error], - stderr: rustc_stderr::process(aux_file, &output.stderr).rendered, - stdout: output.stdout, - }); - } - - // Now run the command again to fetch the output filenames - aux_cmd.arg("--print").arg("file-names"); - let output = aux_cmd.output().unwrap(); - assert!(output.status.success()); - - for file in output.stdout.lines() { - let file = std::str::from_utf8(file).unwrap(); - let crate_name = filename.replace('-', "_"); - let path = config.out_dir.join(file); - extra_args.push("--extern".into()); - let mut cname = OsString::from(&crate_name); - cname.push("="); - cname.push(path); - extra_args.push(cname); - // Help cargo find the crates added with `--extern`. - extra_args.push("-L".into()); - extra_args.push(config.out_dir.as_os_str().to_os_string()); - } - Ok(extra_args) -} - -impl dyn TestStatus { - fn run_test( - &self, - build_manager: &BuildManager<'_>, - config: &Config, - comments: &Comments, - ) -> TestResult { - let path = self.path(); - let revision = self.revision(); - - let extra_args = build_aux_files( - &path.parent().unwrap().join("auxiliary"), - comments, - revision, - config, - build_manager, - )?; - - let mut cmd = build_command(path, config, revision, comments)?; - cmd.args(&extra_args); - - let (status, stderr, stdout) = self.run_command(&mut cmd); - - let mode = config.mode.maybe_override(comments, revision)?; - - match *mode { - Mode::Run { .. } if Mode::Pass.ok(status).is_ok() => { - return run_test_binary(mode, path, revision, comments, cmd, config) - } - Mode::Panic | Mode::Yolo { .. } => {} - Mode::Run { .. } | Mode::Pass | Mode::Fail { .. } => { - if status.code() == Some(101) { - let stderr = String::from_utf8_lossy(&stderr); - let stdout = String::from_utf8_lossy(&stdout); - return Err(Errored { - command: cmd, - errors: vec![Error::Bug(format!( - "test panicked: stderr:\n{stderr}\nstdout:\n{stdout}", - ))], - stderr: vec![], - stdout: vec![], - }); - } - } - } - check_test_result( - cmd, *mode, path, config, revision, comments, status, &stdout, &stderr, - )?; - run_rustfix( - &stderr, &stdout, path, comments, revision, config, *mode, extra_args, - )?; - Ok(TestOk::Ok) - } - - /// Run a command, and if it takes more than 100ms, start appending the last stderr/stdout - /// line to the current status spinner. - fn run_command(&self, cmd: &mut Command) -> (ExitStatus, Vec<u8>, Vec<u8>) { - let output = cmd.output().unwrap_or_else(|err| { - panic!( - "could not spawn `{:?}` as a process: {err}", - cmd.get_program() - ) - }); - - (output.status, output.stderr, output.stdout) - } -} - -fn build_aux_files( - aux_dir: &Path, - comments: &Comments, - revision: &str, - config: &Config, - build_manager: &BuildManager<'_>, -) -> Result<Vec<OsString>, Errored> { - let mut extra_args = vec![]; - for rev in comments.for_revision(revision) { - for aux in &rev.aux_builds { - let line = aux.line(); - let aux = &**aux; - let aux_file = if aux.starts_with("..") { - aux_dir.parent().unwrap().join(aux) - } else { - aux_dir.join(aux) - }; - extra_args.extend( - build_manager - .build( - Build::Aux { - aux_file: strip_path_prefix( - &aux_file.canonicalize().map_err(|err| Errored { - command: Command::new(format!( - "canonicalizing path `{}`", - aux_file.display() - )), - errors: vec![], - stderr: err.to_string().into_bytes(), - stdout: vec![], - })?, - &std::env::current_dir().unwrap(), - ) - .collect(), - }, - config, - ) - .map_err( - |Errored { - command, - errors, - stderr, - stdout, - }| Errored { - command, - errors: vec![Error::Aux { - path: aux_file, - errors, - line, - }], - stderr, - stdout, - }, - )?, - ); - } - } - Ok(extra_args) -} - -fn run_test_binary( - mode: MaybeSpanned<Mode>, - path: &Path, - revision: &str, - comments: &Comments, - mut cmd: Command, - config: &Config, -) -> TestResult { - cmd.arg("--print").arg("file-names"); - let output = cmd.output().unwrap(); - assert!(output.status.success()); - - let mut files = output.stdout.lines(); - let file = files.next().unwrap(); - assert_eq!(files.next(), None); - let file = std::str::from_utf8(file).unwrap(); - let exe = config.out_dir.join(file); - let mut exe = Command::new(exe); - let output = exe.output().unwrap(); - - let mut errors = vec![]; - - check_test_output( - path, - &mut errors, - revision, - config, - comments, - &output.stdout, - &output.stderr, - ); - - errors.extend(mode.ok(output.status).err()); - if errors.is_empty() { - Ok(TestOk::Ok) - } else { - Err(Errored { - command: exe, - errors, - stderr: vec![], - stdout: vec![], - }) - } -} - -fn run_rustfix( - stderr: &[u8], - stdout: &[u8], - path: &Path, - comments: &Comments, - revision: &str, - config: &Config, - mode: Mode, - extra_args: Vec<OsString>, -) -> Result<(), Errored> { - let no_run_rustfix = - comments.find_one_for_revision(revision, "`no-rustfix` annotations", |r| r.no_rustfix)?; - - let global_rustfix = match mode { - Mode::Pass | Mode::Run { .. } | Mode::Panic => RustfixMode::Disabled, - Mode::Fail { rustfix, .. } | Mode::Yolo { rustfix } => rustfix, - }; - - let fixed_code = (no_run_rustfix.is_none() && global_rustfix.enabled()) - .then_some(()) - .and_then(|()| { - let suggestions = std::str::from_utf8(stderr) - .unwrap() - .lines() - .flat_map(|line| { - if !line.starts_with('{') { - return vec![]; - } - rustfix::get_suggestions_from_json( - line, - &HashSet::new(), - if global_rustfix == RustfixMode::Everything { - rustfix::Filter::Everything - } else { - rustfix::Filter::MachineApplicableOnly - }, - ) - .unwrap_or_else(|err| { - panic!("could not deserialize diagnostics json for rustfix {err}:{line}") - }) - }) - .collect::<Vec<_>>(); - if suggestions.is_empty() { - None - } else { - Some(rustfix::apply_suggestions( - &std::fs::read_to_string(path).unwrap(), - &suggestions, - )) - } - }) - .transpose() - .map_err(|err| Errored { - command: Command::new(format!("rustfix {}", path.display())), - errors: vec![Error::Rustfix(err)], - stderr: stderr.into(), - stdout: stdout.into(), - })?; - - let edition = comments.edition(revision, config)?; - let edition = edition - .map(|mwl| { - let line = mwl.span().unwrap_or(Span::INVALID); - Spanned::new(mwl.into_inner(), line) - }) - .into(); - let rustfix_comments = Comments { - revisions: None, - revisioned: std::iter::once(( - vec![], - Revisioned { - span: Span::INVALID, - ignore: vec![], - only: vec![], - stderr_per_bitwidth: false, - compile_flags: comments - .for_revision(revision) - .flat_map(|r| r.compile_flags.iter().cloned()) - .collect(), - env_vars: comments - .for_revision(revision) - .flat_map(|r| r.env_vars.iter().cloned()) - .collect(), - normalize_stderr: vec![], - normalize_stdout: vec![], - error_in_other_files: vec![], - error_matches: vec![], - require_annotations_for_level: Default::default(), - aux_builds: comments - .for_revision(revision) - .flat_map(|r| r.aux_builds.iter().cloned()) - .collect(), - edition, - mode: OptWithLine::new(Mode::Pass, Span::INVALID), - no_rustfix: OptWithLine::new((), Span::INVALID), - needs_asm_support: false, - }, - )) - .collect(), - }; - - let run = fixed_code.is_some(); - let mut errors = vec![]; - let rustfix_path = check_output( - // Always check for `.fixed` files, even if there were reasons not to run rustfix. - // We don't want to leave around stray `.fixed` files - fixed_code.unwrap_or_default().as_bytes(), - path, - &mut errors, - "fixed", - &Filter::default(), - config, - &rustfix_comments, - revision, - ); - if !errors.is_empty() { - return Err(Errored { - command: Command::new(format!("checking {}", path.display())), - errors, - stderr: vec![], - stdout: vec![], - }); - } - - if !run { - return Ok(()); - } - - let mut cmd = build_command(&rustfix_path, config, revision, &rustfix_comments)?; - cmd.args(extra_args); - // picking the crate name from the file name is problematic when `.revision_name` is inserted - cmd.arg("--crate-name").arg( - path.file_stem() - .unwrap() - .to_str() - .unwrap() - .replace('-', "_"), - ); - let output = cmd.output().unwrap(); - if output.status.success() { - Ok(()) - } else { - Err(Errored { - command: cmd, - errors: vec![Error::Command { - kind: "rustfix".into(), - status: output.status, - }], - stderr: rustc_stderr::process(&rustfix_path, &output.stderr).rendered, - stdout: output.stdout, - }) - } -} - -fn revised(revision: &str, extension: &str) -> String { - if revision.is_empty() { - extension.to_string() - } else { - format!("{revision}.{extension}") - } -} - -fn check_test_result( - command: Command, - mode: Mode, - path: &Path, - config: &Config, - revision: &str, - comments: &Comments, - status: ExitStatus, - stdout: &[u8], - stderr: &[u8], -) -> Result<(), Errored> { - let mut errors = vec![]; - errors.extend(mode.ok(status).err()); - // Always remove annotation comments from stderr. - let diagnostics = rustc_stderr::process(path, stderr); - check_test_output( - path, - &mut errors, - revision, - config, - comments, - stdout, - &diagnostics.rendered, - ); - // Check error annotations in the source against output - check_annotations( - diagnostics.messages, - diagnostics.messages_from_unknown_file_or_line, - path, - &mut errors, - config, - revision, - comments, - )?; - if errors.is_empty() { - Ok(()) - } else { - Err(Errored { - command, - errors, - stderr: diagnostics.rendered, - stdout: stdout.into(), - }) - } -} - -fn check_test_output( - path: &Path, - errors: &mut Vec<Error>, - revision: &str, - config: &Config, - comments: &Comments, - stdout: &[u8], - stderr: &[u8], -) { - // Check output files (if any) - // Check output files against actual output - check_output( - stderr, - path, - errors, - "stderr", - &config.stderr_filters, - config, - comments, - revision, - ); - check_output( - stdout, - path, - errors, - "stdout", - &config.stdout_filters, - config, - comments, - revision, - ); -} - -fn check_annotations( - mut messages: Vec<Vec<Message>>, - mut messages_from_unknown_file_or_line: Vec<Message>, - path: &Path, - errors: &mut Errors, - config: &Config, - revision: &str, - comments: &Comments, -) -> Result<(), Errored> { - let error_patterns = comments - .for_revision(revision) - .flat_map(|r| r.error_in_other_files.iter()); - - let mut seen_error_match = false; - for error_pattern in error_patterns { - seen_error_match = true; - // first check the diagnostics messages outside of our file. We check this first, so that - // you can mix in-file annotations with //@error-in-other-file annotations, even if there is overlap - // in the messages. - if let Some(i) = messages_from_unknown_file_or_line - .iter() - .position(|msg| error_pattern.matches(&msg.message)) - { - messages_from_unknown_file_or_line.remove(i); - } else { - errors.push(Error::PatternNotFound(error_pattern.clone())); - } - } - - // The order on `Level` is such that `Error` is the highest level. - // We will ensure that *all* diagnostics of level at least `lowest_annotation_level` - // are matched. - let mut lowest_annotation_level = Level::Error; - for &ErrorMatch { - ref pattern, - level, - line, - } in comments - .for_revision(revision) - .flat_map(|r| r.error_matches.iter()) - { - seen_error_match = true; - // If we found a diagnostic with a level annotation, make sure that all - // diagnostics of that level have annotations, even if we don't end up finding a matching diagnostic - // for this pattern. - if lowest_annotation_level > level { - lowest_annotation_level = level; - } - - if let Some(msgs) = messages.get_mut(line.get()) { - let found = msgs - .iter() - .position(|msg| pattern.matches(&msg.message) && msg.level == level); - if let Some(found) = found { - msgs.remove(found); - continue; - } - } - - errors.push(Error::PatternNotFound(pattern.clone())); - } - - let required_annotation_level = comments.find_one_for_revision( - revision, - "`require_annotations_for_level` annotations", - |r| r.require_annotations_for_level, - )?; - - let required_annotation_level = - required_annotation_level.map_or(lowest_annotation_level, |l| *l); - let filter = |mut msgs: Vec<Message>| -> Vec<_> { - msgs.retain(|msg| msg.level >= required_annotation_level); - msgs - }; - - let mode = config.mode.maybe_override(comments, revision)?; - - if !matches!(config.mode, Mode::Yolo { .. }) { - let messages_from_unknown_file_or_line = filter(messages_from_unknown_file_or_line); - if !messages_from_unknown_file_or_line.is_empty() { - errors.push(Error::ErrorsWithoutPattern { - path: None, - msgs: messages_from_unknown_file_or_line, - }); - } - - for (line, msgs) in messages.into_iter().enumerate() { - let msgs = filter(msgs); - if !msgs.is_empty() { - let line = NonZeroUsize::new(line).expect("line 0 is always empty"); - errors.push(Error::ErrorsWithoutPattern { - path: Some(Spanned::new( - path.to_path_buf(), - Span { - line_start: line, - ..Span::INVALID - }, - )), - msgs, - }); - } - } - } - - match (*mode, seen_error_match) { - (Mode::Pass, true) | (Mode::Panic, true) => errors.push(Error::PatternFoundInPassTest), - ( - Mode::Fail { - require_patterns: true, - .. - }, - false, - ) => errors.push(Error::NoPatternsFound), - _ => {} - } - Ok(()) -} - -fn check_output( - output: &[u8], - path: &Path, - errors: &mut Errors, - kind: &'static str, - filters: &Filter, - config: &Config, - comments: &Comments, - revision: &str, -) -> PathBuf { - let target = config.target.as_ref().unwrap(); - let output = normalize(path, output, filters, comments, revision, kind); - let path = output_path(path, comments, revised(revision, kind), target, revision); - match &config.output_conflict_handling { - OutputConflictHandling::Error(bless_command) => { - let expected_output = std::fs::read(&path).unwrap_or_default(); - if output != expected_output { - errors.push(Error::OutputDiffers { - path: path.clone(), - actual: output.clone(), - expected: expected_output, - bless_command: bless_command.clone(), - }); - } - } - OutputConflictHandling::Bless => { - if output.is_empty() { - let _ = std::fs::remove_file(&path); - } else { - std::fs::write(&path, &output).unwrap(); - } - } - OutputConflictHandling::Ignore => {} - } - path -} - -fn output_path( - path: &Path, - comments: &Comments, - kind: String, - target: &str, - revision: &str, -) -> PathBuf { - if comments - .for_revision(revision) - .any(|r| r.stderr_per_bitwidth) - { - return path.with_extension(format!("{}bit.{kind}", get_pointer_width(target))); - } - path.with_extension(kind) -} - -fn test_condition(condition: &Condition, config: &Config) -> bool { - let target = config.target.as_ref().unwrap(); - match condition { - Condition::Bitwidth(bits) => get_pointer_width(target) == *bits, - Condition::Target(t) => target.contains(t), - Condition::Host(t) => config.host.as_ref().unwrap().contains(t), - Condition::OnHost => target == config.host.as_ref().unwrap(), - } -} - -impl dyn TestStatus { - /// Returns whether according to the in-file conditions, this file should be run. - fn test_file_conditions(&self, comments: &Comments, config: &Config) -> bool { - let revision = self.revision(); - if comments - .for_revision(revision) - .flat_map(|r| r.ignore.iter()) - .any(|c| test_condition(c, config)) - { - return false; - } - if comments - .for_revision(revision) - .any(|r| r.needs_asm_support && !config.has_asm_support()) - { - return false; - } - comments - .for_revision(revision) - .flat_map(|r| r.only.iter()) - .all(|c| test_condition(c, config)) - } -} - -// Taken 1:1 from compiletest-rs -fn get_pointer_width(triple: &str) -> u8 { - if (triple.contains("64") && !triple.ends_with("gnux32") && !triple.ends_with("gnu_ilp32")) - || triple.starts_with("s390x") - { - 64 - } else if triple.starts_with("avr") { - 16 - } else { - 32 - } -} - -fn normalize( - path: &Path, - text: &[u8], - filters: &Filter, - comments: &Comments, - revision: &str, - kind: &'static str, -) -> Vec<u8> { - // Useless paths - let path_filter = (Match::from(path.parent().unwrap()), b"$DIR" as &[u8]); - let filters = filters.iter().chain(std::iter::once(&path_filter)); - let mut text = text.to_owned(); - if let Some(lib_path) = option_env!("RUSTC_LIB_PATH") { - text = text.replace(lib_path, "RUSTLIB"); - } - - for (rule, replacement) in filters { - text = rule.replace_all(&text, replacement).into_owned(); - } - - for (from, to) in comments.for_revision(revision).flat_map(|r| match kind { - "fixed" => &[] as &[_], - "stderr" => &r.normalize_stderr, - "stdout" => &r.normalize_stdout, - _ => unreachable!(), - }) { - text = from.replace_all(&text, to).into_owned(); - } - text -} -/// Remove the common prefix of this path and the `root_dir`. -fn strip_path_prefix<'a>(path: &'a Path, prefix: &Path) -> impl Iterator<Item = Component<'a>> { - let mut components = path.components(); - for c in prefix.components() { - // Windows has some funky paths. This is probably wrong, but works well in practice. - let deverbatimize = |c| match c { - Component::Prefix(prefix) => Err(match prefix.kind() { - Prefix::VerbatimUNC(a, b) => Prefix::UNC(a, b), - Prefix::VerbatimDisk(d) => Prefix::Disk(d), - other => other, - }), - c => Ok(c), - }; - let c2 = components.next(); - if Some(deverbatimize(c)) == c2.map(deverbatimize) { - continue; - } - return c2.into_iter().chain(components); - } - None.into_iter().chain(components) -} diff --git a/vendor/ui_test-0.20.0/src/mode.rs b/vendor/ui_test-0.20.0/src/mode.rs deleted file mode 100644 index ce37adc4c..000000000 --- a/vendor/ui_test-0.20.0/src/mode.rs +++ /dev/null @@ -1,93 +0,0 @@ -use super::Error; -use crate::parser::Comments; -use crate::parser::MaybeSpanned; -use crate::Errored; -use std::fmt::Display; -use std::process::ExitStatus; - -/// When to run rustfix on tests -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum RustfixMode { - /// Do not run rustfix on the test - Disabled, - /// Apply only `MachineApplicable` suggestions emitted by the test - MachineApplicable, - /// Apply all suggestions emitted by the test - Everything, -} - -impl RustfixMode { - pub(crate) fn enabled(self) -> bool { - self != RustfixMode::Disabled - } -} - -#[derive(Copy, Clone, Debug)] -/// Decides what is expected of each test's exit status. -pub enum Mode { - /// The test passes a full execution of the rustc driver - Pass, - /// The test produces an executable binary that can get executed on the host - Run { - /// The expected exit code - exit_code: i32, - }, - /// The rustc driver panicked - Panic, - /// The rustc driver emitted an error - Fail { - /// Whether failing tests must have error patterns. Set to false if you just care about .stderr output. - require_patterns: bool, - /// When to run rustfix on the test - rustfix: RustfixMode, - }, - /// Run the tests, but always pass them as long as all annotations are satisfied and stderr files match. - Yolo { - /// When to run rustfix on the test - rustfix: RustfixMode, - }, -} - -impl Mode { - pub(crate) fn ok(self, status: ExitStatus) -> Result<(), Error> { - let expected = match self { - Mode::Run { exit_code } => exit_code, - Mode::Pass => 0, - Mode::Panic => 101, - Mode::Fail { .. } => 1, - Mode::Yolo { .. } => return Ok(()), - }; - if status.code() == Some(expected) { - Ok(()) - } else { - Err(Error::ExitStatus { - mode: self, - status, - expected, - }) - } - } - pub(crate) fn maybe_override( - self, - comments: &Comments, - revision: &str, - ) -> Result<MaybeSpanned<Self>, Errored> { - let mode = comments.find_one_for_revision(revision, "mode changes", |r| r.mode)?; - Ok(mode.map_or(MaybeSpanned::new_config(self), Into::into)) - } -} - -impl Display for Mode { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - Mode::Run { exit_code } => write!(f, "run({exit_code})"), - Mode::Pass => write!(f, "pass"), - Mode::Panic => write!(f, "panic"), - Mode::Fail { - require_patterns: _, - rustfix: _, - } => write!(f, "fail"), - Mode::Yolo { rustfix: _ } => write!(f, "yolo"), - } - } -} diff --git a/vendor/ui_test-0.20.0/src/parser.rs b/vendor/ui_test-0.20.0/src/parser.rs deleted file mode 100644 index 62ee5a1d1..000000000 --- a/vendor/ui_test-0.20.0/src/parser.rs +++ /dev/null @@ -1,822 +0,0 @@ -use std::{ - collections::HashMap, - num::NonZeroUsize, - path::{Path, PathBuf}, - process::Command, -}; - -use bstr::{ByteSlice, Utf8Error}; -use regex::bytes::Regex; - -use crate::{ - rustc_stderr::{Level, Span}, - Error, Errored, Mode, -}; - -use color_eyre::eyre::{Context, Result}; - -pub(crate) use spanned::*; - -mod spanned; -#[cfg(test)] -mod tests; - -/// This crate supports various magic comments that get parsed as file-specific -/// configuration values. This struct parses them all in one go and then they -/// get processed by their respective use sites. -#[derive(Default, Debug)] -pub(crate) struct Comments { - /// List of revision names to execute. Can only be specified once - pub revisions: Option<Vec<String>>, - /// Comments that are only available under specific revisions. - /// The defaults are in key `vec![]` - pub revisioned: HashMap<Vec<String>, Revisioned>, -} - -impl Comments { - /// Check that a comment isn't specified twice across multiple differently revisioned statements. - /// e.g. `//@[foo, bar] error-in-other-file: bop` and `//@[foo, baz] error-in-other-file boop` would end up - /// specifying two error patterns that are available in revision `foo`. - pub fn find_one_for_revision<'a, T: 'a>( - &'a self, - revision: &'a str, - kind: &str, - f: impl Fn(&'a Revisioned) -> OptWithLine<T>, - ) -> Result<OptWithLine<T>, Errored> { - let mut result = None; - let mut errors = vec![]; - for rev in self.for_revision(revision) { - if let Some(found) = f(rev).into_inner() { - if result.is_some() { - errors.push(found.line()); - } else { - result = found.into(); - } - } - } - if errors.is_empty() { - Ok(result.into()) - } else { - Err(Errored { - command: Command::new(format!("<finding flags for revision `{revision}`>")), - errors: vec![Error::MultipleRevisionsWithResults { - kind: kind.to_string(), - lines: errors, - }], - stderr: vec![], - stdout: vec![], - }) - } - } - - /// Returns an iterator over all revisioned comments that match the revision. - pub fn for_revision<'a>(&'a self, revision: &'a str) -> impl Iterator<Item = &'a Revisioned> { - self.revisioned.iter().filter_map(move |(k, v)| { - if k.is_empty() || k.iter().any(|rev| rev == revision) { - Some(v) - } else { - None - } - }) - } - - pub(crate) fn edition( - &self, - revision: &str, - config: &crate::Config, - ) -> Result<Option<MaybeSpanned<String>>, Errored> { - let edition = - self.find_one_for_revision(revision, "`edition` annotations", |r| r.edition.clone())?; - let edition = edition - .into_inner() - .map(MaybeSpanned::from) - .or(config.edition.clone().map(MaybeSpanned::new_config)); - Ok(edition) - } -} - -#[derive(Debug)] -/// Comments that can be filtered for specific revisions. -pub(crate) struct Revisioned { - /// The character range in which this revisioned item was first added. - /// Used for reporting errors on unknown revisions. - pub span: Span, - /// Don't run this test if any of these filters apply - pub ignore: Vec<Condition>, - /// Only run this test if all of these filters apply - pub only: Vec<Condition>, - /// Generate one .stderr file per bit width, by prepending with `.64bit` and similar - pub stderr_per_bitwidth: bool, - /// Additional flags to pass to the executable - pub compile_flags: Vec<String>, - /// Additional env vars to set for the executable - pub env_vars: Vec<(String, String)>, - /// Normalizations to apply to the stderr output before emitting it to disk - pub normalize_stderr: Vec<(Regex, Vec<u8>)>, - /// Normalizations to apply to the stdout output before emitting it to disk - pub normalize_stdout: Vec<(Regex, Vec<u8>)>, - /// Arbitrary patterns to look for in the stderr. - /// The error must be from another file, as errors from the current file must be - /// checked via `error_matches`. - pub error_in_other_files: Vec<Spanned<Pattern>>, - pub error_matches: Vec<ErrorMatch>, - /// Ignore diagnostics below this level. - /// `None` means pick the lowest level from the `error_pattern`s. - pub require_annotations_for_level: OptWithLine<Level>, - pub aux_builds: Vec<Spanned<PathBuf>>, - pub edition: OptWithLine<String>, - /// Overwrites the mode from `Config`. - pub mode: OptWithLine<Mode>, - pub needs_asm_support: bool, - /// Don't run [`rustfix`] for this test - pub no_rustfix: OptWithLine<()>, -} - -#[derive(Debug)] -struct CommentParser<T> { - /// The comments being built. - comments: T, - /// Any errors that ocurred during comment parsing. - errors: Vec<Error>, - /// The available commands and their parsing logic - commands: HashMap<&'static str, CommandParserFunc>, -} - -type CommandParserFunc = fn(&mut CommentParser<&mut Revisioned>, args: Spanned<&str>, span: Span); - -impl<T> std::ops::Deref for CommentParser<T> { - type Target = T; - - fn deref(&self) -> &Self::Target { - &self.comments - } -} - -impl<T> std::ops::DerefMut for CommentParser<T> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.comments - } -} - -/// The conditions used for "ignore" and "only" filters. -#[derive(Debug)] -pub(crate) enum Condition { - /// The given string must appear in the host triple. - Host(String), - /// The given string must appear in the target triple. - Target(String), - /// Tests that the bitwidth is the given one. - Bitwidth(u8), - /// Tests that the target is the host. - OnHost, -} - -#[derive(Debug, Clone)] -/// An error pattern parsed from a `//~` comment. -pub enum Pattern { - SubString(String), - Regex(Regex), -} - -#[derive(Debug)] -pub(crate) struct ErrorMatch { - pub pattern: Spanned<Pattern>, - pub level: Level, - /// The line this pattern is expecting to find a message in. - pub line: NonZeroUsize, -} - -impl Condition { - fn parse(c: &str) -> std::result::Result<Self, String> { - if c == "on-host" { - Ok(Condition::OnHost) - } else if let Some(bits) = c.strip_suffix("bit") { - let bits: u8 = bits.parse().map_err(|_err| { - format!("invalid ignore/only filter ending in 'bit': {c:?} is not a valid bitwdith") - })?; - Ok(Condition::Bitwidth(bits)) - } else if let Some(triple_substr) = c.strip_prefix("target-") { - Ok(Condition::Target(triple_substr.to_owned())) - } else if let Some(triple_substr) = c.strip_prefix("host-") { - Ok(Condition::Host(triple_substr.to_owned())) - } else { - Err(format!( - "`{c}` is not a valid condition, expected `on-host`, /[0-9]+bit/, /host-.*/, or /target-.*/" - )) - } - } -} - -impl Comments { - pub(crate) fn parse_file(path: &Path) -> Result<std::result::Result<Self, Vec<Error>>> { - let content = - std::fs::read(path).wrap_err_with(|| format!("failed to read {}", path.display()))?; - Ok(Self::parse(&content)) - } - - /// Parse comments in `content`. - /// `path` is only used to emit diagnostics if parsing fails. - pub(crate) fn parse( - content: &(impl AsRef<[u8]> + ?Sized), - ) -> std::result::Result<Self, Vec<Error>> { - let mut parser = CommentParser { - comments: Comments::default(), - errors: vec![], - commands: CommentParser::<_>::commands(), - }; - - let mut fallthrough_to = None; // The line that a `|` will refer to. - for (l, line) in content.as_ref().lines().enumerate() { - let l = NonZeroUsize::new(l + 1).unwrap(); // enumerate starts at 0, but line numbers start at 1 - let span = Span { - line_start: l, - line_end: l, - column_start: NonZeroUsize::new(1).unwrap(), - column_end: NonZeroUsize::new(line.chars().count() + 1).unwrap(), - }; - match parser.parse_checked_line(&mut fallthrough_to, Spanned::new(line, span)) { - Ok(()) => {} - Err(e) => parser.error(span, format!("Comment is not utf8: {e:?}")), - } - } - if let Some(revisions) = &parser.comments.revisions { - for (key, revisioned) in &parser.comments.revisioned { - for rev in key { - if !revisions.contains(rev) { - parser.errors.push(Error::InvalidComment { - msg: format!("the revision `{rev}` is not known"), - span: revisioned.span, - }) - } - } - } - } else { - for (key, revisioned) in &parser.comments.revisioned { - if !key.is_empty() { - parser.errors.push(Error::InvalidComment { - msg: "there are no revisions in this test".into(), - span: revisioned.span, - }) - } - } - } - if parser.errors.is_empty() { - Ok(parser.comments) - } else { - Err(parser.errors) - } - } -} - -impl CommentParser<Comments> { - fn parse_checked_line( - &mut self, - fallthrough_to: &mut Option<NonZeroUsize>, - line: Spanned<&[u8]>, - ) -> std::result::Result<(), Utf8Error> { - if let Some(command) = line.strip_prefix(b"//@") { - self.parse_command(command.to_str()?.trim()) - } else if let Some((_, pattern)) = line.split_once_str("//~") { - let (revisions, pattern) = self.parse_revisions(pattern.to_str()?); - self.revisioned(revisions, |this| { - this.parse_pattern(pattern, fallthrough_to) - }) - } else { - *fallthrough_to = None; - for pos in line.find_iter("//") { - let (_, rest) = line.to_str()?.split_at(pos + 2); - for rest in std::iter::once(rest).chain(rest.strip_prefix(" ")) { - if let Some('@' | '~' | '[' | ']' | '^' | '|') = rest.chars().next() { - self.error( - rest.span(), - format!( - "comment looks suspiciously like a test suite command: `{}`\n\ - All `//@` test suite commands must be at the start of the line.\n\ - The `//` must be directly followed by `@` or `~`.", - *rest, - ), - ); - } else { - let mut parser = Self { - errors: vec![], - comments: Comments::default(), - commands: std::mem::take(&mut self.commands), - }; - parser.parse_command(rest); - if parser.errors.is_empty() { - self.error( - rest.span(), - "a compiletest-rs style comment was detected.\n\ - Please use text that could not also be interpreted as a command,\n\ - and prefix all actual commands with `//@`", - ); - } - self.commands = parser.commands; - } - } - } - } - Ok(()) - } -} - -impl<CommentsType> CommentParser<CommentsType> { - fn error(&mut self, span: Span, s: impl Into<String>) { - self.errors.push(Error::InvalidComment { - msg: s.into(), - span, - }); - } - - fn check(&mut self, span: Span, cond: bool, s: impl Into<String>) { - if !cond { - self.error(span, s); - } - } - - fn check_some<T>(&mut self, span: Span, opt: Option<T>, s: impl Into<String>) -> Option<T> { - self.check(span, opt.is_some(), s); - opt - } -} - -impl CommentParser<Comments> { - fn parse_command(&mut self, command: Spanned<&str>) { - let (revisions, command) = self.parse_revisions(command); - - // Commands are letters or dashes, grab everything until the first character that is neither of those. - let (command, args) = match command - .char_indices() - .find_map(|(i, c)| (!c.is_alphanumeric() && c != '-' && c != '_').then_some(i)) - { - None => (command, Spanned::new("", command.span().shrink_to_end())), - Some(i) => { - let (command, args) = command.split_at(i); - // Commands are separated from their arguments by ':' or ' ' - let next = args - .chars() - .next() - .expect("the `position` above guarantees that there is at least one char"); - self.check( - args.span().shrink_to_start(), - next == ':', - "test command must be followed by `:` (or end the line)", - ); - (command, args.split_at(next.len_utf8()).1.trim()) - } - }; - - if *command == "revisions" { - self.check( - revisions.span(), - revisions.is_empty(), - "revisions cannot be declared under a revision", - ); - self.check( - revisions.span(), - self.revisions.is_none(), - "cannot specify `revisions` twice", - ); - self.revisions = Some(args.split_whitespace().map(|s| s.to_string()).collect()); - return; - } - self.revisioned(revisions, |this| this.parse_command(command, args)); - } - - fn revisioned( - &mut self, - revisions: Spanned<Vec<String>>, - f: impl FnOnce(&mut CommentParser<&mut Revisioned>), - ) { - let span = revisions.span(); - let revisions = revisions.into_inner(); - let mut this = CommentParser { - errors: std::mem::take(&mut self.errors), - commands: std::mem::take(&mut self.commands), - comments: self - .revisioned - .entry(revisions) - .or_insert_with(|| Revisioned { - span, - ignore: Default::default(), - only: Default::default(), - stderr_per_bitwidth: Default::default(), - compile_flags: Default::default(), - env_vars: Default::default(), - normalize_stderr: Default::default(), - normalize_stdout: Default::default(), - error_in_other_files: Default::default(), - error_matches: Default::default(), - require_annotations_for_level: Default::default(), - aux_builds: Default::default(), - edition: Default::default(), - mode: Default::default(), - needs_asm_support: Default::default(), - no_rustfix: Default::default(), - }), - }; - f(&mut this); - let CommentParser { - errors, commands, .. - } = this; - self.commands = commands; - self.errors = errors; - } -} - -impl CommentParser<&mut Revisioned> { - fn parse_normalize_test( - &mut self, - args: Spanned<&str>, - mode: &str, - ) -> Option<(Regex, Vec<u8>)> { - let (from, rest) = self.parse_str(args); - - let to = match rest.strip_prefix("->") { - Some(v) => v, - None => { - self.error( - rest.span(), - format!( - "normalize-{mode}-test needs a pattern and replacement separated by `->`" - ), - ); - return None; - } - } - .trim_start(); - let (to, rest) = self.parse_str(to); - - self.check( - rest.span(), - rest.is_empty(), - "trailing text after pattern replacement", - ); - - let regex = self.parse_regex(from)?; - Some((regex, to.as_bytes().to_owned())) - } - - fn commands() -> HashMap<&'static str, CommandParserFunc> { - let mut commands = HashMap::<_, CommandParserFunc>::new(); - macro_rules! commands { - ($($name:expr => ($this:ident, $args:ident, $span:ident)$block:block)*) => { - $(commands.insert($name, |$this, $args, $span| { - $block - });)* - }; - } - commands! { - "compile-flags" => (this, args, _span){ - if let Some(parsed) = comma::parse_command(*args) { - this.compile_flags.extend(parsed); - } else { - this.error(args.span(), format!("`{}` contains an unclosed quotation mark", *args)); - } - } - "rustc-env" => (this, args, _span){ - for env in args.split_whitespace() { - if let Some((k, v)) = this.check_some( - args.span(), - env.split_once('='), - "environment variables must be key/value pairs separated by a `=`", - ) { - this.env_vars.push((k.to_string(), v.to_string())); - } - } - } - "normalize-stderr-test" => (this, args, _span){ - if let Some(res) = this.parse_normalize_test(args, "stderr") { - this.normalize_stderr.push(res) - } - } - "normalize-stdout-test" => (this, args, _span){ - if let Some(res) = this.parse_normalize_test(args, "stdout") { - this.normalize_stdout.push(res) - } - } - "error-pattern" => (this, _args, span){ - this.error(span, "`error-pattern` has been renamed to `error-in-other-file`"); - } - "error-in-other-file" => (this, args, _span){ - let args = args.trim(); - let pat = this.parse_error_pattern(args); - this.error_in_other_files.push(pat); - } - "stderr-per-bitwidth" => (this, _args, span){ - // args are ignored (can be used as comment) - this.check( - span, - !this.stderr_per_bitwidth, - "cannot specify `stderr-per-bitwidth` twice", - ); - this.stderr_per_bitwidth = true; - } - "run-rustfix" => (this, _args, span){ - this.error(span, "rustfix is now ran by default when applicable suggestions are found"); - } - "no-rustfix" => (this, _args, span){ - // args are ignored (can be used as comment) - let prev = this.no_rustfix.set((), span); - this.check( - span, - prev.is_none(), - "cannot specify `no-rustfix` twice", - ); - } - "needs-asm-support" => (this, _args, span){ - // args are ignored (can be used as comment) - this.check( - span, - !this.needs_asm_support, - "cannot specify `needs-asm-support` twice", - ); - this.needs_asm_support = true; - } - "aux-build" => (this, args, _span){ - let name = match args.split_once(":") { - Some((name, rest)) => { - this.error(rest.span(), "proc macros are now auto-detected, you can remove the `:proc-macro` after the file name"); - name - }, - None => args, - }; - this.aux_builds.push(name.map(Into::into)); - } - "edition" => (this, args, span){ - let prev = this.edition.set((*args).into(), args.span()); - this.check(span, prev.is_none(), "cannot specify `edition` twice"); - } - "check-pass" => (this, _args, span){ - let prev = this.mode.set(Mode::Pass, span); - // args are ignored (can be used as comment) - this.check( - span, - prev.is_none(), - "cannot specify test mode changes twice", - ); - } - "run" => (this, args, span){ - this.check( - span, - this.mode.is_none(), - "cannot specify test mode changes twice", - ); - let mut set = |exit_code| this.mode.set(Mode::Run { exit_code }, args.span()); - if args.is_empty() { - set(0); - } else { - match args.parse() { - Ok(exit_code) => {set(exit_code);}, - Err(err) => this.error(args.span(), err.to_string()), - } - } - } - "require-annotations-for-level" => (this, args, span){ - let args = args.trim(); - let prev = match args.parse() { - Ok(it) => this.require_annotations_for_level.set(it, args.span()), - Err(msg) => { - this.error(args.span(), msg); - None - }, - }; - - this.check( - span, - prev.is_none(), - "cannot specify `require-annotations-for-level` twice", - ); - } - } - commands - } - - fn parse_command(&mut self, command: Spanned<&str>, args: Spanned<&str>) { - if let Some(command_handler) = self.commands.get(*command) { - command_handler(self, args, command.span()); - } else if let Some(s) = command.strip_prefix("ignore-") { - // args are ignored (can be used as comment) - match Condition::parse(*s) { - Ok(cond) => self.ignore.push(cond), - Err(msg) => self.error(s.span(), msg), - } - } else if let Some(s) = command.strip_prefix("only-") { - // args are ignored (can be used as comment) - match Condition::parse(*s) { - Ok(cond) => self.only.push(cond), - Err(msg) => self.error(s.span(), msg), - } - } else { - let best_match = self - .commands - .keys() - .min_by_key(|key| levenshtein::levenshtein(key, *command)) - .unwrap(); - self.error( - command.span(), - format!( - "`{}` is not a command known to `ui_test`, did you mean `{best_match}`?", - *command - ), - ); - } - } -} - -impl<CommentsType> CommentParser<CommentsType> { - fn parse_regex(&mut self, regex: Spanned<&str>) -> Option<Regex> { - match Regex::new(*regex) { - Ok(regex) => Some(regex), - Err(err) => { - self.error(regex.span(), format!("invalid regex: {err:?}")); - None - } - } - } - - /// Parses a string literal. `s` has to start with `"`; everything until the next `"` is - /// returned in the first component. `\` can be used to escape arbitrary character. - /// Second return component is the rest of the string with leading whitespace removed. - fn parse_str<'a>(&mut self, s: Spanned<&'a str>) -> (Spanned<&'a str>, Spanned<&'a str>) { - match s.strip_prefix("\"") { - Some(s) => { - let mut escaped = false; - for (i, c) in s.char_indices() { - if escaped { - // Accept any character as literal after a `\`. - escaped = false; - } else if c == '"' { - let (a, b) = s.split_at(i); - let b = b.split_at(1).1; - return (a, b.trim_start()); - } else { - escaped = c == '\\'; - } - } - self.error(s.span(), format!("no closing quotes found for {}", *s)); - (s, Spanned::new("", s.span())) - } - None => { - if s.is_empty() { - self.error(s.span(), "expected quoted string, but found end of line") - } else { - self.error( - s.span(), - format!("expected `\"`, got `{}`", s.chars().next().unwrap()), - ) - } - (s, Spanned::new("", s.span())) - } - } - } - - // parse something like \[[a-z]+(,[a-z]+)*\] - fn parse_revisions<'a>( - &mut self, - pattern: Spanned<&'a str>, - ) -> (Spanned<Vec<String>>, Spanned<&'a str>) { - match pattern.strip_prefix("[") { - Some(s) => { - // revisions - let end = s.char_indices().find_map(|(i, c)| match c { - ']' => Some(i), - _ => None, - }); - let Some(end) = end else { - self.error(s.span(), "`[` without corresponding `]`"); - return ( - Spanned::new(vec![], pattern.span().shrink_to_start()), - pattern, - ); - }; - let (revision, pattern) = s.split_at(end); - let revisions = revision.split(',').map(|s| s.trim().to_string()).collect(); - ( - Spanned::new(revisions, revision.span()), - // 1.. because `split_at` includes the separator - pattern.split_at(1).1.trim_start(), - ) - } - _ => ( - Spanned::new(vec![], pattern.span().shrink_to_start()), - pattern, - ), - } - } -} - -impl CommentParser<&mut Revisioned> { - // parse something like (\[[a-z]+(,[a-z]+)*\])?(?P<offset>\||[\^]+)? *(?P<level>ERROR|HELP|WARN|NOTE): (?P<text>.*) - fn parse_pattern(&mut self, pattern: Spanned<&str>, fallthrough_to: &mut Option<NonZeroUsize>) { - let (match_line, pattern) = match pattern.chars().next() { - Some('|') => ( - match fallthrough_to { - Some(fallthrough) => *fallthrough, - None => { - self.error(pattern.span(), "`//~|` pattern without preceding line"); - return; - } - }, - pattern.split_at(1).1, - ), - Some('^') => { - let offset = pattern.chars().take_while(|&c| c == '^').count(); - match pattern - .span() - .line_start - .get() - .checked_sub(offset) - .and_then(NonZeroUsize::new) - { - // lines are one-indexed, so a target line of 0 is invalid, but also - // prevented via `NonZeroUsize` - Some(match_line) => (match_line, pattern.split_at(offset).1), - _ => { - self.error(pattern.span(), format!( - "//~^ pattern is trying to refer to {} lines above, but there are only {} lines above", - offset, - pattern.line().get() - 1, - )); - return; - } - } - } - Some(_) => (pattern.span().line_start, pattern), - None => { - self.error(pattern.span(), "no pattern specified"); - return; - } - }; - - let pattern = pattern.trim_start(); - let offset = match pattern.chars().position(|c| !c.is_ascii_alphabetic()) { - Some(offset) => offset, - None => { - self.error(pattern.span(), "pattern without level"); - return; - } - }; - - let (level, pattern) = pattern.split_at(offset); - let level = match (*level).parse() { - Ok(level) => level, - Err(msg) => { - self.error(level.span(), msg); - return; - } - }; - let pattern = match pattern.strip_prefix(":") { - Some(offset) => offset, - None => { - self.error(pattern.span(), "no `:` after level found"); - return; - } - }; - - let pattern = pattern.trim(); - - self.check(pattern.span(), !pattern.is_empty(), "no pattern specified"); - - let pattern = self.parse_error_pattern(pattern); - - *fallthrough_to = Some(match_line); - - self.error_matches.push(ErrorMatch { - pattern, - level, - line: match_line, - }); - } -} - -impl Pattern { - pub(crate) fn matches(&self, message: &str) -> bool { - match self { - Pattern::SubString(s) => message.contains(s), - Pattern::Regex(r) => r.is_match(message.as_bytes()), - } - } -} - -impl<CommentsType> CommentParser<CommentsType> { - fn parse_error_pattern(&mut self, pattern: Spanned<&str>) -> Spanned<Pattern> { - if let Some(regex) = pattern.strip_prefix("/") { - match regex.strip_suffix("/") { - Some(regex) => match self.parse_regex(regex) { - Some(r) => Spanned::new(Pattern::Regex(r), regex.span()), - None => Spanned::new(Pattern::SubString(pattern.to_string()), regex.span()), - }, - None => { - self.error( - regex.span(), - "expected regex pattern due to leading `/`, but found no closing `/`", - ); - Spanned::new(Pattern::SubString(pattern.to_string()), regex.span()) - } - } - } else { - Spanned::new(Pattern::SubString(pattern.to_string()), pattern.span()) - } - } -} diff --git a/vendor/ui_test-0.20.0/src/parser/spanned.rs b/vendor/ui_test-0.20.0/src/parser/spanned.rs deleted file mode 100644 index 8c5f98ecd..000000000 --- a/vendor/ui_test-0.20.0/src/parser/spanned.rs +++ /dev/null @@ -1,264 +0,0 @@ -use std::num::NonZeroUsize; - -use bstr::{ByteSlice, Utf8Error}; - -use crate::rustc_stderr::Span; - -#[derive(Default, Debug, Clone, Copy)] -pub struct MaybeSpanned<T> { - data: T, - span: Option<Span>, -} - -impl<T> std::ops::Deref for MaybeSpanned<T> { - type Target = T; - - fn deref(&self) -> &Self::Target { - &self.data - } -} - -impl<T> MaybeSpanned<T> { - /// Values from the `Config` struct don't have lines. - pub fn new_config(data: T) -> Self { - Self { data, span: None } - } - - pub fn span(&self) -> Option<Span> { - self.span - } - - pub fn into_inner(self) -> T { - self.data - } -} - -impl<T> From<Spanned<T>> for MaybeSpanned<T> { - fn from(value: Spanned<T>) -> Self { - Self { - data: value.data, - span: Some(value.span), - } - } -} - -#[derive(Debug, Clone, Copy)] -pub struct Spanned<T> { - data: T, - span: Span, -} - -impl<T> std::ops::Deref for Spanned<T> { - type Target = T; - - fn deref(&self) -> &Self::Target { - &self.data - } -} - -impl<'a> Spanned<&'a str> { - pub fn strip_prefix(&self, prefix: &str) -> Option<Self> { - let data = self.data.strip_prefix(prefix)?; - let mut span = self.span; - span.column_start = - NonZeroUsize::new(span.column_start.get() + prefix.chars().count()).unwrap(); - Some(Self { span, data }) - } - - pub fn strip_suffix(&self, suffix: &str) -> Option<Self> { - let data = self.data.strip_suffix(suffix)?; - let mut span = self.span; - span.column_end = - NonZeroUsize::new(span.column_end.get() - suffix.chars().count()).unwrap(); - Some(Self { span, data }) - } - - pub fn trim_start(&self) -> Self { - let data = self.data.trim_start(); - let mut span = self.span; - span.column_start = NonZeroUsize::new( - span.column_start.get() + self.data.chars().count() - data.chars().count(), - ) - .unwrap(); - Self { data, span } - } - - pub fn trim_end(&self) -> Self { - let data = self.data.trim_end(); - let mut span = self.span; - span.column_end = NonZeroUsize::new( - span.column_end.get() - (self.data.chars().count() - data.chars().count()), - ) - .unwrap(); - Self { data, span } - } - - pub fn trim(&self) -> Self { - self.trim_start().trim_end() - } - - pub fn split_at(&self, i: usize) -> (Self, Self) { - let (a, b) = self.data.split_at(i); - ( - Self { - data: a, - span: Span { - column_end: NonZeroUsize::new(self.span.column_start.get() + a.chars().count()) - .unwrap(), - ..self.span - }, - }, - Self { - data: b, - span: Span { - column_start: NonZeroUsize::new( - self.span.column_start.get() + a.chars().count(), - ) - .unwrap(), - ..self.span - }, - }, - ) - } - - pub fn split_once(&self, splitter: &str) -> Option<(Self, Self)> { - let (a, b) = self.data.split_once(splitter)?; - Some(( - Self { - data: a, - span: Span { - column_end: NonZeroUsize::new(self.span.column_start.get() + a.chars().count()) - .unwrap(), - ..self.span - }, - }, - Self { - data: b, - span: Span { - column_start: NonZeroUsize::new( - self.span.column_start.get() + a.chars().count() + splitter.chars().count(), - ) - .unwrap(), - ..self.span - }, - }, - )) - } -} - -impl<'a> Spanned<&'a [u8]> { - pub fn strip_prefix(&self, prefix: &[u8]) -> Option<Self> { - let data = self.data.strip_prefix(prefix)?; - let mut span = self.span; - span.column_start = NonZeroUsize::new(span.column_start.get() + prefix.len()).unwrap(); - Some(Self { span, data }) - } - - pub fn split_once_str(&self, splitter: &str) -> Option<(Self, Self)> { - let (a, b) = self.data.split_once_str(splitter)?; - Some(( - Self { - data: a, - span: Span { - column_end: NonZeroUsize::new(self.span.column_start.get() + a.len()).unwrap(), - ..self.span - }, - }, - Self { - data: b, - span: Span { - column_start: NonZeroUsize::new( - self.span.column_start.get() + a.len() + splitter.len(), - ) - .unwrap(), - ..self.span - }, - }, - )) - } - - pub fn to_str(self) -> Result<Spanned<&'a str>, Utf8Error> { - Ok(Spanned { - data: self.data.to_str()?, - span: self.span, - }) - } -} - -impl<T> Spanned<T> { - pub fn new(data: T, span: Span) -> Self { - Self { data, span } - } - - pub fn line(&self) -> NonZeroUsize { - self.span.line_start - } - - pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Spanned<U> { - Spanned { - data: f(self.data), - span: self.span, - } - } - - pub fn into_inner(self) -> T { - self.data - } - - pub fn span(&self) -> Span { - self.span - } -} - -#[derive(Debug, Clone, Copy)] -pub struct OptWithLine<T>(Option<Spanned<T>>); - -impl<T> std::ops::Deref for OptWithLine<T> { - type Target = Option<Spanned<T>>; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl<T> From<Option<Spanned<T>>> for OptWithLine<T> { - fn from(value: Option<Spanned<T>>) -> Self { - Self(value) - } -} - -impl<T> From<Spanned<T>> for OptWithLine<T> { - fn from(value: Spanned<T>) -> Self { - Self(Some(value)) - } -} - -impl<T> Default for OptWithLine<T> { - fn default() -> Self { - Self(Default::default()) - } -} - -impl<T> OptWithLine<T> { - pub fn new(data: T, span: Span) -> Self { - Self(Some(Spanned::new(data, span))) - } - - /// Tries to set the value if not already set. Returns newly passed - /// value in case there was already a value there. - #[must_use] - pub fn set(&mut self, data: T, span: Span) -> Option<Spanned<T>> { - let new = Spanned::new(data, span); - if self.0.is_some() { - Some(new) - } else { - self.0 = Some(new); - None - } - } - - #[must_use] - pub fn into_inner(self) -> Option<Spanned<T>> { - self.0 - } -} diff --git a/vendor/ui_test-0.20.0/src/parser/tests.rs b/vendor/ui_test-0.20.0/src/parser/tests.rs deleted file mode 100644 index f1b3b4cbf..000000000 --- a/vendor/ui_test-0.20.0/src/parser/tests.rs +++ /dev/null @@ -1,139 +0,0 @@ -use crate::{ - parser::{Condition, Pattern}, - Error, -}; - -use super::Comments; - -#[test] -fn parse_simple_comment() { - let s = r" -use std::mem; - -fn main() { - let _x: &i32 = unsafe { mem::transmute(16usize) }; //~ ERROR: encountered a dangling reference (address $HEX is unallocated) -} - "; - let comments = Comments::parse(s).unwrap(); - println!("parsed comments: {:#?}", comments); - assert_eq!(comments.revisioned.len(), 1); - let revisioned = &comments.revisioned[&vec![]]; - assert_eq!(revisioned.error_matches[0].pattern.line().get(), 5); - match &*revisioned.error_matches[0].pattern { - Pattern::SubString(s) => { - assert_eq!( - s, - "encountered a dangling reference (address $HEX is unallocated)" - ) - } - other => panic!("expected substring, got {other:?}"), - } -} - -#[test] -fn parse_missing_level() { - let s = r" -use std::mem; - -fn main() { - let _x: &i32 = unsafe { mem::transmute(16usize) }; //~ encountered a dangling reference (address $HEX is unallocated) -} - "; - let errors = Comments::parse(s).unwrap_err(); - println!("parsed comments: {:#?}", errors); - assert_eq!(errors.len(), 1); - match &errors[0] { - Error::InvalidComment { msg, span } if span.line_start.get() == 5 => { - assert_eq!(msg, "unknown level `encountered`") - } - _ => unreachable!(), - } -} - -#[test] -fn parse_slash_slash_at() { - let s = r" -//@ error-in-other-file: foomp -use std::mem; - - "; - let comments = Comments::parse(s).unwrap(); - println!("parsed comments: {:#?}", comments); - assert_eq!(comments.revisioned.len(), 1); - let revisioned = &comments.revisioned[&vec![]]; - let pat = &revisioned.error_in_other_files[0]; - assert_eq!(format!("{:?}", **pat), r#"SubString("foomp")"#); - assert_eq!(pat.line().get(), 2); -} - -#[test] -fn parse_regex_error_pattern() { - let s = r" -//@ error-in-other-file: /foomp/ -use std::mem; - - "; - let comments = Comments::parse(s).unwrap(); - println!("parsed comments: {:#?}", comments); - assert_eq!(comments.revisioned.len(), 1); - let revisioned = &comments.revisioned[&vec![]]; - let pat = &revisioned.error_in_other_files[0]; - assert_eq!(format!("{:?}", **pat), r#"Regex(Regex("foomp"))"#); - assert_eq!(pat.line().get(), 2); -} - -#[test] -fn parse_slash_slash_at_fail() { - let s = r" -//@ error-patttern foomp -use std::mem; - - "; - let errors = Comments::parse(s).unwrap_err(); - println!("parsed comments: {:#?}", errors); - assert_eq!(errors.len(), 2); - match &errors[0] { - Error::InvalidComment { msg, span } if span.line_start.get() == 2 => { - assert!(msg.contains("must be followed by `:`")) - } - _ => unreachable!(), - } - match &errors[1] { - Error::InvalidComment { msg, span } if span.line_start.get() == 2 => { - assert_eq!(msg, "`error-patttern` is not a command known to `ui_test`, did you mean `error-pattern`?"); - } - _ => unreachable!(), - } -} - -#[test] -fn missing_colon_fail() { - let s = r" -//@stderr-per-bitwidth hello -use std::mem; - - "; - let errors = Comments::parse(s).unwrap_err(); - println!("parsed comments: {:#?}", errors); - assert_eq!(errors.len(), 1); - match &errors[0] { - Error::InvalidComment { msg, span } if span.line_start.get() == 2 => { - assert!(msg.contains("must be followed by `:`")) - } - _ => unreachable!(), - } -} - -#[test] -fn parse_x86_64() { - let s = r"//@ only-target-x86_64-unknown-linux"; - let comments = Comments::parse(s).unwrap(); - println!("parsed comments: {:#?}", comments); - assert_eq!(comments.revisioned.len(), 1); - let revisioned = &comments.revisioned[&vec![]]; - assert_eq!(revisioned.only.len(), 1); - match &revisioned.only[0] { - Condition::Target(t) => assert_eq!(t, "x86_64-unknown-linux"), - _ => unreachable!(), - } -} diff --git a/vendor/ui_test-0.20.0/src/rustc_stderr.rs b/vendor/ui_test-0.20.0/src/rustc_stderr.rs deleted file mode 100644 index 4a371ce1d..000000000 --- a/vendor/ui_test-0.20.0/src/rustc_stderr.rs +++ /dev/null @@ -1,201 +0,0 @@ -use bstr::ByteSlice; -use regex::Regex; -use std::{ - num::NonZeroUsize, - path::{Path, PathBuf}, -}; - -#[derive(serde::Deserialize, Debug)] -struct RustcMessage { - rendered: Option<String>, - spans: Vec<RustcSpan>, - level: String, - message: String, - children: Vec<RustcMessage>, -} - -#[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] -pub(crate) enum Level { - Ice = 5, - Error = 4, - Warn = 3, - Help = 2, - Note = 1, - /// Only used for "For more information about this error, try `rustc --explain EXXXX`". - FailureNote = 0, -} - -#[derive(Debug)] -/// A diagnostic message. -pub struct Message { - pub(crate) level: Level, - pub(crate) message: String, - pub(crate) line_col: Option<Span>, -} - -/// Information about macro expansion. -#[derive(serde::Deserialize, Debug)] -struct Expansion { - span: RustcSpan, -} - -#[derive(serde::Deserialize, Debug)] -struct RustcSpan { - #[serde(flatten)] - line_col: Span, - file_name: PathBuf, - is_primary: bool, - expansion: Option<Box<Expansion>>, -} - -#[derive(serde::Deserialize, Debug, Copy, Clone)] -pub struct Span { - pub line_start: NonZeroUsize, - pub column_start: NonZeroUsize, - pub line_end: NonZeroUsize, - pub column_end: NonZeroUsize, -} - -impl Span { - pub const INVALID: Self = Self { - line_start: NonZeroUsize::MAX, - column_start: NonZeroUsize::MAX, - line_end: NonZeroUsize::MAX, - column_end: NonZeroUsize::MAX, - }; - - pub fn shrink_to_end(self) -> Span { - Self { - line_start: self.line_end, - column_start: self.column_end, - ..self - } - } - - pub fn shrink_to_start(self) -> Span { - Self { - line_end: self.line_start, - column_end: self.column_start, - ..self - } - } -} - -impl std::str::FromStr for Level { - type Err = String; - fn from_str(s: &str) -> Result<Self, Self::Err> { - match s { - "ERROR" | "error" => Ok(Self::Error), - "WARN" | "warning" => Ok(Self::Warn), - "HELP" | "help" => Ok(Self::Help), - "NOTE" | "note" => Ok(Self::Note), - "failure-note" => Ok(Self::FailureNote), - "error: internal compiler error" => Ok(Self::Ice), - _ => Err(format!("unknown level `{s}`")), - } - } -} - -#[derive(Debug)] -pub(crate) struct Diagnostics { - /// Rendered and concatenated version of all diagnostics. - /// This is equivalent to non-json diagnostics. - pub rendered: Vec<u8>, - /// Per line, a list of messages for that line. - pub messages: Vec<Vec<Message>>, - /// Messages not on any line (usually because they are from libstd) - pub messages_from_unknown_file_or_line: Vec<Message>, -} - -impl RustcMessage { - fn line(&self, file: &Path) -> Option<Span> { - let span = |primary| self.spans.iter().find_map(|span| span.line(file, primary)); - span(true).or_else(|| span(false)) - } - - /// Put the message and its children into the line-indexed list. - fn insert_recursive( - self, - file: &Path, - messages: &mut Vec<Vec<Message>>, - messages_from_unknown_file_or_line: &mut Vec<Message>, - line: Option<Span>, - ) { - let line = self.line(file).or(line); - let msg = Message { - level: self.level.parse().unwrap(), - message: self.message, - line_col: line, - }; - if let Some(line) = line { - if messages.len() <= line.line_start.get() { - messages.resize_with(line.line_start.get() + 1, Vec::new); - } - messages[line.line_start.get()].push(msg); - // All other messages go into the general bin, unless they are specifically of the - // "aborting due to X previous errors" variety, as we never want to match those. They - // only count the number of errors and provide no useful information about the tests. - } else if !(msg.message.starts_with("aborting due to") - && msg.message.contains("previous error")) - { - messages_from_unknown_file_or_line.push(msg); - } - for child in self.children { - child.insert_recursive(file, messages, messages_from_unknown_file_or_line, line) - } - } -} - -impl RustcSpan { - /// Returns the most expanded line number *in the given file*, if possible. - fn line(&self, file: &Path, primary: bool) -> Option<Span> { - if let Some(exp) = &self.expansion { - if let Some(line) = exp.span.line(file, primary && !self.is_primary) { - return Some(line); - } - } - ((!primary || self.is_primary) && self.file_name == file).then_some(self.line_col) - } -} - -pub(crate) fn filter_annotations_from_rendered(rendered: &str) -> std::borrow::Cow<'_, str> { - let annotations = Regex::new(r" *//(\[[a-z,]+\])?~.*").unwrap(); - annotations.replace_all(rendered, "") -} - -pub(crate) fn process(file: &Path, stderr: &[u8]) -> Diagnostics { - let mut rendered = Vec::new(); - let mut messages = vec![]; - let mut messages_from_unknown_file_or_line = vec![]; - for (line_number, line) in stderr.lines_with_terminator().enumerate() { - if line.starts_with_str(b"{") { - match serde_json::from_slice::<RustcMessage>(line) { - Ok(msg) => { - rendered.extend( - filter_annotations_from_rendered(msg.rendered.as_ref().unwrap()).as_bytes(), - ); - msg.insert_recursive( - file, - &mut messages, - &mut messages_from_unknown_file_or_line, - None, - ); - } - Err(err) => { - panic!( - "failed to parse rustc JSON output at line {line_number}: {err}: {}", - line.to_str_lossy() - ) - } - } - } else { - // FIXME: do we want to throw interpreter stderr into a separate file? - rendered.extend(line); - } - } - Diagnostics { - rendered, - messages, - messages_from_unknown_file_or_line, - } -} diff --git a/vendor/ui_test-0.20.0/src/status_emitter.rs b/vendor/ui_test-0.20.0/src/status_emitter.rs deleted file mode 100644 index b32235907..000000000 --- a/vendor/ui_test-0.20.0/src/status_emitter.rs +++ /dev/null @@ -1,972 +0,0 @@ -//! Variaous schemes for reporting messages during testing or after testing is done. - -use annotate_snippets::{ - display_list::{DisplayList, FormatOptions}, - snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation}, -}; -use bstr::ByteSlice; -use colored::Colorize; -use crossbeam_channel::{Sender, TryRecvError}; -use indicatif::{MultiProgress, ProgressBar, ProgressDrawTarget, ProgressStyle}; - -use crate::{ - github_actions, - parser::Pattern, - rustc_stderr::{Message, Span}, - Error, Errored, Errors, TestOk, TestResult, -}; -use std::{ - collections::HashMap, - fmt::{Debug, Write as _}, - io::Write as _, - num::NonZeroUsize, - panic::RefUnwindSafe, - path::{Path, PathBuf}, - process::Command, - sync::atomic::AtomicBool, - time::Duration, -}; - -/// A generic way to handle the output of this crate. -pub trait StatusEmitter: Sync + RefUnwindSafe { - /// Invoked the moment we know a test will later be run. - /// Useful for progress bars and such. - fn register_test(&self, path: PathBuf) -> Box<dyn TestStatus>; - - /// Create a report about the entire test run at the end. - #[allow(clippy::type_complexity)] - fn finalize( - &self, - failed: usize, - succeeded: usize, - ignored: usize, - filtered: usize, - ) -> Box<dyn Summary>; -} - -/// Information about a specific test run. -pub trait TestStatus: Send + Sync + RefUnwindSafe { - /// Create a copy of this test for a new revision. - fn for_revision(&self, revision: &str) -> Box<dyn TestStatus>; - - /// Invoked before each failed test prints its errors along with a drop guard that can - /// gets invoked afterwards. - fn failed_test<'a>( - &'a self, - cmd: &'a Command, - stderr: &'a [u8], - stdout: &'a [u8], - ) -> Box<dyn Debug + 'a>; - - /// Change the status of the test while it is running to supply some kind of progress - fn update_status(&self, msg: String); - - /// A test has finished, handle the result immediately. - fn done(&self, _result: &TestResult) {} - - /// The path of the test file. - fn path(&self) -> &Path; - - /// The revision, usually an empty string. - fn revision(&self) -> &str; -} - -/// Report a summary at the end of a test run. -pub trait Summary { - /// A test has finished, handle the result. - fn test_failure(&mut self, _status: &dyn TestStatus, _errors: &Errors) {} -} - -impl Summary for () {} - -/// A human readable output emitter. -#[derive(Clone)] -pub struct Text { - sender: Sender<Msg>, - progress: bool, -} - -#[derive(Debug)] -enum Msg { - Pop(String, Option<String>), - Push(String), - Inc, - IncLength, - Finish, - Status(String, String), -} - -impl Text { - fn start_thread() -> Sender<Msg> { - let (sender, receiver) = crossbeam_channel::unbounded(); - std::thread::spawn(move || { - let bars = MultiProgress::new(); - let mut progress = None; - let mut threads: HashMap<String, ProgressBar> = HashMap::new(); - 'outer: loop { - std::thread::sleep(Duration::from_millis(100)); - loop { - match receiver.try_recv() { - Ok(val) => match val { - Msg::Pop(msg, new_msg) => { - let Some(spinner) = threads.remove(&msg) else { - // This can happen when a test was not run at all, because it failed directly during - // comment parsing. - continue; - }; - spinner.set_style( - ProgressStyle::with_template("{prefix} {msg}").unwrap(), - ); - if let Some(new_msg) = new_msg { - bars.remove(&spinner); - let spinner = bars.insert(0, spinner); - spinner.tick(); - spinner.finish_with_message(new_msg); - } else { - spinner.finish_and_clear(); - } - } - Msg::Status(msg, status) => { - threads.get_mut(&msg).unwrap().set_message(status); - } - Msg::Push(msg) => { - let spinner = - bars.add(ProgressBar::new_spinner().with_prefix(msg.clone())); - spinner.set_style( - ProgressStyle::with_template("{prefix} {spinner} {msg}") - .unwrap(), - ); - threads.insert(msg, spinner); - } - Msg::IncLength => { - progress - .get_or_insert_with(|| bars.add(ProgressBar::new(0))) - .inc_length(1); - } - Msg::Inc => { - progress.as_ref().unwrap().inc(1); - } - Msg::Finish => return, - }, - Err(TryRecvError::Disconnected) => break 'outer, - Err(TryRecvError::Empty) => break, - } - } - for spinner in threads.values() { - spinner.tick() - } - if let Some(progress) = &progress { - progress.tick() - } - } - assert_eq!(threads.len(), 0); - if let Some(progress) = progress { - progress.tick(); - assert!(progress.is_finished()); - } - }); - sender - } - - /// Print one line per test that gets run. - pub fn verbose() -> Self { - Self { - sender: Self::start_thread(), - progress: false, - } - } - /// Print a progress bar. - pub fn quiet() -> Self { - Self { - sender: Self::start_thread(), - progress: true, - } - } -} - -struct TextTest { - text: Text, - path: PathBuf, - revision: String, - first: AtomicBool, -} - -impl TextTest { - fn msg(&self) -> String { - if self.revision.is_empty() { - self.path.display().to_string() - } else { - format!("{} ({})", self.path.display(), self.revision) - } - } -} - -impl TestStatus for TextTest { - fn done(&self, result: &TestResult) { - if self.text.progress { - self.text.sender.send(Msg::Inc).unwrap(); - self.text.sender.send(Msg::Pop(self.msg(), None)).unwrap(); - } else { - let result = match result { - Ok(TestOk::Ok) => "ok".green(), - Err(Errored { .. }) => "FAILED".red().bold(), - Ok(TestOk::Ignored) => "ignored (in-test comment)".yellow(), - Ok(TestOk::Filtered) => return, - }; - let old_msg = self.msg(); - let msg = format!("... {result}"); - if ProgressDrawTarget::stdout().is_hidden() { - println!("{old_msg} {msg}"); - std::io::stdout().flush().unwrap(); - } else { - self.text.sender.send(Msg::Pop(old_msg, Some(msg))).unwrap(); - } - } - } - - fn update_status(&self, msg: String) { - self.text.sender.send(Msg::Status(self.msg(), msg)).unwrap(); - } - - fn failed_test<'a>( - &self, - cmd: &Command, - stderr: &'a [u8], - stdout: &'a [u8], - ) -> Box<dyn Debug + 'a> { - println!(); - let path = self.path.display().to_string(); - print!("{}", path.underline().bold()); - let revision = if self.revision.is_empty() { - String::new() - } else { - format!(" (revision `{}`)", self.revision) - }; - print!("{revision}"); - print!(" {}", "FAILED:".red().bold()); - println!(); - println!("command: {cmd:?}"); - println!(); - - #[derive(Debug)] - struct Guard<'a> { - stderr: &'a [u8], - stdout: &'a [u8], - } - impl<'a> Drop for Guard<'a> { - fn drop(&mut self) { - println!("full stderr:"); - std::io::stdout().write_all(self.stderr).unwrap(); - println!(); - println!("full stdout:"); - std::io::stdout().write_all(self.stdout).unwrap(); - println!(); - println!(); - } - } - Box::new(Guard { stderr, stdout }) - } - - fn path(&self) -> &Path { - &self.path - } - - fn for_revision(&self, revision: &str) -> Box<dyn TestStatus> { - assert_eq!(self.revision, ""); - if !self.first.swap(false, std::sync::atomic::Ordering::Relaxed) && self.text.progress { - self.text.sender.send(Msg::IncLength).unwrap(); - } - - let text = Self { - text: self.text.clone(), - path: self.path.clone(), - revision: revision.to_owned(), - first: AtomicBool::new(false), - }; - self.text.sender.send(Msg::Push(text.msg())).unwrap(); - Box::new(text) - } - - fn revision(&self) -> &str { - &self.revision - } -} - -impl StatusEmitter for Text { - fn register_test(&self, path: PathBuf) -> Box<dyn TestStatus> { - if self.progress { - self.sender.send(Msg::IncLength).unwrap(); - } - Box::new(TextTest { - text: self.clone(), - path, - revision: String::new(), - first: AtomicBool::new(true), - }) - } - - fn finalize( - &self, - failures: usize, - succeeded: usize, - ignored: usize, - filtered: usize, - ) -> Box<dyn Summary> { - self.sender.send(Msg::Finish).unwrap(); - while !self.sender.is_empty() { - std::thread::sleep(Duration::from_millis(10)); - } - if !ProgressDrawTarget::stdout().is_hidden() { - // The progress bars do not have a trailing newline, so let's - // add it here. - println!(); - } - // Print all errors in a single thread to show reliable output - if failures == 0 { - println!(); - print!("test result: {}.", "ok".green()); - if succeeded > 0 { - print!(" {} passed;", succeeded.to_string().green()); - } - if ignored > 0 { - print!(" {} ignored;", ignored.to_string().yellow()); - } - if filtered > 0 { - print!(" {} filtered out;", filtered.to_string().yellow()); - } - println!(); - println!(); - Box::new(()) - } else { - struct Summarizer { - failures: Vec<String>, - succeeded: usize, - ignored: usize, - filtered: usize, - } - - impl Summary for Summarizer { - fn test_failure(&mut self, status: &dyn TestStatus, errors: &Errors) { - for error in errors { - print_error(error, status.path()); - } - - self.failures.push(if status.revision().is_empty() { - format!(" {}", status.path().display()) - } else { - format!( - " {} (revision {})", - status.path().display(), - status.revision() - ) - }); - } - } - - impl Drop for Summarizer { - fn drop(&mut self) { - println!("{}", "FAILURES:".red().underline().bold()); - for line in &self.failures { - println!("{line}"); - } - println!(); - print!("test result: {}.", "FAIL".red()); - print!(" {} failed;", self.failures.len().to_string().green()); - if self.succeeded > 0 { - print!(" {} passed;", self.succeeded.to_string().green()); - } - if self.ignored > 0 { - print!(" {} ignored;", self.ignored.to_string().yellow()); - } - if self.filtered > 0 { - print!(" {} filtered out;", self.filtered.to_string().yellow()); - } - println!(); - println!(); - } - } - Box::new(Summarizer { - failures: vec![], - succeeded, - ignored, - filtered, - }) - } - } -} - -fn print_error(error: &Error, path: &Path) { - match error { - Error::ExitStatus { - mode, - status, - expected, - } => { - println!("{mode} test got {status}, but expected {expected}") - } - Error::Command { kind, status } => { - println!("{kind} failed with {status}"); - } - Error::PatternNotFound(pattern) => { - let msg = match &**pattern { - Pattern::SubString(s) => { - format!("substring `{s}` {} in stderr output", "not found") - } - Pattern::Regex(r) => { - format!("`/{r}/` does {} stderr output", "not match") - } - }; - create_error( - msg, - &[( - &[("expected because of this pattern", Some(pattern.span()))], - pattern.line(), - )], - path, - ); - } - Error::NoPatternsFound => { - println!("{}", "no error patterns found in fail test".red()); - } - Error::PatternFoundInPassTest => { - println!("{}", "error pattern found in pass test".red()) - } - Error::OutputDiffers { - path: output_path, - actual, - expected, - bless_command, - } => { - println!("{}", "actual output differed from expected".underline()); - println!( - "Execute `{}` to update `{}` to the actual output", - bless_command, - output_path.display() - ); - println!("{}", format!("--- {}", output_path.display()).red()); - println!( - "{}", - format!( - "+++ <{} output>", - output_path.extension().unwrap().to_str().unwrap() - ) - .green() - ); - crate::diff::print_diff(expected, actual); - } - Error::ErrorsWithoutPattern { path, msgs } => { - if let Some(path) = path.as_ref() { - let line = path.line(); - let msgs = msgs - .iter() - .map(|msg| (format!("{:?}: {}", msg.level, msg.message), msg.line_col)) - .collect::<Vec<_>>(); - create_error( - format!("There were {} unmatched diagnostics", msgs.len()), - &[( - &msgs - .iter() - .map(|(msg, lc)| (msg.as_ref(), *lc)) - .collect::<Vec<_>>(), - line, - )], - path, - ); - } else { - println!( - "There were {} unmatched diagnostics that occurred outside the testfile and had no pattern", - msgs.len(), - ); - for Message { - level, - message, - line_col: _, - } in msgs - { - println!(" {level:?}: {message}") - } - } - } - Error::InvalidComment { msg, span } => { - create_error(msg, &[(&[("", Some(*span))], span.line_start)], path) - } - Error::MultipleRevisionsWithResults { kind, lines } => { - let title = format!("multiple {kind} found"); - create_error( - title, - &lines - .iter() - .map(|&line| (&[] as &[_], line)) - .collect::<Vec<_>>(), - path, - ) - } - Error::Bug(msg) => { - println!("A bug in `ui_test` occurred: {msg}"); - } - Error::Aux { - path: aux_path, - errors, - line, - } => { - println!("Aux build from {}:{line} failed", path.display()); - for error in errors { - print_error(error, aux_path); - } - } - Error::Rustfix(error) => { - println!( - "failed to apply suggestions for {} with rustfix: {error}", - path.display() - ); - println!("Add //@no-rustfix to the test file to ignore rustfix suggestions"); - } - } - println!(); -} - -#[allow(clippy::type_complexity)] -fn create_error( - s: impl AsRef<str>, - lines: &[(&[(&str, Option<Span>)], NonZeroUsize)], - file: &Path, -) { - let source = std::fs::read_to_string(file).unwrap(); - let source: Vec<_> = source.split_inclusive('\n').collect(); - let file = file.display().to_string(); - let msg = Snippet { - title: Some(Annotation { - id: None, - annotation_type: AnnotationType::Error, - label: Some(s.as_ref()), - }), - slices: lines - .iter() - .map(|(label, line)| { - let source = source[line.get() - 1]; - let len = source.chars().count(); - Slice { - source, - line_start: line.get(), - origin: Some(&file), - annotations: label - .iter() - .map(|(label, lc)| SourceAnnotation { - range: lc.map_or((0, len - 1), |lc| { - assert_eq!(lc.line_start, *line); - if lc.line_end > lc.line_start { - (lc.column_start.get() - 1, len - 1) - } else if lc.column_start == lc.column_end { - if lc.column_start.get() - 1 == len { - // rustc sometimes produces spans pointing *after* the `\n` at the end of the line, - // but we want to render an annotation at the end. - (lc.column_start.get() - 2, lc.column_start.get() - 1) - } else { - (lc.column_start.get() - 1, lc.column_start.get()) - } - } else { - (lc.column_start.get() - 1, lc.column_end.get() - 1) - } - }), - label, - annotation_type: AnnotationType::Error, - }) - .collect(), - fold: false, - } - }) - .collect(), - footer: vec![], - opt: FormatOptions { - color: colored::control::SHOULD_COLORIZE.should_colorize(), - anonymized_line_numbers: false, - margin: None, - }, - }; - println!("{}", DisplayList::from(msg)); -} - -fn gha_error(error: &Error, test_path: &str, revision: &str) { - match error { - Error::ExitStatus { - mode, - status, - expected, - } => { - github_actions::error( - test_path, - format!("{mode} test{revision} got {status}, but expected {expected}"), - ); - } - Error::Command { kind, status } => { - github_actions::error(test_path, format!("{kind}{revision} failed with {status}")); - } - Error::PatternNotFound(pattern) => { - github_actions::error(test_path, format!("Pattern not found{revision}")) - .line(pattern.line()); - } - Error::NoPatternsFound => { - github_actions::error( - test_path, - format!("no error patterns found in fail test{revision}"), - ); - } - Error::PatternFoundInPassTest => { - github_actions::error( - test_path, - format!("error pattern found in pass test{revision}"), - ); - } - Error::OutputDiffers { - path: output_path, - actual, - expected, - bless_command, - } => { - if expected.is_empty() { - let mut err = github_actions::error( - test_path, - "test generated output, but there was no output file", - ); - writeln!( - err, - "you likely need to bless the tests with `{bless_command}`" - ) - .unwrap(); - return; - } - - let mut line = 1; - for r in - prettydiff::diff_lines(expected.to_str().unwrap(), actual.to_str().unwrap()).diff() - { - use prettydiff::basic::DiffOp::*; - match r { - Equal(s) => { - line += s.len(); - continue; - } - Replace(l, r) => { - let mut err = github_actions::error( - output_path.display().to_string(), - "actual output differs from expected", - ) - .line(NonZeroUsize::new(line + 1).unwrap()); - writeln!(err, "this line was expected to be `{}`", r[0]).unwrap(); - line += l.len(); - } - Remove(l) => { - let mut err = github_actions::error( - output_path.display().to_string(), - "extraneous lines in output", - ) - .line(NonZeroUsize::new(line + 1).unwrap()); - writeln!( - err, - "remove this line and possibly later ones by blessing the test" - ) - .unwrap(); - line += l.len(); - } - Insert(r) => { - let mut err = github_actions::error( - output_path.display().to_string(), - "missing line in output", - ) - .line(NonZeroUsize::new(line + 1).unwrap()); - writeln!(err, "bless the test to create a line containing `{}`", r[0]) - .unwrap(); - // Do not count these lines, they don't exist in the original file and - // would thus mess up the line number. - } - } - } - } - Error::ErrorsWithoutPattern { path, msgs } => { - if let Some(path) = path.as_ref() { - let line = path.line(); - let path = path.display(); - let mut err = - github_actions::error(&path, format!("Unmatched diagnostics{revision}")) - .line(line); - for Message { - level, - message, - line_col: _, - } in msgs - { - writeln!(err, "{level:?}: {message}").unwrap(); - } - } else { - let mut err = github_actions::error( - test_path, - format!("Unmatched diagnostics outside the testfile{revision}"), - ); - for Message { - level, - message, - line_col: _, - } in msgs - { - writeln!(err, "{level:?}: {message}").unwrap(); - } - } - } - Error::InvalidComment { msg, span } => { - let mut err = github_actions::error(test_path, format!("Could not parse comment")) - .line(span.line_start); - writeln!(err, "{msg}").unwrap(); - } - Error::MultipleRevisionsWithResults { kind, lines } => { - github_actions::error(test_path, format!("multiple {kind} found")).line(lines[0]); - } - Error::Bug(_) => {} - Error::Aux { - path: aux_path, - errors, - line, - } => { - github_actions::error(test_path, format!("Aux build failed")).line(*line); - for error in errors { - gha_error(error, &aux_path.display().to_string(), "") - } - } - Error::Rustfix(error) => { - github_actions::error( - test_path, - format!("failed to apply suggestions with rustfix: {error}"), - ); - } - } -} - -/// Emits Github Actions Workspace commands to show the failures directly in the github diff view. -/// If the const generic `GROUP` boolean is `true`, also emit `::group` commands. -pub struct Gha<const GROUP: bool> { - /// Show a specific name for the final summary. - pub name: String, -} - -#[derive(Clone)] -struct PathAndRev<const GROUP: bool> { - path: PathBuf, - revision: String, -} - -impl<const GROUP: bool> TestStatus for PathAndRev<GROUP> { - fn path(&self) -> &Path { - &self.path - } - - fn for_revision(&self, revision: &str) -> Box<dyn TestStatus> { - assert_eq!(self.revision, ""); - Box::new(Self { - path: self.path.clone(), - revision: revision.to_owned(), - }) - } - - fn failed_test(&self, _cmd: &Command, _stderr: &[u8], _stdout: &[u8]) -> Box<dyn Debug> { - if GROUP { - Box::new(github_actions::group(format_args!( - "{}:{}", - self.path.display(), - self.revision - ))) - } else { - Box::new(()) - } - } - - fn revision(&self) -> &str { - &self.revision - } - - fn update_status(&self, _msg: String) {} -} - -impl<const GROUP: bool> StatusEmitter for Gha<GROUP> { - fn register_test(&self, path: PathBuf) -> Box<dyn TestStatus> { - Box::new(PathAndRev::<GROUP> { - path, - revision: String::new(), - }) - } - - fn finalize( - &self, - _failures: usize, - succeeded: usize, - ignored: usize, - filtered: usize, - ) -> Box<dyn Summary> { - struct Summarizer<const GROUP: bool> { - failures: Vec<String>, - succeeded: usize, - ignored: usize, - filtered: usize, - name: String, - } - - impl<const GROUP: bool> Summary for Summarizer<GROUP> { - fn test_failure(&mut self, status: &dyn TestStatus, errors: &Errors) { - let revision = if status.revision().is_empty() { - "".to_string() - } else { - format!(" (revision: {})", status.revision()) - }; - for error in errors { - gha_error(error, &status.path().display().to_string(), &revision); - } - self.failures - .push(format!("{}{revision}", status.path().display())); - } - } - impl<const GROUP: bool> Drop for Summarizer<GROUP> { - fn drop(&mut self) { - if let Some(mut file) = github_actions::summary() { - writeln!(file, "### {}", self.name).unwrap(); - for line in &self.failures { - writeln!(file, "* {line}").unwrap(); - } - writeln!(file).unwrap(); - writeln!(file, "| failed | passed | ignored | filtered out |").unwrap(); - writeln!(file, "| --- | --- | --- | --- |").unwrap(); - writeln!( - file, - "| {} | {} | {} | {} |", - self.failures.len(), - self.succeeded, - self.ignored, - self.filtered, - ) - .unwrap(); - } - } - } - - Box::new(Summarizer::<GROUP> { - failures: vec![], - succeeded, - ignored, - filtered, - name: self.name.clone(), - }) - } -} - -impl<T: TestStatus, U: TestStatus> TestStatus for (T, U) { - fn done(&self, result: &TestResult) { - self.0.done(result); - self.1.done(result); - } - - fn failed_test<'a>( - &'a self, - cmd: &'a Command, - stderr: &'a [u8], - stdout: &'a [u8], - ) -> Box<dyn Debug + 'a> { - Box::new(( - self.0.failed_test(cmd, stderr, stdout), - self.1.failed_test(cmd, stderr, stdout), - )) - } - - fn path(&self) -> &Path { - let path = self.0.path(); - assert_eq!(path, self.1.path()); - path - } - - fn revision(&self) -> &str { - let rev = self.0.revision(); - assert_eq!(rev, self.1.revision()); - rev - } - - fn for_revision(&self, revision: &str) -> Box<dyn TestStatus> { - Box::new((self.0.for_revision(revision), self.1.for_revision(revision))) - } - - fn update_status(&self, msg: String) { - self.0.update_status(msg.clone()); - self.1.update_status(msg) - } -} - -impl<T: StatusEmitter, U: StatusEmitter> StatusEmitter for (T, U) { - fn register_test(&self, path: PathBuf) -> Box<dyn TestStatus> { - Box::new(( - self.0.register_test(path.clone()), - self.1.register_test(path), - )) - } - - fn finalize( - &self, - failures: usize, - succeeded: usize, - ignored: usize, - filtered: usize, - ) -> Box<dyn Summary> { - Box::new(( - self.1.finalize(failures, succeeded, ignored, filtered), - self.0.finalize(failures, succeeded, ignored, filtered), - )) - } -} - -impl<T: TestStatus + ?Sized> TestStatus for Box<T> { - fn done(&self, result: &TestResult) { - (**self).done(result); - } - - fn path(&self) -> &Path { - (**self).path() - } - - fn revision(&self) -> &str { - (**self).revision() - } - - fn for_revision(&self, revision: &str) -> Box<dyn TestStatus> { - (**self).for_revision(revision) - } - - fn failed_test<'a>( - &'a self, - cmd: &'a Command, - stderr: &'a [u8], - stdout: &'a [u8], - ) -> Box<dyn Debug + 'a> { - (**self).failed_test(cmd, stderr, stdout) - } - - fn update_status(&self, msg: String) { - (**self).update_status(msg) - } -} - -impl<T: StatusEmitter + ?Sized> StatusEmitter for Box<T> { - fn register_test(&self, path: PathBuf) -> Box<dyn TestStatus> { - (**self).register_test(path) - } - - fn finalize( - &self, - failures: usize, - succeeded: usize, - ignored: usize, - filtered: usize, - ) -> Box<dyn Summary> { - (**self).finalize(failures, succeeded, ignored, filtered) - } -} - -impl Summary for (Box<dyn Summary>, Box<dyn Summary>) { - fn test_failure(&mut self, status: &dyn TestStatus, errors: &Errors) { - self.0.test_failure(status, errors); - self.1.test_failure(status, errors); - } -} diff --git a/vendor/ui_test-0.20.0/src/tests.rs b/vendor/ui_test-0.20.0/src/tests.rs deleted file mode 100644 index 791027d4f..000000000 --- a/vendor/ui_test-0.20.0/src/tests.rs +++ /dev/null @@ -1,350 +0,0 @@ -use std::path::{Path, PathBuf}; - -use crate::rustc_stderr::Level; -use crate::rustc_stderr::Message; - -use super::*; - -fn config() -> Config { - Config { - root_dir: PathBuf::from("$RUSTROOT"), - program: CommandBuilder::cmd("cake"), - ..Config::rustc(PathBuf::new()) - } -} - -#[test] -fn issue_2156() { - let s = r" -use std::mem; - -fn main() { - let _x: &i32 = unsafe { mem::transmute(16usize) }; //~ ERROR: encountered a dangling reference (address $HEX is unallocated) -} - "; - let comments = Comments::parse(s).unwrap(); - let mut errors = vec![]; - let config = config(); - let messages = vec![ - vec![], vec![], vec![], vec![], vec![], - vec![ - Message { - message:"Undefined Behavior: type validation failed: encountered a dangling reference (address 0x10 is unallocated)".to_string(), - level: Level::Error, - line_col: None, - } - ] - ]; - check_annotations( - messages, - vec![], - Path::new("moobar"), - &mut errors, - &config, - "", - &comments, - ) - .unwrap(); - match &errors[..] { - [Error::PatternNotFound(pattern), Error::ErrorsWithoutPattern { path, .. }] - if path.as_ref().is_some_and(|p| p.line().get() == 5) && pattern.line().get() == 5 => {} - _ => panic!("{:#?}", errors), - } -} - -#[test] -fn find_pattern() { - let s = r" -use std::mem; - -fn main() { - let _x: &i32 = unsafe { mem::transmute(16usize) }; //~ ERROR: encountered a dangling reference (address 0x10 is unallocated) -} - "; - let comments = Comments::parse(s).unwrap(); - let config = config(); - { - let messages = vec![vec![], vec![], vec![], vec![], vec![], vec![ - Message { - message: "Undefined Behavior: type validation failed: encountered a dangling reference (address 0x10 is unallocated)".to_string(), - level: Level::Error, - line_col: None, - } - ] - ]; - let mut errors = vec![]; - check_annotations( - messages, - vec![], - Path::new("moobar"), - &mut errors, - &config, - "", - &comments, - ) - .unwrap(); - match &errors[..] { - [] => {} - _ => panic!("{:#?}", errors), - } - } - - // only difference to above is a wrong line number - { - let messages = vec![vec![], vec![], vec![], vec![], vec![ - Message { - message: "Undefined Behavior: type validation failed: encountered a dangling reference (address 0x10 is unallocated)".to_string(), - level: Level::Error, - line_col: None, - } - ] - ]; - let mut errors = vec![]; - check_annotations( - messages, - vec![], - Path::new("moobar"), - &mut errors, - &config, - "", - &comments, - ) - .unwrap(); - match &errors[..] { - [Error::PatternNotFound(pattern), Error::ErrorsWithoutPattern { path, .. }] - if path.as_ref().is_some_and(|p| p.line().get() == 4) - && pattern.line().get() == 5 => {} - _ => panic!("not the expected error: {:#?}", errors), - } - } - - // only difference to first is a wrong level - { - let messages = vec![ - vec![], vec![], vec![], vec![], vec![], - vec![ - Message { - message: "Undefined Behavior: type validation failed: encountered a dangling reference (address 0x10 is unallocated)".to_string(), - level: Level::Note, - line_col: None, - } - ] - ]; - let mut errors = vec![]; - check_annotations( - messages, - vec![], - Path::new("moobar"), - &mut errors, - &config, - "", - &comments, - ) - .unwrap(); - match &errors[..] { - // Note no `ErrorsWithoutPattern`, because there are no `//~NOTE` in the test file, so we ignore them - [Error::PatternNotFound(pattern)] if pattern.line().get() == 5 => {} - _ => panic!("not the expected error: {:#?}", errors), - } - } -} - -#[test] -fn duplicate_pattern() { - let s = r" -use std::mem; - -fn main() { - let _x: &i32 = unsafe { mem::transmute(16usize) }; //~ ERROR: encountered a dangling reference (address 0x10 is unallocated) - //~^ ERROR: encountered a dangling reference (address 0x10 is unallocated) -} - "; - let comments = Comments::parse(s).unwrap(); - let config = config(); - let messages = vec![ - vec![], vec![], vec![], vec![], vec![], - vec![ - Message { - message: "Undefined Behavior: type validation failed: encountered a dangling reference (address 0x10 is unallocated)".to_string(), - level: Level::Error, - line_col: None, - } - ] - ]; - let mut errors = vec![]; - check_annotations( - messages, - vec![], - Path::new("moobar"), - &mut errors, - &config, - "", - &comments, - ) - .unwrap(); - match &errors[..] { - [Error::PatternNotFound(pattern)] if pattern.line().get() == 6 => {} - _ => panic!("{:#?}", errors), - } -} - -#[test] -fn missing_pattern() { - let s = r" -use std::mem; - -fn main() { - let _x: &i32 = unsafe { mem::transmute(16usize) }; //~ ERROR: encountered a dangling reference (address 0x10 is unallocated) -} - "; - let comments = Comments::parse(s).unwrap(); - let config = config(); - let messages = vec![ - vec![], vec![], vec![], vec![], vec![], - vec![ - Message { - message: "Undefined Behavior: type validation failed: encountered a dangling reference (address 0x10 is unallocated)".to_string(), - level: Level::Error, - line_col: None, - }, - Message { - message: "Undefined Behavior: type validation failed: encountered a dangling reference (address 0x10 is unallocated)".to_string(), - level: Level::Error, - line_col: None, - } - ] - ]; - let mut errors = vec![]; - check_annotations( - messages, - vec![], - Path::new("moobar"), - &mut errors, - &config, - "", - &comments, - ) - .unwrap(); - match &errors[..] { - [Error::ErrorsWithoutPattern { path, .. }] - if path.as_ref().is_some_and(|p| p.line().get() == 5) => {} - _ => panic!("{:#?}", errors), - } -} - -#[test] -fn missing_warn_pattern() { - let s = r" -use std::mem; - -fn main() { - let _x: &i32 = unsafe { mem::transmute(16usize) }; //~ ERROR: encountered a dangling reference (address 0x10 is unallocated) - //~^ WARN: cake -} - "; - let comments = Comments::parse(s).unwrap(); - let config = config(); - let messages= vec![ - vec![], - vec![], - vec![], - vec![], - vec![], - vec![ - Message { - message: "Undefined Behavior: type validation failed: encountered a dangling reference (address 0x10 is unallocated)".to_string(), - level: Level::Error, - line_col: None, - }, - Message { - message: "kaboom".to_string(), - level: Level::Warn, - line_col: None, - }, - Message { - message: "cake".to_string(), - level: Level::Warn, - line_col: None, - }, - ], - ]; - let mut errors = vec![]; - check_annotations( - messages, - vec![], - Path::new("moobar"), - &mut errors, - &config, - "", - &comments, - ) - .unwrap(); - match &errors[..] { - [Error::ErrorsWithoutPattern { path, msgs, .. }] - if path.as_ref().is_some_and(|p| p.line().get() == 5) => - { - match &msgs[..] { - [Message { - message, - level: Level::Warn, - line_col: _, - }] if message == "kaboom" => {} - _ => panic!("{:#?}", msgs), - } - } - _ => panic!("{:#?}", errors), - } -} - -#[test] -fn missing_implicit_warn_pattern() { - let s = r" -use std::mem; -//@require-annotations-for-level: ERROR -fn main() { - let _x: &i32 = unsafe { mem::transmute(16usize) }; //~ ERROR: encountered a dangling reference (address 0x10 is unallocated) - //~^ WARN: cake -} - "; - let comments = Comments::parse(s).unwrap(); - let config = config(); - let messages = vec![ - vec![], - vec![], - vec![], - vec![], - vec![], - vec![ - Message { - message: "Undefined Behavior: type validation failed: encountered a dangling reference (address 0x10 is unallocated)".to_string(), - level: Level::Error, - line_col: None, - }, - Message { - message: "kaboom".to_string(), - level: Level::Warn, - line_col: None, - }, - Message { - message: "cake".to_string(), - level: Level::Warn, - line_col: None, - }, - ], - ]; - let mut errors = vec![]; - check_annotations( - messages, - vec![], - Path::new("moobar"), - &mut errors, - &config, - "", - &comments, - ) - .unwrap(); - match &errors[..] { - [] => {} - _ => panic!("{:#?}", errors), - } -} diff --git a/vendor/ui_test-0.20.0/tests/integration.rs b/vendor/ui_test-0.20.0/tests/integration.rs deleted file mode 100644 index abc9a92e2..000000000 --- a/vendor/ui_test-0.20.0/tests/integration.rs +++ /dev/null @@ -1,120 +0,0 @@ -use std::path::Path; - -use ui_test::color_eyre::Result; -use ui_test::*; - -fn main() -> Result<()> { - let path = Path::new(file!()).parent().unwrap(); - let root_dir = path.join("integrations"); - let mut config = Config { - ..Config::cargo(root_dir.clone()) - }; - let args = Args::test()?; - config.with_args(&args, true); - - config.program.args = vec![ - "test".into(), - "--color".into(), - "never".into(), - "--quiet".into(), - "--jobs".into(), - "1".into(), - "--no-fail-fast".into(), - ]; - config - .program - .envs - .push(("RUST_TEST_THREADS".into(), Some("1".into()))); - - config - .program - .envs - .push(("BLESS".into(), (!args.check).then(|| String::new().into()))); - - config.stdout_filter("in ([0-9]m )?[0-9\\.]+s", ""); - config.stdout_filter(r#""--out-dir"(,)? "[^"]+""#, r#""--out-dir"$1 "$$TMP"#); - config.filter( - "( *process didn't exit successfully: `[^-]+)-[0-9a-f]+", - "$1-HASH", - ); - // Windows io::Error uses "exit code". - config.filter("exit code", "exit status"); - config.filter( - "The system cannot find the file specified.", - "No such file or directory", - ); - // The order of the `/deps` directory flag is flaky - config.stdout_filter("/deps", ""); - config.path_filter(std::path::Path::new(path), "$DIR"); - config.stdout_filter("[0-9a-f]+\\.rmeta", "$$HASH.rmeta"); - // Windows backslashes are sometimes escaped. - // Insert the replacement filter at the start to make sure the filter for single backslashes - // runs afterwards. - config - .stdout_filters - .insert(0, (Match::Exact(b"\\\\".to_vec()), b"\\")); - config.filter("\\.exe", b""); - config.stdout_filter(r#"(panic.*)\.rs:[0-9]+:[0-9]+"#, "$1.rs"); - config.filter(" [0-9]: .*", ""); - config.stdout_filter("/target/[^/]+/[^/]+/debug", "/target/$$TMP/$$TRIPLE/debug"); - config.stdout_filter("/target/.tmp[^/ \"]+", "/target/$$TMP"); - // Normalize proc macro filenames on windows to their linux repr - config.stdout_filter("/([^/\\.]+)\\.dll", "/lib$1.so"); - // Normalize proc macro filenames on mac to their linux repr - config.stdout_filter("/([^/\\.]+)\\.dylib", "/$1.so"); - config.stdout_filter("(command: )\"[^<rp][^\"]+", "$1\"$$CMD"); - config.filter("(src/.*?\\.rs):[0-9]+:[0-9]+", "$1:LL:CC"); - config.filter("program not found", "No such file or directory"); - config.filter(" \\(os error [0-9]+\\)", ""); - - let text = if args.quiet { - ui_test::status_emitter::Text::quiet() - } else { - ui_test::status_emitter::Text::verbose() - }; - - run_tests_generic( - vec![ - Config { - mode: Mode::Pass, - ..config.clone() - }, - Config { - mode: Mode::Panic, - ..config - }, - ], - |path, config| { - let fail = path - .parent() - .unwrap() - .file_name() - .unwrap() - .to_str() - .unwrap() - .ends_with("-fail"); - if cfg!(windows) && path.components().any(|c| c.as_os_str() == "basic-bin") { - // on windows there's also a .pdb file, so we get additional errors that aren't there on other platforms - return false; - } - path.ends_with("Cargo.toml") - && path.parent().unwrap().parent().unwrap() == root_dir - && match config.mode { - Mode::Pass => !fail, - // This is weird, but `cargo test` returns 101 instead of 1 when - // multiple [[test]]s exist. If there's only one test, it returns - // 1 on failure. - Mode::Panic => fail, - Mode::Run { .. } | Mode::Yolo { .. } | Mode::Fail { .. } => unreachable!(), - } - && default_any_file_filter(path, config) - }, - |_, _, _| {}, - ( - text, - ui_test::status_emitter::Gha::<true> { - name: "integration tests".into(), - }, - ), - ) -} |