better error reporting

This commit is contained in:
elkowar 2021-07-17 17:17:46 +02:00
parent 8405d01303
commit d12d129eb8
No known key found for this signature in database
GPG key ID: E321AD71B1D1F27F
4 changed files with 90 additions and 13 deletions

View file

@ -1,9 +1,10 @@
use eww_config::{ast::*, config::*};
use eww_config::{ast::*, config::*, format_diagnostic::ToDiagnostic};
fn main() {
let mut files = codespan_reporting::files::SimpleFiles::new();
let input = r#"(hi :bar 22 :baz {"hi" asdfasdf * 2} (foo) (baz))"#;
let input = r#"
(hi :bar 22 :baz {(foo == bar ? 12.K : 12)} (foo) (baz))"#;
let file_id = files.add("foo.eww", input);
let ast = eww_config::parse_string(file_id, input);
@ -12,10 +13,12 @@ fn main() {
println!("{:?}", ast);
}
Err(err) => {
let diag = err.pretty_diagnostic(&files);
dbg!(&err);
let diag = err.to_diagnostic(&files);
use codespan_reporting::term;
let config = term::Config::default();
let mut writer = term::termcolor::StandardStream::stderr(term::termcolor::ColorChoice::Always);
term::emit(&mut writer, &term::Config::default(), &files, &diag).unwrap();
term::emit(&mut writer, &config, &files, &diag).unwrap();
}
}
}

View file

@ -30,15 +30,6 @@ impl AstError {
}
}
pub fn pretty_diagnostic(&self, files: &files::SimpleFiles<&str, &str>) -> diagnostic::Diagnostic<usize> {
let diag = diagnostic::Diagnostic::error().with_message(format!("{}", self));
if let Some(span) = self.get_span() {
diag.with_labels(vec![diagnostic::Label::primary(span.2, span.0..span.1)])
} else {
diag
}
}
pub fn from_parse_error(
file_id: usize,
err: lalrpop_util::ParseError<usize, lexer::Token, parse_error::ParseError>,

81
src/format_diagnostic.rs Normal file
View file

@ -0,0 +1,81 @@
use codespan_reporting::{diagnostic, files};
use simplexpr::dynval;
use crate::{ast::Span, error::AstError, parse_error};
use diagnostic::*;
fn span_to_label(span: Span) -> Label<usize> {
Label::primary(span.2, span.0..span.1)
}
pub trait ToDiagnostic {
fn to_diagnostic(&self, files: &files::SimpleFiles<&str, &str>) -> Diagnostic<usize>;
}
impl ToDiagnostic for AstError {
fn to_diagnostic(&self, files: &files::SimpleFiles<&str, &str>) -> Diagnostic<usize> {
let diag = Diagnostic::error();
if let Some(span) = self.get_span() {
use lalrpop_util::ParseError::*;
match self {
AstError::InvalidDefinition(_) => todo!(),
AstError::MissingNode(_, expected) => diag
.with_message(format!("Missing {}", expected))
.with_labels(vec![span_to_label(span).with_message(format!("Expected `{}` here", expected))]),
AstError::WrongExprType(_, expected, actual) => diag
.with_message("Wrong type of expression")
.with_notes(vec![format!("Expected: {}\nGot: {}", expected, actual)])
.with_labels(vec![span_to_label(span).with_message(format!("Expected a `{}` here", expected))]),
AstError::ParseError { file_id, source } => {
lalrpop_error_to_diagnostic(source, diag, span, move |diag, error| match error {
parse_error::ParseError::SimplExpr(_, error) => simplexpr_error_to_diagnostic(error, diag, span),
parse_error::ParseError::LexicalError(_) => diag
.with_message("Invalid token")
.with_labels(vec![span_to_label(span).with_message("Invalid token")]),
})
}
}
} else {
diag.with_message(format!("{}", self))
}
}
}
fn lalrpop_error_to_diagnostic<T: std::fmt::Display, E>(
error: &lalrpop_util::ParseError<usize, T, E>,
diag: Diagnostic<usize>,
span: Span,
handle_user_error: impl FnOnce(Diagnostic<usize>, &E) -> Diagnostic<usize>,
) -> Diagnostic<usize> {
use lalrpop_util::ParseError::*;
match error {
InvalidToken { location } => diag.with_message("Invalid token").with_labels(vec![span_to_label(span)]),
UnrecognizedEOF { location, expected } => diag
.with_message("Input ended unexpectedly. Check if you have any unclosed delimiters")
.with_labels(vec![span_to_label(span)]),
UnrecognizedToken { token, expected } => diag
.with_message(format!("Unexpected token `{}` encoutered", token.1))
.with_labels(vec![span_to_label(span).with_message("Token unexpected")]),
ExtraToken { token } => diag.with_message(format!("Extra token encountered: `{}`", token.1)),
User { error } => handle_user_error(diag, error),
}
}
fn simplexpr_error_to_diagnostic(error: &simplexpr::error::Error, diag: Diagnostic<usize>, span: Span) -> Diagnostic<usize> {
match error {
simplexpr::error::Error::ParseError { source } => lalrpop_error_to_diagnostic(source, diag, span, move |diag, error| {
diag.with_message("Invalid token").with_labels(vec![span_to_label(span).with_message("Invalid token")])
}),
simplexpr::error::Error::ConversionError(dynval::ConversionError { value, target_type, source }) => diag
.with_message(format!("{}", error))
.with_labels(vec![span_to_label(span).with_message(format!("{} is not of type `{}`", value, target_type))])
.with_notes(source.as_ref().map(|x| vec![format!("{}", x)]).unwrap_or_default()),
simplexpr::error::Error::Spanned(..) => todo!(),
simplexpr::error::Error::Eval(error) => diag.with_message(format!("{}", error)).with_labels(vec![span_to_label(span)]),
simplexpr::error::Error::Other(error) => diag.with_message(format!("{}", error)).with_labels(vec![span_to_label(span)]),
}
}

View file

@ -5,9 +5,11 @@
pub mod ast;
pub mod config;
pub mod error;
pub mod format_diagnostic;
mod lexer;
mod parse_error;
pub mod value;
use ast::Ast;
use error::{AstError, AstResult};