feat: starting to get pretty error messages

This commit is contained in:
rvcas 2022-10-11 13:55:38 -04:00 committed by Lucas
parent 59d7b54473
commit da89e9902c
8 changed files with 103 additions and 12 deletions

1
Cargo.lock generated
View File

@ -78,6 +78,7 @@ dependencies = [
"internment", "internment",
"miette", "miette",
"pretty_assertions", "pretty_assertions",
"thiserror",
"vec1", "vec1",
] ]

View File

@ -1,18 +1,80 @@
use std::{io, path::PathBuf}; use std::{
fmt::{Debug, Display},
io,
path::PathBuf,
};
use aiken_lang::error::ParseError; use aiken_lang::error::ParseError;
use miette::{EyreContext, LabeledSpan, MietteHandlerOpts, RgbColors, SourceCode};
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Debug, thiserror::Error, miette::Diagnostic)] #[derive(thiserror::Error)]
pub enum Error { pub enum Error {
#[error("file operation failed")] #[error("file operation failed")]
FileIo { path: PathBuf, error: io::Error }, FileIo { path: PathBuf, error: io::Error },
#[error("failed to parse Aiken source code")] #[error("failed to parse")]
Parse { Parse {
path: PathBuf, path: PathBuf,
src: String, src: String,
#[source]
error: Box<ParseError>, error: Box<ParseError>,
}, },
#[error("list of errors")] /// Useful for returning many [`Error::Parse`] at once
#[error("a list of errors")]
List(Vec<Self>), List(Vec<Self>),
} }
impl Debug for Error {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let miette_handler = MietteHandlerOpts::new()
// For better support of terminal themes use the ANSI coloring
.rgb_colors(RgbColors::Never)
// If ansi support is disabled in the config disable the eye-candy
.color(true)
.unicode(true)
.terminal_links(true)
.build();
// Ignore error to prevent format! panics. This can happen if span points at some
// inaccessible location, for example by calling `report_error()` with wrong working set.
let _ = miette_handler.debug(self, f);
Ok(())
}
}
impl miette::Diagnostic for Error {
fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
match self {
Error::Parse { .. } => Some(Box::new("aiken::parser".to_string())),
Error::FileIo { .. } => None,
Error::List(_) => None,
}
}
fn source_code(&self) -> Option<&dyn SourceCode> {
match self {
Error::Parse { src, .. } => Some(src),
Error::FileIo { .. } => None,
Error::List(_) => None,
}
}
fn labels(&self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + '_>> {
match self {
Error::Parse { error, .. } => error.labels(),
Error::FileIo { .. } => None,
Error::List(_) => None,
}
}
fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> {
match self {
Error::Parse { error, .. } => error.kind.help(),
Error::FileIo { .. } => None,
Error::List(_) => None,
}
}
}

View File

@ -16,13 +16,15 @@ use uplc::{
}, },
}; };
use aiken::{config::Config, project::Project}; use aiken::{config::Config, error, project::Project};
mod args; mod args;
use args::{Args, TxCommand, UplcCommand}; use args::{Args, TxCommand, UplcCommand};
fn main() -> miette::Result<()> { fn main() -> miette::Result<()> {
miette::set_panic_hook();
let args = Args::default(); let args = Args::default();
match args { match args {
@ -47,7 +49,16 @@ fn main() -> miette::Result<()> {
let mut project = Project::new(config, project_path); let mut project = Project::new(config, project_path);
project.build()?; if let Err(err) = project.build() {
match err {
error::Error::List(errors) => {
for error in errors {
eprintln!("Error: {:?}", error)
}
}
rest => Err(rest)?,
}
};
} }
Args::Dev => { Args::Dev => {

View File

@ -17,6 +17,7 @@ pub struct Source {
} }
#[derive(Debug)] #[derive(Debug)]
#[allow(dead_code)]
struct ParsedModule { struct ParsedModule {
path: PathBuf, path: PathBuf,
name: String, name: String,

View File

@ -14,6 +14,7 @@ authors = ["Lucas Rosa <x@rvcas.dev>", "Kasey White <kwhitemsg@gmail.com>"]
chumsky = "0.8.0" chumsky = "0.8.0"
internment = "0.7.0" internment = "0.7.0"
miette = "5.2.0" miette = "5.2.0"
thiserror = "1.0.37"
vec1 = "1.8.0" vec1 = "1.8.0"
[dev-dependencies] [dev-dependencies]

View File

@ -498,6 +498,12 @@ pub struct Span {
pub end: usize, pub end: usize,
} }
impl From<Span> for miette::SourceSpan {
fn from(span: Span) -> Self {
Self::new(span.start.into(), span.end.into())
}
}
impl Span { impl Span {
pub fn empty() -> Self { pub fn empty() -> Self {
use chumsky::Span; use chumsky::Span;

View File

@ -1,11 +1,15 @@
use std::{collections::HashSet, fmt}; use std::{collections::HashSet, fmt};
use miette::Diagnostic;
use crate::{ast::Span, token::Token}; use crate::{ast::Span, token::Token};
#[derive(Debug)] #[derive(Debug, Diagnostic, thiserror::Error)]
#[error("{}", .kind)]
pub struct ParseError { pub struct ParseError {
kind: ErrorKind, pub kind: ErrorKind,
span: Span, #[label]
pub span: Span,
#[allow(dead_code)] #[allow(dead_code)]
while_parsing: Option<(Span, &'static str)>, while_parsing: Option<(Span, &'static str)>,
expected: HashSet<Pattern>, expected: HashSet<Pattern>,
@ -63,19 +67,24 @@ impl<T: Into<Pattern>> chumsky::Error<T> for ParseError {
} }
} }
#[derive(Debug, PartialEq, Eq)] #[derive(Debug, PartialEq, Eq, Diagnostic, thiserror::Error)]
pub enum ErrorKind { pub enum ErrorKind {
#[error("unexpected end")]
UnexpectedEnd, UnexpectedEnd,
#[error("unexpected {0}")]
#[diagnostic(help("try removing it"))]
Unexpected(Pattern), Unexpected(Pattern),
#[error("unclosed {start}")]
Unclosed { Unclosed {
start: Pattern, start: Pattern,
before_span: Span, before_span: Span,
before: Option<Pattern>, before: Option<Pattern>,
}, },
#[error("no end branch")]
NoEndBranch, NoEndBranch,
} }
#[derive(Debug, PartialEq, Eq, Hash)] #[derive(Debug, PartialEq, Eq, Hash, Diagnostic, thiserror::Error)]
pub enum Pattern { pub enum Pattern {
Char(char), Char(char),
Token(Token), Token(Token),

View File

@ -5,7 +5,7 @@ pub type Datum {
} }
pub type Redeemer { pub type Redeemer {
Buy Buy,
Sell Sell
} }