skhtml/macros/src/error.rs

90 lines
2.9 KiB
Rust

use ansi_term::Style;
use lalrpop_util::ParseError::*;
use lexer::Token;
use proc_macro::{Diagnostic, Level};
use proc_macro2::Ident;
pub type ParseError = lalrpop_util::ParseError<usize, Token, HtmlParseError>;
#[derive(Debug)]
pub enum HtmlParseError {
TagMismatch { open: Ident, close: Ident },
}
fn pprint_token(token: &str) -> &str {
match token {
"BraceGroupToken" => "code block",
"LiteralToken" => "literal",
"IdentToken" => "identifier",
a => a,
}
}
fn pprint_tokens(tokens: &[String]) -> String {
let tokens: Vec<&str> = tokens.iter().map(|s| pprint_token(&s)).collect();
if tokens.len() > 1 {
let start = tokens[..tokens.len() - 1].join(", ");
let end = &tokens[tokens.len() - 1];
format!("{} or {}", start, end)
} else {
tokens[0].to_string()
}
}
fn is_in_node_position(tokens: &[String]) -> bool {
use std::collections::HashSet;
let input: HashSet<&str> = tokens.iter().map(String::as_str).collect();
let output: HashSet<&str> = ["\"<\"", "BraceGroupToken", "LiteralToken"]
.iter()
.cloned()
.collect();
input == output
}
pub fn parse_error(input: &[Token], error: &ParseError) -> Diagnostic {
match error {
InvalidToken { location } => {
let loc = &input[*location];
Diagnostic::spanned(loc.span().unstable(), Level::Error, "invalid token")
}
UnrecognizedToken {
token: None,
expected,
} => {
let msg = format!("missing {}", pprint_tokens(&expected));
Diagnostic::spanned(
input[0].span().unstable().join(input[input.len() - 1].span().unstable()).unwrap(),
Level::Error,
"unexpected end of macro",
)
.help(msg)
}
UnrecognizedToken {
token: Some((_, token, _)),
expected,
} => {
let msg = format!("expected {}", pprint_tokens(&expected));
let mut diag = Diagnostic::spanned(token.span().unstable(), Level::Error, msg);
if is_in_node_position(expected) && token.is_ident() {
// special case: you probably meant to quote that text
diag = diag.help(format!(
"text nodes need to be quoted, eg. {}",
Style::new().bold().paint("<p>\"Hello Joe!\"</p>")
))
}
diag
}
ExtraToken {
token: (_, token, _),
} => Diagnostic::spanned(token.span().unstable(), Level::Error, "superfluous token"),
User {
error: HtmlParseError::TagMismatch { open, close },
} => Diagnostic::spanned(
close.span().unstable(),
Level::Error,
format!("expected closing tag '</{}>', found '</{}>'", open, close),
)
.span_help(open.span().unstable(), "opening tag is here:"),
}
}