Actually use eyre and get rid of the ad-hoc macros emulating error handling
This commit is contained in:
parent
570032b0dd
commit
54b6b03410
@ -2,7 +2,7 @@ use colored::*;
|
|||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use ui_test::{Config, Mode, OutputConflictHandling, color_eyre::Result};
|
use ui_test::{color_eyre::Result, Config, Mode, OutputConflictHandling};
|
||||||
|
|
||||||
fn miri_path() -> PathBuf {
|
fn miri_path() -> PathBuf {
|
||||||
PathBuf::from(option_env!("MIRI").unwrap_or(env!("CARGO_BIN_EXE_miri")))
|
PathBuf::from(option_env!("MIRI").unwrap_or(env!("CARGO_BIN_EXE_miri")))
|
||||||
|
@ -4,7 +4,7 @@ use regex::Regex;
|
|||||||
|
|
||||||
use crate::rustc_stderr::Level;
|
use crate::rustc_stderr::Level;
|
||||||
|
|
||||||
use color_eyre::eyre::Result;
|
use color_eyre::eyre::{bail, ensure, eyre, Result};
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
@ -66,43 +66,6 @@ impl Condition {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! checked {
|
|
||||||
($path:expr, $l:expr) => {
|
|
||||||
let path = $path;
|
|
||||||
let l = $l;
|
|
||||||
#[allow(unused_macros)]
|
|
||||||
macro_rules! exit {
|
|
||||||
($fmt:expr $$(,$args:expr)*) => {{
|
|
||||||
eprint!("{}:{l}: ", path.display());
|
|
||||||
eprintln!($fmt, $$($args,)*);
|
|
||||||
#[cfg(not(test))]
|
|
||||||
std::process::exit(1);
|
|
||||||
#[cfg(test)]
|
|
||||||
panic!();
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
#[allow(unused_macros)]
|
|
||||||
macro_rules! check {
|
|
||||||
($cond:expr, $fmt:expr $$(,$args:expr)*) => {{
|
|
||||||
if !$cond {
|
|
||||||
exit!($fmt $$(,$args)*);
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
#[allow(unused_macros)]
|
|
||||||
macro_rules! unwrap {
|
|
||||||
($cond:expr, $fmt:expr $$(,$args:expr)*) => {{
|
|
||||||
match $cond {
|
|
||||||
Some(val) => val,
|
|
||||||
None => {
|
|
||||||
exit!($fmt $$(,$args)*);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Comments {
|
impl Comments {
|
||||||
pub(crate) fn parse_file(path: &Path) -> Result<Self> {
|
pub(crate) fn parse_file(path: &Path) -> Result<Self> {
|
||||||
let content = std::fs::read_to_string(path)?;
|
let content = std::fs::read_to_string(path)?;
|
||||||
@ -121,26 +84,38 @@ impl Comments {
|
|||||||
let mut fallthrough_to = None;
|
let mut fallthrough_to = None;
|
||||||
for (l, line) in content.lines().enumerate() {
|
for (l, line) in content.lines().enumerate() {
|
||||||
let l = l + 1; // enumerate starts at 0, but line numbers start at 1
|
let l = l + 1; // enumerate starts at 0, but line numbers start at 1
|
||||||
if let Some((_, command)) = line.split_once("//@") {
|
this.parse_checked_line(l, &mut fallthrough_to, line).map_err(|err| {
|
||||||
let command = command.trim();
|
err.wrap_err(format!("{}:{l}: failed to parse annotation", path.display()))
|
||||||
if let Some((command, args)) = command.split_once(':') {
|
})?;
|
||||||
this.parse_command_with_args(command, args, path, l);
|
|
||||||
} else if let Some((command, _comments)) = command.split_once(' ') {
|
|
||||||
this.parse_command(command, path, l)
|
|
||||||
} else {
|
|
||||||
this.parse_command(command, path, l)
|
|
||||||
}
|
|
||||||
} else if let Some((_, pattern)) = line.split_once("//~") {
|
|
||||||
this.parse_pattern(pattern, &mut fallthrough_to, path, l)
|
|
||||||
} else if let Some((_, pattern)) = line.split_once("//[") {
|
|
||||||
this.parse_revisioned_pattern(pattern, &mut fallthrough_to, path, l)
|
|
||||||
} else {
|
|
||||||
fallthrough_to = None;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(this)
|
Ok(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_checked_line(
|
||||||
|
&mut self,
|
||||||
|
l: usize,
|
||||||
|
fallthrough_to: &mut Option<usize>,
|
||||||
|
line: &str,
|
||||||
|
) -> Result<()> {
|
||||||
|
if let Some((_, command)) = line.split_once("//@") {
|
||||||
|
let command = command.trim();
|
||||||
|
if let Some((command, args)) = command.split_once(':') {
|
||||||
|
self.parse_command_with_args(command, args, l)
|
||||||
|
} else if let Some((command, _comments)) = command.split_once(' ') {
|
||||||
|
self.parse_command(command)
|
||||||
|
} else {
|
||||||
|
self.parse_command(command)
|
||||||
|
}
|
||||||
|
} else if let Some((_, pattern)) = line.split_once("//~") {
|
||||||
|
self.parse_pattern(pattern, fallthrough_to, l)
|
||||||
|
} else if let Some((_, pattern)) = line.split_once("//[") {
|
||||||
|
self.parse_revisioned_pattern(pattern, fallthrough_to, l)
|
||||||
|
} else {
|
||||||
|
*fallthrough_to = None;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse comments in `content`.
|
/// Parse comments in `content`.
|
||||||
/// `path` is only used to emit diagnostics if parsing fails.
|
/// `path` is only used to emit diagnostics if parsing fails.
|
||||||
pub(crate) fn parse(path: &Path, content: &str) -> Result<Self> {
|
pub(crate) fn parse(path: &Path, content: &str) -> Result<Self> {
|
||||||
@ -214,11 +189,10 @@ impl Comments {
|
|||||||
Ok(this)
|
Ok(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_command_with_args(&mut self, command: &str, args: &str, path: &Path, l: usize) {
|
fn parse_command_with_args(&mut self, command: &str, args: &str, l: usize) -> Result<()> {
|
||||||
checked!(path, l);
|
|
||||||
match command {
|
match command {
|
||||||
"revisions" => {
|
"revisions" => {
|
||||||
check!(self.revisions.is_none(), "cannot specifiy revisions twice");
|
ensure!(self.revisions.is_none(), "cannot specifiy revisions twice");
|
||||||
self.revisions = Some(args.split_whitespace().map(|s| s.to_string()).collect());
|
self.revisions = Some(args.split_whitespace().map(|s| s.to_string()).collect());
|
||||||
}
|
}
|
||||||
"compile-flags" => {
|
"compile-flags" => {
|
||||||
@ -226,22 +200,22 @@ impl Comments {
|
|||||||
}
|
}
|
||||||
"rustc-env" =>
|
"rustc-env" =>
|
||||||
for env in args.split_whitespace() {
|
for env in args.split_whitespace() {
|
||||||
let (k, v) = unwrap!(
|
let (k, v) = env.split_once('=').ok_or_else(|| {
|
||||||
env.split_once('='),
|
eyre!("environment variables must be key/value pairs separated by a `=`")
|
||||||
"environment variables must be key/value pairs separated by a `=`"
|
})?;
|
||||||
);
|
|
||||||
self.env_vars.push((k.to_string(), v.to_string()));
|
self.env_vars.push((k.to_string(), v.to_string()));
|
||||||
},
|
},
|
||||||
"normalize-stderr-test" => {
|
"normalize-stderr-test" => {
|
||||||
let (from, to) =
|
let (from, to) = args
|
||||||
unwrap!(args.split_once("->"), "normalize-stderr-test needs a `->`");
|
.split_once("->")
|
||||||
|
.ok_or_else(|| eyre!("normalize-stderr-test needs a `->`"))?;
|
||||||
let from = from.trim().trim_matches('"');
|
let from = from.trim().trim_matches('"');
|
||||||
let to = to.trim().trim_matches('"');
|
let to = to.trim().trim_matches('"');
|
||||||
let from = unwrap!(Regex::new(from).ok(), "invalid regex");
|
let from = Regex::new(from).ok().ok_or_else(|| eyre!("invalid regex"))?;
|
||||||
self.normalize_stderr.push((from, to.to_string()));
|
self.normalize_stderr.push((from, to.to_string()));
|
||||||
}
|
}
|
||||||
"error-pattern" => {
|
"error-pattern" => {
|
||||||
check!(
|
ensure!(
|
||||||
self.error_pattern.is_none(),
|
self.error_pattern.is_none(),
|
||||||
"cannot specifiy error_pattern twice, previous: {:?}",
|
"cannot specifiy error_pattern twice, previous: {:?}",
|
||||||
self.error_pattern
|
self.error_pattern
|
||||||
@ -249,56 +223,52 @@ impl Comments {
|
|||||||
self.error_pattern = Some((args.trim().to_string(), l));
|
self.error_pattern = Some((args.trim().to_string(), l));
|
||||||
}
|
}
|
||||||
// Maybe the user just left a comment explaining a command without arguments
|
// Maybe the user just left a comment explaining a command without arguments
|
||||||
_ => self.parse_command(command, path, l),
|
_ => self.parse_command(command)?,
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_command(&mut self, command: &str, path: &Path, l: usize) {
|
fn parse_command(&mut self, command: &str) -> Result<()> {
|
||||||
checked!(path, l);
|
|
||||||
|
|
||||||
if let Some(s) = command.strip_prefix("ignore-") {
|
if let Some(s) = command.strip_prefix("ignore-") {
|
||||||
self.ignore.push(Condition::parse(s));
|
self.ignore.push(Condition::parse(s));
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(s) = command.strip_prefix("only-") {
|
if let Some(s) = command.strip_prefix("only-") {
|
||||||
self.only.push(Condition::parse(s));
|
self.only.push(Condition::parse(s));
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
if command.starts_with("stderr-per-bitwidth") {
|
if command.starts_with("stderr-per-bitwidth") {
|
||||||
check!(!self.stderr_per_bitwidth, "cannot specifiy stderr-per-bitwidth twice");
|
ensure!(!self.stderr_per_bitwidth, "cannot specifiy stderr-per-bitwidth twice");
|
||||||
self.stderr_per_bitwidth = true;
|
self.stderr_per_bitwidth = true;
|
||||||
return;
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
exit!("unknown command {command}");
|
bail!("unknown command {command}");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_pattern(
|
fn parse_pattern(
|
||||||
&mut self,
|
&mut self,
|
||||||
pattern: &str,
|
pattern: &str,
|
||||||
fallthrough_to: &mut Option<usize>,
|
fallthrough_to: &mut Option<usize>,
|
||||||
path: &Path,
|
|
||||||
l: usize,
|
l: usize,
|
||||||
) {
|
) -> Result<()> {
|
||||||
self.parse_pattern_inner(pattern, fallthrough_to, None, path, l)
|
self.parse_pattern_inner(pattern, fallthrough_to, None, l)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_revisioned_pattern(
|
fn parse_revisioned_pattern(
|
||||||
&mut self,
|
&mut self,
|
||||||
pattern: &str,
|
pattern: &str,
|
||||||
fallthrough_to: &mut Option<usize>,
|
fallthrough_to: &mut Option<usize>,
|
||||||
path: &Path,
|
|
||||||
l: usize,
|
l: usize,
|
||||||
) {
|
) -> Result<()> {
|
||||||
checked!(path, l);
|
|
||||||
let (revision, pattern) =
|
let (revision, pattern) =
|
||||||
unwrap!(pattern.split_once(']'), "`//[` without corresponding `]`");
|
pattern.split_once(']').ok_or_else(|| eyre!("`//[` without corresponding `]`"))?;
|
||||||
if let Some(pattern) = pattern.strip_prefix('~') {
|
if let Some(pattern) = pattern.strip_prefix('~') {
|
||||||
self.parse_pattern_inner(pattern, fallthrough_to, Some(revision.to_owned()), path, l)
|
self.parse_pattern_inner(pattern, fallthrough_to, Some(revision.to_owned()), l)
|
||||||
} else {
|
} else {
|
||||||
exit!("revisioned pattern must have `~` following the `]`");
|
bail!("revisioned pattern must have `~` following the `]`");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -308,21 +278,25 @@ impl Comments {
|
|||||||
pattern: &str,
|
pattern: &str,
|
||||||
fallthrough_to: &mut Option<usize>,
|
fallthrough_to: &mut Option<usize>,
|
||||||
revision: Option<String>,
|
revision: Option<String>,
|
||||||
path: &Path,
|
|
||||||
l: usize,
|
l: usize,
|
||||||
) {
|
) -> Result<()> {
|
||||||
checked!(path, l);
|
|
||||||
// FIXME: check that the error happens on the marked line
|
// FIXME: check that the error happens on the marked line
|
||||||
|
|
||||||
let (match_line, pattern) = match unwrap!(pattern.chars().next(), "no pattern specified") {
|
let (match_line, pattern) =
|
||||||
'|' =>
|
match pattern.chars().next().ok_or_else(|| eyre!("no pattern specified"))? {
|
||||||
(*unwrap!(fallthrough_to, "`//~|` pattern without preceding line"), &pattern[1..]),
|
'|' =>
|
||||||
'^' => {
|
(
|
||||||
let offset = pattern.chars().take_while(|&c| c == '^').count();
|
*fallthrough_to
|
||||||
(l - offset, &pattern[offset..])
|
.as_mut()
|
||||||
}
|
.ok_or_else(|| eyre!("`//~|` pattern without preceding line"))?,
|
||||||
_ => (l, pattern),
|
&pattern[1..],
|
||||||
};
|
),
|
||||||
|
'^' => {
|
||||||
|
let offset = pattern.chars().take_while(|&c| c == '^').count();
|
||||||
|
(l - offset, &pattern[offset..])
|
||||||
|
}
|
||||||
|
_ => (l, pattern),
|
||||||
|
};
|
||||||
|
|
||||||
let (level, pattern) = match pattern.trim_start().split_once(|c| matches!(c, ':' | ' ')) {
|
let (level, pattern) = match pattern.trim_start().split_once(|c| matches!(c, ':' | ' ')) {
|
||||||
None => (None, pattern),
|
None => (None, pattern),
|
||||||
@ -335,7 +309,7 @@ impl Comments {
|
|||||||
|
|
||||||
let matched = pattern.trim().to_string();
|
let matched = pattern.trim().to_string();
|
||||||
|
|
||||||
check!(!matched.is_empty(), "no pattern specified");
|
ensure!(!matched.is_empty(), "no pattern specified");
|
||||||
|
|
||||||
*fallthrough_to = Some(match_line);
|
*fallthrough_to = Some(match_line);
|
||||||
|
|
||||||
@ -346,5 +320,7 @@ impl Comments {
|
|||||||
definition_line: l,
|
definition_line: l,
|
||||||
line: match_line,
|
line: match_line,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
use std::{path::Path, panic::catch_unwind};
|
use std::path::Path;
|
||||||
|
|
||||||
use super::Comments;
|
use super::Comments;
|
||||||
|
|
||||||
use color_eyre::eyre::{Result, bail};
|
|
||||||
use crate::tests::init;
|
use crate::tests::init;
|
||||||
|
use color_eyre::eyre::{bail, Result};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_simple_comment() -> Result<()> {
|
fn parse_simple_comment() -> Result<()> {
|
||||||
@ -48,8 +48,8 @@ fn parse_slash_slash_at_fail() -> Result<()> {
|
|||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
";
|
";
|
||||||
match catch_unwind(|| Comments::parse(Path::new("<dummy>"), s)) {
|
match Comments::parse(Path::new("<dummy>"), s) {
|
||||||
Ok(_) => bail!("expected parsing to panic"),
|
Ok(_) => bail!("expected parsing to fail"),
|
||||||
Err(_) => Ok(()),
|
Err(_) => Ok(()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,12 +7,12 @@ use std::process::{Command, ExitStatus};
|
|||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use std::sync::Mutex;
|
use std::sync::Mutex;
|
||||||
|
|
||||||
|
pub use color_eyre;
|
||||||
|
use color_eyre::eyre::Result;
|
||||||
use colored::*;
|
use colored::*;
|
||||||
use comments::ErrorMatch;
|
use comments::ErrorMatch;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use rustc_stderr::{Level, Message};
|
use rustc_stderr::{Level, Message};
|
||||||
use color_eyre::eyre::Result;
|
|
||||||
pub use color_eyre;
|
|
||||||
|
|
||||||
use crate::comments::{Comments, Condition};
|
use crate::comments::{Comments, Condition};
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ pub fn run_tests(config: Config) -> Result<()> {
|
|||||||
let ignored = AtomicUsize::default();
|
let ignored = AtomicUsize::default();
|
||||||
let filtered = AtomicUsize::default();
|
let filtered = AtomicUsize::default();
|
||||||
|
|
||||||
crossbeam::scope(|s| {
|
crossbeam::scope(|s| -> Result<()> {
|
||||||
// Create a thread that is in charge of walking the directory and submitting jobs.
|
// Create a thread that is in charge of walking the directory and submitting jobs.
|
||||||
// It closes the channel when it is done.
|
// It closes the channel when it is done.
|
||||||
s.spawn(|_| {
|
s.spawn(|_| {
|
||||||
@ -94,9 +94,11 @@ pub fn run_tests(config: Config) -> Result<()> {
|
|||||||
drop(submit);
|
drop(submit);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let mut threads = vec![];
|
||||||
|
|
||||||
// Create N worker threads that receive files to test.
|
// Create N worker threads that receive files to test.
|
||||||
for _ in 0..std::thread::available_parallelism().unwrap().get() {
|
for _ in 0..std::thread::available_parallelism().unwrap().get() {
|
||||||
s.spawn(|_| -> Result<()> {
|
threads.push(s.spawn(|_| -> Result<()> {
|
||||||
for path in &receive {
|
for path in &receive {
|
||||||
if !config.path_filter.is_empty() {
|
if !config.path_filter.is_empty() {
|
||||||
let path_display = path.display().to_string();
|
let path_display = path.display().to_string();
|
||||||
@ -145,10 +147,14 @@ pub fn run_tests(config: Config) -> Result<()> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
});
|
}));
|
||||||
}
|
}
|
||||||
|
for thread in threads {
|
||||||
|
thread.join().unwrap()?;
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap()?;
|
||||||
|
|
||||||
// Print all errors in a single thread to show reliable output
|
// Print all errors in a single thread to show reliable output
|
||||||
let failures = failures.into_inner().unwrap();
|
let failures = failures.into_inner().unwrap();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user