feat: starting to get pretty error messages
This commit is contained in:
parent
59d7b54473
commit
da89e9902c
|
@ -78,6 +78,7 @@ dependencies = [
|
||||||
"internment",
|
"internment",
|
||||||
"miette",
|
"miette",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
|
"thiserror",
|
||||||
"vec1",
|
"vec1",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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 => {
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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]
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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),
|
||||||
|
|
|
@ -5,7 +5,7 @@ pub type Datum {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Redeemer {
|
pub type Redeemer {
|
||||||
Buy
|
Buy,
|
||||||
Sell
|
Sell
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue