cranelift-isle: Rewrite error reporting (#5318)

There were several issues with ISLE's existing error reporting
implementation.

- When using Miette for more readable error reports, it would panic if
  errors were reported from multiple files in the same run.
- Miette is pretty heavy-weight for what we're doing, with a lot of
  dependencies.
- The `Error::Errors` enum variant led to normalization steps in many
  places, to avoid using that variant to represent a single error.

This commit:
- replaces Miette with codespan-reporting
- gets rid of a bunch of cargo-vet exemptions
- replaces the `Error::Errors` variant with a new `Errors` type
- removes source info from `Error` variants so they're easy to construct
- adds source info only when formatting `Errors`
- formats `Errors` with a custom `Debug` impl
- shares common code between ISLE's callers, islec and cranelift-codegen
- includes a source snippet even with fancy-errors disabled

I tried to make this a series of smaller commits but I couldn't find any
good split points; everything was too entangled with everything else.
This commit is contained in:
Jamey Sharp
2022-11-23 14:20:48 -08:00
committed by GitHub
parent 48ee42efc2
commit 044b57f334
18 changed files with 342 additions and 548 deletions

View File

@@ -15,6 +15,7 @@
// current directory is used to find the sources.
use cranelift_codegen_meta as meta;
use cranelift_isle::error::Errors;
use std::env;
use std::io::Read;
@@ -288,13 +289,16 @@ fn build_isle(
}
if let Err(e) = run_compilation(compilation) {
eprintln!("Error building ISLE files: {:?}", e);
let mut source = e.source();
while let Some(e) = source {
eprintln!("{:?}", e);
source = e.source();
}
had_error = true;
eprintln!("Error building ISLE files:");
eprintln!("{:?}", e);
#[cfg(not(feature = "isle-errors"))]
{
eprintln!("To see a more detailed error report, run: ");
eprintln!();
eprintln!(" $ cargo check -p cranelift-codegen --features isle-errors");
eprintln!();
}
}
}
@@ -311,21 +315,16 @@ fn build_isle(
///
/// NB: This must happen *after* the `cranelift-codegen-meta` functions, since
/// it consumes files generated by them.
fn run_compilation(
compilation: &IsleCompilation,
) -> Result<(), Box<dyn std::error::Error + 'static>> {
fn run_compilation(compilation: &IsleCompilation) -> Result<(), Errors> {
use cranelift_isle as isle;
eprintln!("Rebuilding {}", compilation.output.display());
let code = (|| {
let lexer = isle::lexer::Lexer::from_files(
compilation
.inputs
.iter()
.chain(compilation.untracked_inputs.iter()),
)?;
let defs = isle::parser::parse(lexer)?;
let code = {
let file_paths = compilation
.inputs
.iter()
.chain(compilation.untracked_inputs.iter());
let mut options = isle::codegen::CodegenOptions::default();
// Because we include!() the generated ISLE source, we cannot
@@ -335,62 +334,8 @@ fn run_compilation(
// https://github.com/rust-lang/rust/issues/47995.)
options.exclude_global_allow_pragmas = true;
isle::compile::compile(&defs, &options)
})()
.map_err(|e| {
// Make sure to include the source snippets location info along with
// the error messages.
#[cfg(feature = "isle-errors")]
{
let report = miette::Report::new(e);
return DebugReport(report);
struct DebugReport(miette::Report);
impl std::fmt::Display for DebugReport {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.0.handler().debug(&*self.0, f)
}
}
impl std::fmt::Debug for DebugReport {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::Display::fmt(self, f)
}
}
impl std::error::Error for DebugReport {}
}
#[cfg(not(feature = "isle-errors"))]
{
return DebugReport(format!("{}", e));
struct DebugReport(String);
impl std::fmt::Display for DebugReport {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
writeln!(f, "ISLE errors:\n\n{}\n", self.0)?;
writeln!(f, "To see a more detailed error report, run: ")?;
writeln!(f, "")?;
writeln!(
f,
" $ cargo check -p cranelift-codegen --features isle-errors"
)?;
writeln!(f, "")?;
Ok(())
}
}
impl std::fmt::Debug for DebugReport {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
std::fmt::Display::fmt(self, f)
}
}
impl std::error::Error for DebugReport {}
}
})?;
isle::compile::from_files(file_paths, &options)?
};
let code = rustfmt(&code).unwrap_or_else(|e| {
println!(
@@ -404,7 +349,8 @@ fn run_compilation(
"Writing ISLE-generated Rust code to {}",
compilation.output.display()
);
std::fs::write(&compilation.output, code)?;
std::fs::write(&compilation.output, code)
.map_err(|e| Errors::from_io(e, "failed writing output"))?;
Ok(())
}