Improve parser error reporting.

This commit is contained in:
Bodil Stokke 2018-11-13 14:31:44 +00:00
parent 0db3bfb8bc
commit d25c4d5624
2 changed files with 31 additions and 22 deletions

View File

@ -1,6 +1,7 @@
#![feature(proc_macro_hygiene)] #![feature(proc_macro_hygiene)]
#![feature(proc_macro_quote)] #![feature(proc_macro_quote)]
#![feature(proc_macro_span)] #![feature(proc_macro_span)]
#![feature(proc_macro_diagnostic)]
extern crate pom; extern crate pom;
extern crate proc_macro; extern crate proc_macro;
@ -18,7 +19,10 @@ pub fn html(input: TokenStream) -> TokenStream {
let input: Vec<TokenTree> = input.into_iter().collect(); let input: Vec<TokenTree> = input.into_iter().collect();
let result = html::expand_html(&input); let result = html::expand_html(&input);
match result { match result {
Err(error) => panic!(parser::parse_error(&input, &error)), Err(error) => {
parser::parse_error(&input, &error).emit();
panic!("macro expansion produced errors; see above.")
}
Ok(ts) => ts, Ok(ts) => ts,
} }
} }
@ -28,7 +32,10 @@ pub fn declare_element(input: TokenStream) -> TokenStream {
let input: Vec<TokenTree> = input.into_iter().collect(); let input: Vec<TokenTree> = input.into_iter().collect();
let result = declare::expand_declare(&input); let result = declare::expand_declare(&input);
match result { match result {
Err(error) => panic!(parser::parse_error(&input, &error)), Err(error) => {
parser::parse_error(&input, &error).emit();
panic!("macro expansion produced errors; see above.")
}
Ok(ts) => ts, Ok(ts) => ts,
} }
} }

View File

@ -1,6 +1,8 @@
use pom::combinator::*; use pom::combinator::*;
use pom::{Error, Parser}; use pom::{Error, Parser};
use proc_macro::{Delimiter, Group, Ident, Literal, Punct, TokenStream, TokenTree}; use proc_macro::{
Delimiter, Diagnostic, Group, Ident, Level, Literal, Punct, TokenStream, TokenTree,
};
pub fn unit<'a, I: 'a, A: Clone>(value: A) -> Combinator<impl Parser<'a, I, Output = A>> { pub fn unit<'a, I: 'a, A: Clone>(value: A) -> Combinator<impl Parser<'a, I, Output = A>> {
comb(move |_, start| Ok((value.clone(), start))) comb(move |_, start| Ok((value.clone(), start)))
@ -102,7 +104,7 @@ pub fn html_ident<'a>() -> Combinator<impl Parser<'a, TokenTree, Output = Ident>
let next = punct('-') * ident(); let next = punct('-') * ident();
(start * next.repeat(0..)).collect().map(|stream| { (start * next.repeat(0..)).collect().map(|stream| {
let (span, name) = stream let (span, name) = stream
.into_iter() .iter()
.fold((None, String::new()), |(span, name), token| { .fold((None, String::new()), |(span, name), token| {
( (
match span { match span {
@ -120,39 +122,39 @@ pub fn html_ident<'a>() -> Combinator<impl Parser<'a, TokenTree, Output = Ident>
}) })
} }
fn error_location(input: &[TokenTree], position: usize) -> String { /// Turn a parser error into a proc_macro diagnostic.
format!("{:?}", input[position].span()) pub fn parse_error(input: &[TokenTree], error: &pom::Error) -> Diagnostic {
}
pub fn parse_error(input: &[TokenTree], error: &pom::Error) -> String {
match error { match error {
pom::Error::Incomplete => "Incomplete token stream".to_string(), pom::Error::Incomplete => Diagnostic::new(Level::Error, "unexpected end of macro!"),
pom::Error::Mismatch { message, position } => { pom::Error::Mismatch { message, position } => {
format!("{}: {}", error_location(input, *position), message) Diagnostic::spanned(input[*position].span(), Level::Error, message.as_str())
} }
pom::Error::Conversion { message, position } => { pom::Error::Conversion { message, position } => {
format!("{}: {}", error_location(input, *position), message) Diagnostic::spanned(input[*position].span(), Level::Error, message.as_str())
} }
pom::Error::Expect { pom::Error::Expect {
message, message,
position, position,
inner, inner,
} => format!( } => {
"{}: {}\n{}", let mut diag =
error_location(input, *position), Diagnostic::spanned(input[*position].span(), Level::Error, message.as_str());
message, let child = parse_error(input, &inner);
parse_error(input, &inner) diag.span_error(child.spans(), child.message())
), }
pom::Error::Custom { pom::Error::Custom {
message, message,
position, position,
inner, inner,
} => { } => {
let mut out = format!("{}: {}", error_location(input, *position), message); let mut diag =
if let Some(error) = inner { Diagnostic::spanned(input[*position].span(), Level::Error, message.as_str());
out += &format!("\n{}", parse_error(input, error)); if let Some(inner) = inner {
let child = parse_error(input, &inner);
diag.span_error(child.spans(), child.message())
} else {
diag
} }
out
} }
} }
} }