Display expected patterns/tokens in parse error when applicable.
We've never been using those 'expected' tokens captured during parsing, which is lame because they contain useful information! This is much better than merely showing our infamous "Try removing it!"
This commit is contained in:
parent
d6fd37c80e
commit
2922c0aa6f
|
@ -9,6 +9,30 @@ use std::collections::HashSet;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Diagnostic, thiserror::Error)]
|
#[derive(Debug, Clone, Diagnostic, thiserror::Error)]
|
||||||
#[error("{kind}\n")]
|
#[error("{kind}\n")]
|
||||||
|
#[diagnostic(
|
||||||
|
help(
|
||||||
|
"{}",
|
||||||
|
match kind {
|
||||||
|
ErrorKind::Unexpected(..) if !expected.is_empty() => {
|
||||||
|
format!(
|
||||||
|
"I am looking for one of the following patterns:\n{}",
|
||||||
|
expected
|
||||||
|
.iter()
|
||||||
|
.map(|x| format!(
|
||||||
|
"→ {}",
|
||||||
|
x.to_aiken()
|
||||||
|
.if_supports_color(Stdout, |s| s.purple())
|
||||||
|
))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join("\n")
|
||||||
|
)
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
kind.help().map(|x| x.to_string()).unwrap_or_default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)]
|
||||||
pub struct ParseError {
|
pub struct ParseError {
|
||||||
pub kind: ErrorKind,
|
pub kind: ErrorKind,
|
||||||
#[label]
|
#[label]
|
||||||
|
@ -28,6 +52,16 @@ impl ParseError {
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn expected_but_got(expected: Pattern, got: Pattern, span: Span) -> Self {
|
||||||
|
Self {
|
||||||
|
kind: ErrorKind::Unexpected(got),
|
||||||
|
expected: HashSet::from_iter([expected]),
|
||||||
|
span,
|
||||||
|
while_parsing: None,
|
||||||
|
label: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn invalid_assignment_right_hand_side(span: Span) -> Self {
|
pub fn invalid_assignment_right_hand_side(span: Span) -> Self {
|
||||||
Self {
|
Self {
|
||||||
kind: ErrorKind::UnfinishedAssignmentRightHandSide,
|
kind: ErrorKind::UnfinishedAssignmentRightHandSide,
|
||||||
|
@ -163,7 +197,7 @@ pub enum ErrorKind {
|
||||||
UnexpectedEnd,
|
UnexpectedEnd,
|
||||||
|
|
||||||
#[error("{0}")]
|
#[error("{0}")]
|
||||||
#[diagnostic(help("{}", .0.help().unwrap_or_else(|| Box::new(""))))]
|
#[diagnostic(help("{}", .0.help().unwrap_or_else(|| Box::new("")))) ]
|
||||||
Unexpected(Pattern),
|
Unexpected(Pattern),
|
||||||
|
|
||||||
#[error("I discovered an invalid tuple index.")]
|
#[error("I discovered an invalid tuple index.")]
|
||||||
|
@ -221,7 +255,9 @@ pub enum ErrorKind {
|
||||||
HybridNotationInByteArray,
|
HybridNotationInByteArray,
|
||||||
|
|
||||||
#[error("I failed to understand a when clause guard.")]
|
#[error("I failed to understand a when clause guard.")]
|
||||||
#[diagnostic(url("https://aiken-lang.org/language-tour/control-flow#checking-equality-and-ordering-in-patterns"))]
|
#[diagnostic(url(
|
||||||
|
"https://aiken-lang.org/language-tour/control-flow#checking-equality-and-ordering-in-patterns"
|
||||||
|
))]
|
||||||
#[diagnostic(help("{}", formatdoc! {
|
#[diagnostic(help("{}", formatdoc! {
|
||||||
r#"Clause guards are not as capable as standard expressions. While you can combine multiple clauses using '{operator_or}' and '{operator_and}', you can't do any arithmetic in there. They are mainly meant to compare pattern variables to some known constants using simple binary operators.
|
r#"Clause guards are not as capable as standard expressions. While you can combine multiple clauses using '{operator_or}' and '{operator_and}', you can't do any arithmetic in there. They are mainly meant to compare pattern variables to some known constants using simple binary operators.
|
||||||
|
|
||||||
|
@ -278,15 +314,6 @@ pub enum Pattern {
|
||||||
#[error("I found an unexpected token '{0}'.")]
|
#[error("I found an unexpected token '{0}'.")]
|
||||||
#[diagnostic(help("Try removing it!"))]
|
#[diagnostic(help("Try removing it!"))]
|
||||||
Token(Token),
|
Token(Token),
|
||||||
#[error("I found an unexpected literal value.")]
|
|
||||||
#[diagnostic(help("Try removing it!"))]
|
|
||||||
Literal,
|
|
||||||
#[error("I found an unexpected type name.")]
|
|
||||||
#[diagnostic(help("Try removing it!"))]
|
|
||||||
TypeIdent,
|
|
||||||
#[error("I found an unexpected identifier.")]
|
|
||||||
#[diagnostic(help("Try removing it!"))]
|
|
||||||
TermIdent,
|
|
||||||
#[error("I found an unexpected end of input.")]
|
#[error("I found an unexpected end of input.")]
|
||||||
End,
|
End,
|
||||||
#[error("I found a malformed list spread pattern.")]
|
#[error("I found a malformed list spread pattern.")]
|
||||||
|
@ -295,11 +322,6 @@ pub enum Pattern {
|
||||||
#[error("I found an out-of-bound byte literal.")]
|
#[error("I found an out-of-bound byte literal.")]
|
||||||
#[diagnostic(help("Bytes must be between 0-255."))]
|
#[diagnostic(help("Bytes must be between 0-255."))]
|
||||||
Byte,
|
Byte,
|
||||||
#[error("I found an unexpected pattern.")]
|
|
||||||
#[diagnostic(help(
|
|
||||||
"If no label is provided then only variables\nmatching a field name are allowed."
|
|
||||||
))]
|
|
||||||
RecordPunning,
|
|
||||||
#[error("I found an unexpected label.")]
|
#[error("I found an unexpected label.")]
|
||||||
#[diagnostic(help("You can only use labels surrounded by curly braces"))]
|
#[diagnostic(help("You can only use labels surrounded by curly braces"))]
|
||||||
Label,
|
Label,
|
||||||
|
@ -308,11 +330,27 @@ pub enum Pattern {
|
||||||
Discard,
|
Discard,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Pattern {
|
||||||
|
fn to_aiken(&self) -> String {
|
||||||
|
use Pattern::*;
|
||||||
|
match self {
|
||||||
|
Token(tok) => tok.to_string(),
|
||||||
|
Char(c) => c.to_string(),
|
||||||
|
End => "<END OF FILE>".to_string(),
|
||||||
|
Match => "A pattern (a discard, a var, etc...)".to_string(),
|
||||||
|
Byte => "A byte between [0; 255]".to_string(),
|
||||||
|
Label => "A label".to_string(),
|
||||||
|
Discard => "_".to_string(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<char> for Pattern {
|
impl From<char> for Pattern {
|
||||||
fn from(c: char) -> Self {
|
fn from(c: char) -> Self {
|
||||||
Self::Char(c)
|
Self::Char(c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Token> for Pattern {
|
impl From<Token> for Pattern {
|
||||||
fn from(tok: Token) -> Self {
|
fn from(tok: Token) -> Self {
|
||||||
Self::Token(tok)
|
Self::Token(tok)
|
||||||
|
|
|
@ -2,7 +2,7 @@ use crate::{
|
||||||
ast::TraceKind,
|
ast::TraceKind,
|
||||||
expr::UntypedExpr,
|
expr::UntypedExpr,
|
||||||
parser::{
|
parser::{
|
||||||
error::ParseError,
|
error::{ParseError, Pattern},
|
||||||
expr::{string, when::clause},
|
expr::{string, when::clause},
|
||||||
token::Token,
|
token::Token,
|
||||||
},
|
},
|
||||||
|
|
|
@ -183,6 +183,6 @@ impl fmt::Display for Token {
|
||||||
Token::Validator => "validator",
|
Token::Validator => "validator",
|
||||||
Token::Via => "via",
|
Token::Via => "via",
|
||||||
};
|
};
|
||||||
write!(f, "\"{s}\"")
|
write!(f, "{s}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -324,7 +324,7 @@ impl Diagnostic for Error {
|
||||||
"Try moving the shared code to a separate module that the others can depend on\n- {}",
|
"Try moving the shared code to a separate module that the others can depend on\n- {}",
|
||||||
modules.join("\n- ")
|
modules.join("\n- ")
|
||||||
))),
|
))),
|
||||||
Error::Parse { error, .. } => error.kind.help(),
|
Error::Parse { error, .. } => error.help(),
|
||||||
Error::Type { error, .. } => error.help(),
|
Error::Type { error, .. } => error.help(),
|
||||||
Error::StandardIo(_) => None,
|
Error::StandardIo(_) => None,
|
||||||
Error::MissingManifest { .. } => Some(Box::new(
|
Error::MissingManifest { .. } => Some(Box::new(
|
||||||
|
|
Loading…
Reference in New Issue