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:
105
Cargo.lock
generated
105
Cargo.lock
generated
@@ -443,6 +443,16 @@ dependencies = [
|
||||
"os_str_bytes",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "codespan-reporting"
|
||||
version = "0.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e"
|
||||
dependencies = [
|
||||
"termcolor",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "component-fuzz-util"
|
||||
version = "0.0.0"
|
||||
@@ -544,7 +554,6 @@ dependencies = [
|
||||
"gimli",
|
||||
"hashbrown",
|
||||
"log",
|
||||
"miette",
|
||||
"regalloc2",
|
||||
"serde",
|
||||
"sha2 0.10.2",
|
||||
@@ -645,8 +654,8 @@ dependencies = [
|
||||
name = "cranelift-isle"
|
||||
version = "0.91.0"
|
||||
dependencies = [
|
||||
"codespan-reporting",
|
||||
"log",
|
||||
"miette",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
@@ -1534,12 +1543,6 @@ dependencies = [
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "is_ci"
|
||||
version = "1.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "616cde7c720bb2bb5824a224687d8f77bfd38922027f01d825cd7453be5099fb"
|
||||
|
||||
[[package]]
|
||||
name = "isle-fuzz"
|
||||
version = "0.0.0"
|
||||
@@ -1557,7 +1560,6 @@ dependencies = [
|
||||
"clap 3.2.8",
|
||||
"cranelift-isle",
|
||||
"env_logger 0.9.0",
|
||||
"miette",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1763,37 +1765,6 @@ dependencies = [
|
||||
"autocfg 1.1.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miette"
|
||||
version = "5.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ec753a43fd71bb5f28751c9ec17fbe89d6d26ca8282d1e1f82f5ac3dbd5581e"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"backtrace",
|
||||
"miette-derive",
|
||||
"once_cell",
|
||||
"owo-colors",
|
||||
"supports-color",
|
||||
"supports-hyperlinks",
|
||||
"supports-unicode",
|
||||
"terminal_size",
|
||||
"textwrap 0.15.0",
|
||||
"thiserror",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miette-derive"
|
||||
version = "5.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fdfc33ea15c5446600f91d319299dd40301614afff7143cdfa9bf4c09da3ca64"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.5.1"
|
||||
@@ -2008,12 +1979,6 @@ version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e22443d1643a904602595ba1cd8f7d896afe56d26712531c5ff73a15b2fbf64"
|
||||
|
||||
[[package]]
|
||||
name = "owo-colors"
|
||||
version = "3.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "decf7381921fea4dcb2549c5667eda59b3ec297ab7e2b5fc33eac69d2e7da87b"
|
||||
|
||||
[[package]]
|
||||
name = "p256"
|
||||
version = "0.9.0"
|
||||
@@ -2681,12 +2646,6 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smawk"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043"
|
||||
|
||||
[[package]]
|
||||
name = "socket2"
|
||||
version = "0.4.4"
|
||||
@@ -2751,34 +2710,6 @@ version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
||||
|
||||
[[package]]
|
||||
name = "supports-color"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4872ced36b91d47bae8a214a683fe54e7078875b399dfa251df346c9b547d1f9"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"is_ci",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "supports-hyperlinks"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "590b34f7c5f01ecc9d78dba4b3f445f31df750a67621cf31626f3b7441ce6406"
|
||||
dependencies = [
|
||||
"atty",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "supports-unicode"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8b945e45b417b125a8ec51f1b7df2f8df7920367700d1f98aedd21e5735f8b2"
|
||||
dependencies = [
|
||||
"atty",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "symbolic_expressions"
|
||||
version = "5.0.3"
|
||||
@@ -2896,11 +2827,6 @@ name = "textwrap"
|
||||
version = "0.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b1141d4d61095b28419e22cb0bbf02755f5e54e0526f97f1e3d1d160e60885fb"
|
||||
dependencies = [
|
||||
"smawk",
|
||||
"unicode-linebreak",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
@@ -3051,15 +2977,6 @@ version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-linebreak"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a52dcaab0c48d931f7cc8ef826fa51690a08e1ea55117ef26f89864f532383f"
|
||||
dependencies = [
|
||||
"regex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
version = "0.1.21"
|
||||
|
||||
@@ -40,7 +40,6 @@ criterion = "0.3"
|
||||
[build-dependencies]
|
||||
cranelift-codegen-meta = { path = "meta", version = "0.91.0" }
|
||||
cranelift-isle = { path = "../isle/isle", version = "=0.91.0" }
|
||||
miette = { version = "5.1.0", features = ["fancy"], optional = true }
|
||||
|
||||
[features]
|
||||
default = ["std", "unwind"]
|
||||
@@ -101,7 +100,7 @@ incremental-cache = [
|
||||
souper-harvest = ["souper-ir", "souper-ir/stringify"]
|
||||
|
||||
# Provide fancy Miette-produced errors for ISLE.
|
||||
isle-errors = ["miette", "cranelift-isle/miette-errors"]
|
||||
isle-errors = ["cranelift-isle/fancy-errors"]
|
||||
|
||||
# Put ISLE generated files in isle_generated_code/, for easier
|
||||
# inspection, rather than inside of target/.
|
||||
|
||||
@@ -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
|
||||
let code = {
|
||||
let file_paths = compilation
|
||||
.inputs
|
||||
.iter()
|
||||
.chain(compilation.untracked_inputs.iter()),
|
||||
)?;
|
||||
let defs = isle::parser::parse(lexer)?;
|
||||
.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(())
|
||||
}
|
||||
|
||||
@@ -9,8 +9,8 @@ repository = "https://github.com/bytecodealliance/wasmtime/tree/main/cranelift/i
|
||||
version = "0.91.0"
|
||||
|
||||
[dependencies]
|
||||
codespan-reporting = { version = "0.11.1", optional = true }
|
||||
log = { workspace = true, optional = true }
|
||||
miette = { version = "5.1.0", optional = true }
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3"
|
||||
@@ -19,4 +19,4 @@ tempfile = "3"
|
||||
default = []
|
||||
|
||||
logging = ["log"]
|
||||
miette-errors = ["miette"]
|
||||
fancy-errors = ["codespan-reporting"]
|
||||
|
||||
@@ -1,13 +1,25 @@
|
||||
//! Compilation process, from AST to Sema to Sequences of Insts.
|
||||
|
||||
use crate::error::Result;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::error::Errors;
|
||||
use crate::{ast, codegen, sema, trie};
|
||||
|
||||
/// Compile the given AST definitions into Rust source code.
|
||||
pub fn compile(defs: &ast::Defs, options: &codegen::CodegenOptions) -> Result<String> {
|
||||
pub fn compile(defs: &ast::Defs, options: &codegen::CodegenOptions) -> Result<String, Errors> {
|
||||
let mut typeenv = sema::TypeEnv::from_ast(defs)?;
|
||||
let termenv = sema::TermEnv::from_ast(&mut typeenv, defs)?;
|
||||
crate::overlap::check(&mut typeenv, &termenv)?;
|
||||
let tries = trie::build_tries(&termenv);
|
||||
Ok(codegen::codegen(&typeenv, &termenv, &tries, options))
|
||||
}
|
||||
|
||||
/// Compile the given files into Rust source code.
|
||||
pub fn from_files<P: AsRef<Path>>(
|
||||
inputs: impl IntoIterator<Item = P>,
|
||||
options: &codegen::CodegenOptions,
|
||||
) -> Result<String, Errors> {
|
||||
let lexer = crate::lexer::Lexer::from_files(inputs)?;
|
||||
let defs = crate::parser::parse(lexer)?;
|
||||
compile(&defs, options)
|
||||
}
|
||||
|
||||
@@ -4,16 +4,75 @@ use std::sync::Arc;
|
||||
|
||||
use crate::lexer::Pos;
|
||||
|
||||
/// Either `Ok(T)` or `Err(isle::Error)`.
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
/// A collection of errors from attempting to compile some ISLE source files.
|
||||
pub struct Errors {
|
||||
/// The individual errors.
|
||||
pub errors: Vec<Error>,
|
||||
pub(crate) filenames: Vec<Arc<str>>,
|
||||
pub(crate) file_texts: Vec<Arc<str>>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Errors {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
if self.errors.is_empty() {
|
||||
return Ok(());
|
||||
}
|
||||
let diagnostics = Vec::from_iter(self.errors.iter().map(|e| {
|
||||
let message = match e {
|
||||
Error::IoError { context, .. } => format!("{}", context),
|
||||
Error::ParseError { msg, .. } => format!("parse error: {}", msg),
|
||||
Error::TypeError { msg, .. } => format!("type error: {}", msg),
|
||||
Error::UnreachableError { msg, .. } => format!("unreachable rule: {}", msg),
|
||||
Error::OverlapError { msg, .. } => format!("overlap error: {}", msg),
|
||||
};
|
||||
|
||||
let labels = match e {
|
||||
Error::IoError { .. } => vec![],
|
||||
|
||||
Error::ParseError { span, .. }
|
||||
| Error::TypeError { span, .. }
|
||||
| Error::UnreachableError { span, .. } => {
|
||||
vec![Label::primary(span.from.file, span)]
|
||||
}
|
||||
|
||||
Error::OverlapError { rules, .. } => {
|
||||
let mut labels = vec![Label::primary(rules[0].from.file, &rules[0])];
|
||||
labels.extend(
|
||||
rules[1..]
|
||||
.iter()
|
||||
.map(|span| Label::secondary(span.from.file, span)),
|
||||
);
|
||||
labels
|
||||
}
|
||||
};
|
||||
|
||||
let mut sources = Vec::new();
|
||||
let mut source = e.source();
|
||||
while let Some(e) = source {
|
||||
sources.push(format!("{:?}", e));
|
||||
source = std::error::Error::source(e);
|
||||
}
|
||||
|
||||
Diagnostic::error()
|
||||
.with_message(message)
|
||||
.with_labels(labels)
|
||||
.with_notes(sources)
|
||||
}));
|
||||
self.emit(f, diagnostics)?;
|
||||
if self.errors.len() > 1 {
|
||||
writeln!(f, "found {} errors", self.errors.len())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Errors produced by ISLE.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
/// An I/O error.
|
||||
IoError {
|
||||
/// The underlying I/O error.
|
||||
error: Arc<std::io::Error>,
|
||||
error: std::io::Error,
|
||||
/// The context explaining what caused the I/O error.
|
||||
context: String,
|
||||
},
|
||||
@@ -23,9 +82,6 @@ pub enum Error {
|
||||
/// The error message.
|
||||
msg: String,
|
||||
|
||||
/// The input ISLE source.
|
||||
src: Source,
|
||||
|
||||
/// The location of the parse error.
|
||||
span: Span,
|
||||
},
|
||||
@@ -35,9 +91,6 @@ pub enum Error {
|
||||
/// The error message.
|
||||
msg: String,
|
||||
|
||||
/// The input ISLE source.
|
||||
src: Source,
|
||||
|
||||
/// The location of the type error.
|
||||
span: Span,
|
||||
},
|
||||
@@ -47,9 +100,6 @@ pub enum Error {
|
||||
/// The error message.
|
||||
msg: String,
|
||||
|
||||
/// The input ISLE source.
|
||||
src: Source,
|
||||
|
||||
/// The location of the unreachable rule.
|
||||
span: Span,
|
||||
},
|
||||
@@ -62,153 +112,98 @@ pub enum Error {
|
||||
/// The locations of all the rules that overlap. When there are more than two rules
|
||||
/// present, the first rule is the one with the most overlaps (likely a fall-through
|
||||
/// wildcard case).
|
||||
rules: Vec<(Source, Span)>,
|
||||
rules: Vec<Span>,
|
||||
},
|
||||
|
||||
/// Multiple errors.
|
||||
Errors(Vec<Error>),
|
||||
}
|
||||
|
||||
impl Error {
|
||||
/// Create a `isle::Error` from the given I/O error and context.
|
||||
impl Errors {
|
||||
/// Create `isle::Errors` from the given I/O error and context.
|
||||
pub fn from_io(error: std::io::Error, context: impl Into<String>) -> Self {
|
||||
Error::IoError {
|
||||
error: Arc::new(error),
|
||||
Errors {
|
||||
errors: vec![Error::IoError {
|
||||
error,
|
||||
context: context.into(),
|
||||
}],
|
||||
filenames: Vec::new(),
|
||||
file_texts: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Vec<Error>> for Error {
|
||||
fn from(es: Vec<Error>) -> Self {
|
||||
Error::Errors(es)
|
||||
#[cfg(feature = "fancy-errors")]
|
||||
fn emit(
|
||||
&self,
|
||||
f: &mut std::fmt::Formatter,
|
||||
diagnostics: Vec<Diagnostic<usize>>,
|
||||
) -> std::fmt::Result {
|
||||
use codespan_reporting::term::termcolor;
|
||||
let w = termcolor::BufferWriter::stderr(termcolor::ColorChoice::Auto);
|
||||
let mut b = w.buffer();
|
||||
let mut files = codespan_reporting::files::SimpleFiles::new();
|
||||
for (name, source) in self.filenames.iter().zip(self.file_texts.iter()) {
|
||||
files.add(name, source);
|
||||
}
|
||||
for diagnostic in diagnostics {
|
||||
codespan_reporting::term::emit(&mut b, &Default::default(), &files, &diagnostic)
|
||||
.map_err(|_| std::fmt::Error)?;
|
||||
}
|
||||
let b = b.into_inner();
|
||||
let b = std::str::from_utf8(&b).map_err(|_| std::fmt::Error)?;
|
||||
f.write_str(b)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "fancy-errors"))]
|
||||
fn emit(
|
||||
&self,
|
||||
f: &mut std::fmt::Formatter,
|
||||
diagnostics: Vec<Diagnostic<usize>>,
|
||||
) -> std::fmt::Result {
|
||||
let line_starts: Vec<Vec<_>> = self
|
||||
.file_texts
|
||||
.iter()
|
||||
.map(|text| {
|
||||
let mut end = 0;
|
||||
text.split_inclusive('\n')
|
||||
.map(|line| {
|
||||
let start = end;
|
||||
end += line.len();
|
||||
start
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.collect();
|
||||
let pos = |file_id: usize, offset| {
|
||||
let starts = &line_starts[file_id];
|
||||
let line = starts.partition_point(|&start| start <= offset);
|
||||
let text = &self.file_texts[file_id];
|
||||
let line_range = starts[line - 1]..starts.get(line).copied().unwrap_or(text.len());
|
||||
let col = offset - line_range.start + 1;
|
||||
format!(
|
||||
"{}:{}:{}: {}",
|
||||
self.filenames[file_id], line, col, &text[line_range]
|
||||
)
|
||||
};
|
||||
for diagnostic in diagnostics {
|
||||
writeln!(f, "{}", diagnostic.message)?;
|
||||
for label in diagnostic.labels {
|
||||
f.write_str(&pos(label.file_id, label.range.start))?;
|
||||
}
|
||||
for note in diagnostic.notes {
|
||||
writeln!(f, "{}", note)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Error {
|
||||
fn unwrap_errors(&self) -> &[Error] {
|
||||
match self {
|
||||
Error::Errors(e) => e,
|
||||
_ => panic!("`isle::Error::unwrap_errors` on non-`isle::Error::Errors`"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Error {
|
||||
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
|
||||
match self {
|
||||
Error::IoError { error, .. } => Some(&*error as &dyn std::error::Error),
|
||||
Error::IoError { error, .. } => Some(error),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Error {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match self {
|
||||
Error::IoError { context, .. } => write!(f, "{}", context),
|
||||
|
||||
// Include locations directly in the `Display` output when
|
||||
// we're not wrapping errors with miette (which provides
|
||||
// its own way of showing locations and context).
|
||||
#[cfg(not(feature = "miette-errors"))]
|
||||
Error::ParseError { src, span, msg, .. } => write!(
|
||||
f,
|
||||
"{}: parse error: {}",
|
||||
span.from.pretty_print_with_filename(&*src.name),
|
||||
msg
|
||||
),
|
||||
#[cfg(not(feature = "miette-errors"))]
|
||||
Error::TypeError { src, span, msg, .. } => write!(
|
||||
f,
|
||||
"{}: type error: {}",
|
||||
span.from.pretty_print_with_filename(&*src.name),
|
||||
msg
|
||||
),
|
||||
|
||||
#[cfg(feature = "miette-errors")]
|
||||
Error::ParseError { msg, .. } => write!(f, "parse error: {}", msg),
|
||||
#[cfg(feature = "miette-errors")]
|
||||
Error::TypeError { msg, .. } => write!(f, "type error: {}", msg),
|
||||
|
||||
Error::UnreachableError { src, span, msg } => {
|
||||
write!(
|
||||
f,
|
||||
"{}: unreachable rule: {}",
|
||||
span.from.pretty_print_with_filename(&*src.name),
|
||||
msg
|
||||
)
|
||||
}
|
||||
|
||||
Error::OverlapError { msg, rules, .. } => {
|
||||
writeln!(f, "overlap error: {}\n{}", msg, OverlappingRules(&rules))
|
||||
}
|
||||
|
||||
Error::Errors(_) => write!(
|
||||
f,
|
||||
"{}found {} errors",
|
||||
DisplayErrors(self.unwrap_errors()),
|
||||
self.unwrap_errors().len(),
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct DisplayErrors<'a>(&'a [Error]);
|
||||
impl std::fmt::Display for DisplayErrors<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
for e in self.0 {
|
||||
writeln!(f, "{}", e)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct OverlappingRules<'a>(&'a [(Source, Span)]);
|
||||
impl std::fmt::Display for OverlappingRules<'_> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
for (src, span) in self.0 {
|
||||
writeln!(f, " {}", span.from.pretty_print_with_filename(&*src.name))?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// A source file and its contents.
|
||||
#[derive(Clone)]
|
||||
pub struct Source {
|
||||
/// The name of this source file.
|
||||
pub name: Arc<str>,
|
||||
/// The text of this source file.
|
||||
#[allow(unused)] // Used only when miette is enabled.
|
||||
pub text: Arc<str>,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Source {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("Source")
|
||||
.field("name", &self.name)
|
||||
.field("source", &"<redacted>");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Source {
|
||||
pub(crate) fn new(name: Arc<str>, text: Arc<str>) -> Self {
|
||||
Self { name, text }
|
||||
}
|
||||
|
||||
/// Get this source's file name.
|
||||
pub fn name(&self) -> &Arc<str> {
|
||||
&self.name
|
||||
}
|
||||
|
||||
/// Get this source's text contents.
|
||||
pub fn text(&self) -> &Arc<str> {
|
||||
&self.name
|
||||
}
|
||||
}
|
||||
|
||||
/// A span in a given source.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Span {
|
||||
@@ -237,3 +232,69 @@ impl Span {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Span> for std::ops::Range<usize> {
|
||||
fn from(span: &Span) -> Self {
|
||||
span.from.offset..span.to.offset
|
||||
}
|
||||
}
|
||||
|
||||
use diagnostic::{Diagnostic, Label};
|
||||
|
||||
#[cfg(feature = "fancy-errors")]
|
||||
use codespan_reporting::diagnostic;
|
||||
|
||||
#[cfg(not(feature = "fancy-errors"))]
|
||||
/// Minimal versions of types from codespan-reporting.
|
||||
mod diagnostic {
|
||||
use std::ops::Range;
|
||||
|
||||
pub struct Diagnostic<FileId> {
|
||||
pub message: String,
|
||||
pub labels: Vec<Label<FileId>>,
|
||||
pub notes: Vec<String>,
|
||||
}
|
||||
|
||||
impl<FileId> Diagnostic<FileId> {
|
||||
pub fn error() -> Self {
|
||||
Self {
|
||||
message: String::new(),
|
||||
labels: Vec::new(),
|
||||
notes: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_message(mut self, message: impl Into<String>) -> Self {
|
||||
self.message = message.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_labels(mut self, labels: Vec<Label<FileId>>) -> Self {
|
||||
self.labels = labels;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_notes(mut self, notes: Vec<String>) -> Self {
|
||||
self.notes = notes;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Label<FileId> {
|
||||
pub file_id: FileId,
|
||||
pub range: Range<usize>,
|
||||
}
|
||||
|
||||
impl<FileId> Label<FileId> {
|
||||
pub fn primary(file_id: FileId, range: impl Into<Range<usize>>) -> Self {
|
||||
Self {
|
||||
file_id,
|
||||
range: range.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn secondary(file_id: FileId, range: impl Into<Range<usize>>) -> Self {
|
||||
Self::primary(file_id, range)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
//! miette-specific trait implementations. This is kept separate so
|
||||
//! that we can have a very lightweight build of the ISLE compiler as
|
||||
//! part of the Cranelift build process without pulling in any
|
||||
//! dependencies.
|
||||
|
||||
use crate::error::{Error, Source, Span};
|
||||
use miette::{SourceCode, SourceSpan};
|
||||
|
||||
impl From<Span> for SourceSpan {
|
||||
fn from(span: Span) -> Self {
|
||||
SourceSpan::new(span.from.offset.into(), span.to.offset.into())
|
||||
}
|
||||
}
|
||||
|
||||
impl SourceCode for Source {
|
||||
fn read_span<'a>(
|
||||
&'a self,
|
||||
span: &SourceSpan,
|
||||
context_lines_before: usize,
|
||||
context_lines_after: usize,
|
||||
) -> std::result::Result<Box<dyn miette::SpanContents<'a> + 'a>, miette::MietteError> {
|
||||
let contents = self
|
||||
.text
|
||||
.read_span(span, context_lines_before, context_lines_after)?;
|
||||
Ok(Box::new(miette::MietteSpanContents::new_named(
|
||||
self.name.to_string(),
|
||||
contents.data(),
|
||||
contents.span().clone(),
|
||||
contents.line(),
|
||||
contents.column(),
|
||||
contents.line_count(),
|
||||
)))
|
||||
}
|
||||
}
|
||||
|
||||
impl miette::Diagnostic for Error {
|
||||
fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
|
||||
match self {
|
||||
Self::ParseError { msg, span, .. } | Self::TypeError { msg, span, .. } => {
|
||||
Some(Box::new(
|
||||
vec![miette::LabeledSpan::new_with_span(
|
||||
Some(msg.clone()),
|
||||
span.clone(),
|
||||
)]
|
||||
.into_iter(),
|
||||
))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
fn source_code(&self) -> std::option::Option<&dyn miette::SourceCode> {
|
||||
match self {
|
||||
Self::ParseError { src, .. } | Self::TypeError { src, .. } => Some(src),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
fn related(&self) -> Option<Box<dyn Iterator<Item = &dyn miette::Diagnostic> + '_>> {
|
||||
match self {
|
||||
Self::Errors(errors) => Some(Box::new(
|
||||
errors.iter().map(|x| x as &dyn miette::Diagnostic),
|
||||
)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +1,12 @@
|
||||
//! Lexer for the ISLE language.
|
||||
|
||||
use crate::error::{Error, Result, Source, Span};
|
||||
use crate::error::{Error, Errors, Span};
|
||||
use std::borrow::Cow;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
|
||||
type Result<T> = std::result::Result<T, Errors>;
|
||||
|
||||
/// The lexer.
|
||||
///
|
||||
/// Breaks source text up into a sequence of tokens (with source positions).
|
||||
@@ -43,19 +45,10 @@ pub struct Pos {
|
||||
}
|
||||
|
||||
impl Pos {
|
||||
/// Print this source position as `file.isle:12:34`.
|
||||
pub fn pretty_print(&self, filenames: &[Arc<str>]) -> String {
|
||||
self.pretty_print_with_filename(&filenames[self.file])
|
||||
}
|
||||
/// Print this source position as `file.isle line 12`.
|
||||
pub fn pretty_print_line(&self, filenames: &[Arc<str>]) -> String {
|
||||
format!("{} line {}", filenames[self.file], self.line)
|
||||
}
|
||||
/// As above for `pretty_print`, but with the specific filename
|
||||
/// already provided.
|
||||
pub fn pretty_print_with_filename(&self, filename: &str) -> String {
|
||||
format!("{}:{}:{}", filename, self.line, self.col)
|
||||
}
|
||||
}
|
||||
|
||||
/// A token of ISLE source.
|
||||
@@ -107,7 +100,7 @@ impl<'a> Lexer<'a> {
|
||||
filenames.push(f.display().to_string().into());
|
||||
|
||||
let s = std::fs::read_to_string(f)
|
||||
.map_err(|e| Error::from_io(e, format!("failed to read file: {}", f.display())))?;
|
||||
.map_err(|e| Errors::from_io(e, format!("failed to read file: {}", f.display())))?;
|
||||
file_texts.push(s.into());
|
||||
}
|
||||
|
||||
@@ -165,14 +158,14 @@ impl<'a> Lexer<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn error(&self, pos: Pos, msg: impl Into<String>) -> Error {
|
||||
Error::ParseError {
|
||||
fn error(&self, pos: Pos, msg: impl Into<String>) -> Errors {
|
||||
Errors {
|
||||
errors: vec![Error::ParseError {
|
||||
msg: msg.into(),
|
||||
src: Source::new(
|
||||
self.filenames[pos.file].clone(),
|
||||
self.file_texts[pos.file].clone(),
|
||||
),
|
||||
span: Span::new_single(self.pos()),
|
||||
span: Span::new_single(pos),
|
||||
}],
|
||||
filenames: self.filenames.clone(),
|
||||
file_texts: self.file_texts.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -211,6 +211,3 @@ pub mod parser;
|
||||
pub mod sema;
|
||||
pub mod trie;
|
||||
pub mod trie_again;
|
||||
|
||||
#[cfg(feature = "miette-errors")]
|
||||
mod error_miette;
|
||||
|
||||
@@ -3,20 +3,24 @@
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
use crate::error::{Error, Result, Source, Span};
|
||||
use crate::error::{self, Error, Span};
|
||||
use crate::lexer::Pos;
|
||||
use crate::sema::{TermEnv, TermId, TermKind, TypeEnv};
|
||||
use crate::trie_again;
|
||||
|
||||
/// Check for overlap.
|
||||
pub fn check(tyenv: &TypeEnv, termenv: &TermEnv) -> Result<()> {
|
||||
let (terms, mut errors) = trie_again::build(termenv, tyenv);
|
||||
errors.append(&mut check_overlaps(terms, termenv).report(tyenv));
|
||||
pub fn check(tyenv: &TypeEnv, termenv: &TermEnv) -> Result<(), error::Errors> {
|
||||
let (terms, mut errors) = trie_again::build(termenv);
|
||||
errors.append(&mut check_overlaps(terms, termenv).report());
|
||||
|
||||
match errors.len() {
|
||||
0 => Ok(()),
|
||||
1 => Err(errors.pop().unwrap()),
|
||||
_ => Err(Error::Errors(errors)),
|
||||
if errors.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(error::Errors {
|
||||
errors,
|
||||
filenames: tyenv.filenames.clone(),
|
||||
file_texts: tyenv.file_texts.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,19 +36,9 @@ impl Errors {
|
||||
/// nodes from the graph with the highest degree, reporting errors for them and their direct
|
||||
/// connections. The goal with reporting errors this way is to prefer reporting rules that
|
||||
/// overlap with many others first, and then report other more targeted overlaps later.
|
||||
fn report(mut self, tyenv: &TypeEnv) -> Vec<Error> {
|
||||
fn report(mut self) -> Vec<Error> {
|
||||
let mut errors = Vec::new();
|
||||
|
||||
let get_info = |pos: Pos| {
|
||||
let file = pos.file;
|
||||
let src = Source::new(
|
||||
tyenv.filenames[file].clone(),
|
||||
tyenv.file_texts[file].clone(),
|
||||
);
|
||||
let span = Span::new_single(pos);
|
||||
(src, span)
|
||||
};
|
||||
|
||||
while let Some((&pos, _)) = self
|
||||
.nodes
|
||||
.iter()
|
||||
@@ -62,9 +56,9 @@ impl Errors {
|
||||
}
|
||||
|
||||
// build the real error
|
||||
let mut rules = vec![get_info(pos)];
|
||||
let mut rules = vec![Span::new_single(pos)];
|
||||
|
||||
rules.extend(node.into_iter().map(get_info));
|
||||
rules.extend(node.into_iter().map(Span::new_single));
|
||||
|
||||
errors.push(Error::OverlapError {
|
||||
msg: String::from("rules are overlapping"),
|
||||
@@ -73,7 +67,7 @@ impl Errors {
|
||||
}
|
||||
|
||||
errors.sort_by_key(|err| match err {
|
||||
Error::OverlapError { rules, .. } => rules.first().unwrap().1.from,
|
||||
Error::OverlapError { rules, .. } => rules.first().unwrap().from,
|
||||
_ => Pos::default(),
|
||||
});
|
||||
errors
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
//! Parser for ISLE language.
|
||||
|
||||
use crate::ast::*;
|
||||
use crate::error::*;
|
||||
use crate::error::{Error, Errors, Span};
|
||||
use crate::lexer::{Lexer, Pos, Token};
|
||||
|
||||
type Result<T> = std::result::Result<T, Errors>;
|
||||
|
||||
/// Parse the top-level ISLE definitions and return their AST.
|
||||
pub fn parse(lexer: Lexer) -> Result<Defs> {
|
||||
let parser = Parser::new(lexer);
|
||||
@@ -32,14 +34,14 @@ impl<'a> Parser<'a> {
|
||||
Parser { lexer }
|
||||
}
|
||||
|
||||
fn error(&self, pos: Pos, msg: String) -> Error {
|
||||
Error::ParseError {
|
||||
fn error(&self, pos: Pos, msg: String) -> Errors {
|
||||
Errors {
|
||||
errors: vec![Error::ParseError {
|
||||
msg,
|
||||
src: Source::new(
|
||||
self.lexer.filenames[pos.file].clone(),
|
||||
self.lexer.file_texts[pos.file].clone(),
|
||||
),
|
||||
span: Span::new_single(pos),
|
||||
}],
|
||||
filenames: self.lexer.filenames.clone(),
|
||||
file_texts: self.lexer.file_texts.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ declare_id!(
|
||||
/// The type environment.
|
||||
///
|
||||
/// Keeps track of which symbols and rules have which types.
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Debug)]
|
||||
pub struct TypeEnv {
|
||||
/// Arena of input ISLE source filenames.
|
||||
///
|
||||
@@ -887,7 +887,7 @@ macro_rules! unwrap_or_continue {
|
||||
|
||||
impl TypeEnv {
|
||||
/// Construct the type environment from the AST.
|
||||
pub fn from_ast(defs: &ast::Defs) -> Result<TypeEnv> {
|
||||
pub fn from_ast(defs: &ast::Defs) -> Result<TypeEnv, Errors> {
|
||||
let mut tyenv = TypeEnv {
|
||||
filenames: defs.filenames.clone(),
|
||||
file_texts: defs.file_texts.clone(),
|
||||
@@ -968,11 +968,15 @@ impl TypeEnv {
|
||||
Ok(tyenv)
|
||||
}
|
||||
|
||||
fn return_errors(&mut self) -> Result<()> {
|
||||
match self.errors.len() {
|
||||
0 => Ok(()),
|
||||
1 => Err(self.errors.pop().unwrap()),
|
||||
_ => Err(Error::Errors(std::mem::take(&mut self.errors))),
|
||||
fn return_errors(&mut self) -> Result<(), Errors> {
|
||||
if self.errors.is_empty() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Errors {
|
||||
errors: std::mem::take(&mut self.errors),
|
||||
filenames: self.filenames.clone(),
|
||||
file_texts: self.file_texts.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1062,16 +1066,10 @@ impl TypeEnv {
|
||||
}
|
||||
|
||||
fn error(&self, pos: Pos, msg: impl Into<String>) -> Error {
|
||||
let e = Error::TypeError {
|
||||
Error::TypeError {
|
||||
msg: msg.into(),
|
||||
src: Source::new(
|
||||
self.filenames[pos.file].clone(),
|
||||
self.file_texts[pos.file].clone(),
|
||||
),
|
||||
span: Span::new_single(pos),
|
||||
};
|
||||
log!("{}", e);
|
||||
e
|
||||
}
|
||||
}
|
||||
|
||||
fn report_error(&mut self, pos: Pos, msg: impl Into<String>) {
|
||||
@@ -1121,7 +1119,7 @@ impl Bindings {
|
||||
|
||||
impl TermEnv {
|
||||
/// Construct the term environment from the AST and the type environment.
|
||||
pub fn from_ast(tyenv: &mut TypeEnv, defs: &ast::Defs) -> Result<TermEnv> {
|
||||
pub fn from_ast(tyenv: &mut TypeEnv, defs: &ast::Defs) -> Result<TermEnv, Errors> {
|
||||
let mut env = TermEnv {
|
||||
terms: vec![],
|
||||
term_map: StableMap::new(),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! A strongly-normalizing intermediate representation for ISLE rules. This representation is chosen
|
||||
//! to closely reflect the operations we can implement in Rust, to make code generation easy.
|
||||
use crate::error::{Error, Source, Span};
|
||||
use crate::error::{Error, Span};
|
||||
use crate::lexer::Pos;
|
||||
use crate::sema;
|
||||
use crate::DisjointSets;
|
||||
@@ -167,16 +167,13 @@ pub struct RuleSet {
|
||||
}
|
||||
|
||||
/// Construct a [RuleSet] for each term in `termenv` that has rules.
|
||||
pub fn build(
|
||||
termenv: &sema::TermEnv,
|
||||
tyenv: &sema::TypeEnv,
|
||||
) -> (Vec<(sema::TermId, RuleSet)>, Vec<Error>) {
|
||||
pub fn build(termenv: &sema::TermEnv) -> (Vec<(sema::TermId, RuleSet)>, Vec<Error>) {
|
||||
let mut errors = Vec::new();
|
||||
let mut term = HashMap::new();
|
||||
for rule in termenv.rules.iter() {
|
||||
term.entry(rule.root_term)
|
||||
.or_insert_with(RuleSetBuilder::default)
|
||||
.add_rule(rule, termenv, tyenv, &mut errors);
|
||||
.add_rule(rule, termenv, &mut errors);
|
||||
}
|
||||
|
||||
// The `term` hash map may return terms in any order. Sort them to ensure that we produce the
|
||||
@@ -282,13 +279,7 @@ struct RuleSetBuilder {
|
||||
}
|
||||
|
||||
impl RuleSetBuilder {
|
||||
fn add_rule(
|
||||
&mut self,
|
||||
rule: &sema::Rule,
|
||||
termenv: &sema::TermEnv,
|
||||
tyenv: &sema::TypeEnv,
|
||||
errors: &mut Vec<Error>,
|
||||
) {
|
||||
fn add_rule(&mut self, rule: &sema::Rule, termenv: &sema::TermEnv, errors: &mut Vec<Error>) {
|
||||
self.current_rule.pos = rule.pos;
|
||||
self.current_rule.prio = rule.prio;
|
||||
self.current_rule.result = rule.visit(self, termenv);
|
||||
@@ -299,20 +290,17 @@ impl RuleSetBuilder {
|
||||
self.rules.rules.push(rule);
|
||||
} else {
|
||||
// If this rule can never match, drop it so it doesn't affect overlap checking.
|
||||
errors.extend(self.unreachable.drain(..).map(|err| {
|
||||
let src = Source::new(
|
||||
tyenv.filenames[err.pos.file].clone(),
|
||||
tyenv.file_texts[err.pos.file].clone(),
|
||||
);
|
||||
Error::UnreachableError {
|
||||
errors.extend(
|
||||
self.unreachable
|
||||
.drain(..)
|
||||
.map(|err| Error::UnreachableError {
|
||||
msg: format!(
|
||||
"rule requires binding to match both {:?} and {:?}",
|
||||
err.constraint_a, err.constraint_b
|
||||
),
|
||||
src,
|
||||
span: Span::new_single(err.pos),
|
||||
}
|
||||
}))
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,16 @@
|
||||
//! Helper for autogenerated unit tests.
|
||||
|
||||
use cranelift_isle::error::Result;
|
||||
use cranelift_isle::{compile, lexer, parser};
|
||||
use cranelift_isle::compile;
|
||||
use cranelift_isle::error::Errors;
|
||||
use std::default::Default;
|
||||
|
||||
fn build(filename: &str) -> Result<String> {
|
||||
let lexer = lexer::Lexer::from_files(vec![filename])?;
|
||||
let defs = parser::parse(lexer)?;
|
||||
compile::compile(&defs, &Default::default())
|
||||
fn build(filename: &str) -> Result<String, Errors> {
|
||||
compile::from_files(&[filename], &Default::default())
|
||||
}
|
||||
|
||||
pub fn run_pass(filename: &str) {
|
||||
if let Err(err) = build(filename) {
|
||||
panic!("pass test failed:\n{}", err);
|
||||
panic!("pass test failed:\n{:?}", err);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ license = "Apache-2.0 WITH LLVM-exception"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
cranelift-isle = { version = "*", path = "../isle/", features = ["miette-errors", "logging"] }
|
||||
cranelift-isle = { version = "*", path = "../isle/", features = ["fancy-errors", "logging"] }
|
||||
env_logger = { workspace = true }
|
||||
miette = { version = "5.1.0", features = ["fancy"] }
|
||||
clap = { workspace = true }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use clap::Parser;
|
||||
use cranelift_isle::{compile, lexer, parser};
|
||||
use miette::{Context, IntoDiagnostic, Result};
|
||||
use cranelift_isle::compile;
|
||||
use cranelift_isle::error::Errors;
|
||||
use std::{
|
||||
default::Default,
|
||||
fs,
|
||||
@@ -20,33 +20,19 @@ struct Opts {
|
||||
inputs: Vec<PathBuf>,
|
||||
}
|
||||
|
||||
fn main() -> Result<()> {
|
||||
fn main() -> Result<(), Errors> {
|
||||
let _ = env_logger::try_init();
|
||||
|
||||
let _ = miette::set_hook(Box::new(|_| {
|
||||
Box::new(
|
||||
miette::MietteHandlerOpts::new()
|
||||
// `miette` mistakenly uses braille-optimized output for emacs's
|
||||
// `M-x shell`.
|
||||
.force_graphical(true)
|
||||
.build(),
|
||||
)
|
||||
}));
|
||||
|
||||
let opts = Opts::parse();
|
||||
|
||||
let lexer = lexer::Lexer::from_files(opts.inputs)?;
|
||||
let defs = parser::parse(lexer)?;
|
||||
let code = compile::compile(&defs, &Default::default())?;
|
||||
let code = compile::from_files(opts.inputs, &Default::default())?;
|
||||
|
||||
let stdout = io::stdout();
|
||||
let (mut output, output_name): (Box<dyn Write>, _) = match &opts.output {
|
||||
Some(f) => {
|
||||
let output = Box::new(
|
||||
fs::File::create(f)
|
||||
.into_diagnostic()
|
||||
.with_context(|| format!("failed to create '{}'", f.display()))?,
|
||||
);
|
||||
let output =
|
||||
Box::new(fs::File::create(f).map_err(|e| {
|
||||
Errors::from_io(e, format!("failed to create '{}'", f.display()))
|
||||
})?);
|
||||
(output, f.display().to_string())
|
||||
}
|
||||
None => {
|
||||
@@ -57,8 +43,7 @@ fn main() -> Result<()> {
|
||||
|
||||
output
|
||||
.write_all(code.as_bytes())
|
||||
.into_diagnostic()
|
||||
.with_context(|| format!("failed to write to '{}'", output_name))?;
|
||||
.map_err(|e| Errors::from_io(e, format!("failed to write to '{}'", output_name)))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -125,6 +125,12 @@ criteria = "safe-to-deploy"
|
||||
version = "1.0.0"
|
||||
notes = "I am the author of this crate."
|
||||
|
||||
[[audits.codespan-reporting]]
|
||||
who = "Jamey Sharp <jsharp@fastly.com>"
|
||||
criteria = "safe-to-deploy"
|
||||
version = "0.11.1"
|
||||
notes = "This library uses `forbid(unsafe_code)` and has no filesystem or network I/O."
|
||||
|
||||
[[audits.criterion]]
|
||||
who = "Alex Crichton <alex@alexcrichton.com>"
|
||||
criteria = "safe-to-run"
|
||||
|
||||
@@ -28,7 +28,7 @@ criteria = "safe-to-deploy"
|
||||
|
||||
[[exemptions.adler]]
|
||||
version = "1.0.2"
|
||||
criteria = "safe-to-deploy"
|
||||
criteria = "safe-to-run"
|
||||
|
||||
[[exemptions.aead]]
|
||||
version = "0.4.3"
|
||||
@@ -394,10 +394,6 @@ criteria = "safe-to-deploy"
|
||||
version = "2.5.0"
|
||||
criteria = "safe-to-deploy"
|
||||
|
||||
[[exemptions.is_ci]]
|
||||
version = "1.1.1"
|
||||
criteria = "safe-to-deploy"
|
||||
|
||||
[[exemptions.itertools]]
|
||||
version = "0.10.3"
|
||||
criteria = "safe-to-deploy"
|
||||
@@ -482,17 +478,9 @@ criteria = "safe-to-deploy"
|
||||
version = "0.6.5"
|
||||
criteria = "safe-to-deploy"
|
||||
|
||||
[[exemptions.miette]]
|
||||
version = "5.1.0"
|
||||
criteria = "safe-to-deploy"
|
||||
|
||||
[[exemptions.miette-derive]]
|
||||
version = "5.1.0"
|
||||
criteria = "safe-to-deploy"
|
||||
|
||||
[[exemptions.miniz_oxide]]
|
||||
version = "0.5.1"
|
||||
criteria = "safe-to-deploy"
|
||||
criteria = "safe-to-run"
|
||||
|
||||
[[exemptions.mio]]
|
||||
version = "0.8.2"
|
||||
@@ -562,10 +550,6 @@ criteria = "safe-to-run"
|
||||
version = "6.0.0"
|
||||
criteria = "safe-to-deploy"
|
||||
|
||||
[[exemptions.owo-colors]]
|
||||
version = "3.4.0"
|
||||
criteria = "safe-to-deploy"
|
||||
|
||||
[[exemptions.p256]]
|
||||
version = "0.9.0"
|
||||
criteria = "safe-to-deploy"
|
||||
@@ -818,10 +802,6 @@ criteria = "safe-to-deploy"
|
||||
version = "1.8.0"
|
||||
criteria = "safe-to-deploy"
|
||||
|
||||
[[exemptions.smawk]]
|
||||
version = "0.3.1"
|
||||
criteria = "safe-to-deploy"
|
||||
|
||||
[[exemptions.socket2]]
|
||||
version = "0.4.4"
|
||||
criteria = "safe-to-deploy"
|
||||
@@ -854,18 +834,6 @@ criteria = "safe-to-deploy"
|
||||
version = "2.4.1"
|
||||
criteria = "safe-to-deploy"
|
||||
|
||||
[[exemptions.supports-color]]
|
||||
version = "1.3.0"
|
||||
criteria = "safe-to-deploy"
|
||||
|
||||
[[exemptions.supports-hyperlinks]]
|
||||
version = "1.2.0"
|
||||
criteria = "safe-to-deploy"
|
||||
|
||||
[[exemptions.supports-unicode]]
|
||||
version = "1.0.2"
|
||||
criteria = "safe-to-deploy"
|
||||
|
||||
[[exemptions.symbolic_expressions]]
|
||||
version = "5.0.3"
|
||||
criteria = "safe-to-run"
|
||||
@@ -946,10 +914,6 @@ criteria = "safe-to-run"
|
||||
version = "1.15.0"
|
||||
criteria = "safe-to-deploy"
|
||||
|
||||
[[exemptions.unicode-linebreak]]
|
||||
version = "0.1.2"
|
||||
criteria = "safe-to-deploy"
|
||||
|
||||
[[exemptions.unicode-width]]
|
||||
version = "0.1.9"
|
||||
criteria = "safe-to-deploy"
|
||||
|
||||
Reference in New Issue
Block a user