Remove proc_macro_diagnostic feature

This commit is contained in:
David Tolnay 2018-11-17 23:40:17 -08:00
parent 78f0e9b1e6
commit dcda57c8af
No known key found for this signature in database
GPG Key ID: F9BA143B95FF6D82
3 changed files with 91 additions and 67 deletions

View File

@ -1,8 +1,8 @@
use ansi_term::Style; use ansi_term::Style;
use lalrpop_util::ParseError::*; use lalrpop_util::ParseError::*;
use lexer::Token; use lexer::Token;
use proc_macro::{Diagnostic, Level}; use proc_macro2::{Ident, TokenStream};
use proc_macro2::Ident; use quote::{quote, quote_spanned};
pub type ParseError = lalrpop_util::ParseError<usize, Token, HtmlParseError>; pub type ParseError = lalrpop_util::ParseError<usize, Token, HtmlParseError>;
@ -41,49 +41,76 @@ fn is_in_node_position(tokens: &[String]) -> bool {
input == output input == output
} }
pub fn parse_error(input: &[Token], error: &ParseError) -> Diagnostic { pub fn parse_error(input: &[Token], error: &ParseError) -> TokenStream {
match error { match error {
InvalidToken { location } => { InvalidToken { location } => {
let loc = &input[*location]; let span = input[*location].span();
Diagnostic::spanned(loc.span().unstable(), Level::Error, "invalid token") quote_spanned! {span=>
compile_error! { "invalid token" }
}
} }
UnrecognizedToken { UnrecognizedToken {
token: None, token: None,
expected, expected,
} => { } => {
let msg = format!("missing {}", pprint_tokens(&expected)); let msg = format!(
Diagnostic::spanned( "unexpected end of macro; missing {}",
input[0].span().unstable().join(input[input.len() - 1].span().unstable()).unwrap(), pprint_tokens(&expected)
Level::Error, );
"unexpected end of macro", quote! {
) compile_error! { #msg }
.help(msg) }
} }
UnrecognizedToken { UnrecognizedToken {
token: Some((_, token, _)), token: Some((_, token, _)),
expected, expected,
} => { } => {
let msg = format!("expected {}", pprint_tokens(&expected)); let span = token.span();
let mut diag = Diagnostic::spanned(token.span().unstable(), Level::Error, msg); let error_msg = format!("expected {}", pprint_tokens(&expected));
if is_in_node_position(expected) && token.is_ident() { let error = quote_spanned! {span=>
compile_error! { #error_msg }
};
let help = if is_in_node_position(expected) && token.is_ident() {
// special case: you probably meant to quote that text // special case: you probably meant to quote that text
diag = diag.help(format!( let help_msg = format!(
"text nodes need to be quoted, eg. {}", "text nodes need to be quoted, eg. {}",
Style::new().bold().paint("<p>\"Hello Joe!\"</p>") Style::new().bold().paint("<p>\"Hello Joe!\"</p>")
)) );
} Some(quote_spanned! {span=>
diag compile_error! { #help_msg }
})
} else {
None
};
quote! {{
#error
#help
}}
} }
ExtraToken { ExtraToken {
token: (_, token, _), token: (_, token, _),
} => Diagnostic::spanned(token.span().unstable(), Level::Error, "superfluous token"), } => {
let span = token.span();
quote_spanned! {span=>
compile_error! { "superfluous token" }
}
}
User { User {
error: HtmlParseError::TagMismatch { open, close }, error: HtmlParseError::TagMismatch { open, close },
} => Diagnostic::spanned( } => {
close.span().unstable(), let close_span = close.span();
Level::Error, let close_msg = format!("expected closing tag '</{}>', found '</{}>'", open, close);
format!("expected closing tag '</{}>', found '</{}>'", open, close), let close_error = quote_spanned! {close_span=>
) compile_error! { #close_msg }
.span_help(open.span().unstable(), "opening tag is here:"), };
let open_span = open.span();
let open_error = quote_spanned! {open_span=>
compile_error! { "unclosed tag" }
};
quote! {{
#close_error
#open_error
}}
}
} }
} }

View File

@ -1,6 +1,5 @@
use proc_macro::{Diagnostic, Level};
use proc_macro2::{Delimiter, Group, Ident, Literal, TokenStream, TokenTree}; use proc_macro2::{Delimiter, Group, Ident, Literal, TokenStream, TokenTree};
use quote::quote; use quote::{quote, quote_spanned};
use config::{required_children, ATTR_EVENTS}; use config::{required_children, ATTR_EVENTS};
use error::ParseError; use error::ParseError;
@ -19,38 +18,38 @@ pub enum Node {
} }
impl Node { impl Node {
pub fn into_token_stream(self) -> TokenStream { pub fn into_token_stream(self) -> Result<TokenStream, TokenStream> {
match self { match self {
Node::Element(el) => el.into_token_stream(), Node::Element(el) => el.into_token_stream(),
Node::Text(text) => { Node::Text(text) => {
let text = TokenTree::Literal(text); let text = TokenTree::Literal(text);
quote!(Box::new(typed_html::dom::TextNode::new(#text.to_string()))) Ok(quote!(Box::new(typed_html::dom::TextNode::new(#text.to_string()))))
} }
Node::Block(_) => panic!("cannot have a block in this position"), Node::Block(_) => panic!("cannot have a block in this position"),
} }
} }
fn into_child_stream(self) -> TokenStream { fn into_child_stream(self) -> Result<TokenStream, TokenStream> {
match self { match self {
Node::Element(el) => { Node::Element(el) => {
let el = el.into_token_stream(); let el = el.into_token_stream()?;
quote!( Ok(quote!(
element.children.push(#el); element.children.push(#el);
) ))
} }
tx @ Node::Text(_) => { tx @ Node::Text(_) => {
let tx = tx.into_token_stream(); let tx = tx.into_token_stream()?;
quote!( Ok(quote!(
element.children.push(#tx); element.children.push(#tx);
) ))
} }
Node::Block(group) => { Node::Block(group) => {
let group: TokenTree = group.into(); let group: TokenTree = group.into();
quote!( Ok(quote!(
for child in #group.into_iter() { for child in #group.into_iter() {
element.children.push(child); element.children.push(child);
} }
) ))
} }
} }
} }
@ -116,24 +115,22 @@ fn is_string_literal(literal: &Literal) -> bool {
} }
impl Element { impl Element {
fn into_token_stream(mut self) -> TokenStream { fn into_token_stream(mut self) -> Result<TokenStream, TokenStream> {
let name = self.name; let name = self.name;
let name_str = name.to_string(); let name_str = name.to_string();
let typename: TokenTree = Ident::new(&name_str, name.span()).into(); let typename: TokenTree = Ident::new(&name_str, name.span()).into();
let req_names = required_children(&name_str); let req_names = required_children(&name_str);
if req_names.len() > self.children.len() { if req_names.len() > self.children.len() {
Diagnostic::spanned( let span = name.span();
name.span().unstable(), let error = format!(
Level::Error,
format!(
"<{}> requires {} children but there are only {}", "<{}> requires {} children but there are only {}",
name_str, name_str,
req_names.len(), req_names.len(),
self.children.len() self.children.len()
), );
) return Err(quote_spanned! {span=>
.emit(); compile_error! { #error }
panic!(); });
} }
let events = extract_event_handlers(&mut self.attributes); let events = extract_event_handlers(&mut self.attributes);
let data_attrs = extract_data_attrs(&mut self.attributes); let data_attrs = extract_data_attrs(&mut self.attributes);
@ -148,8 +145,13 @@ impl Element {
.children .children
.split_off(req_names.len()) .split_off(req_names.len())
.into_iter() .into_iter()
.map(Node::into_child_stream); .map(Node::into_child_stream)
let req_children = self.children.into_iter().map(Node::into_token_stream); .collect::<Result<Vec<TokenStream>, TokenStream>>()?;
let req_children = self
.children
.into_iter()
.map(Node::into_token_stream)
.collect::<Result<Vec<TokenStream>, TokenStream>>()?;
let mut body = TokenStream::new(); let mut body = TokenStream::new();
for (attr_str, key, value) in attrs { for (attr_str, key, value) in attrs {
@ -205,13 +207,13 @@ impl Element {
args.extend(quote!( #arg, )); args.extend(quote!( #arg, ));
} }
quote!( Ok(quote!(
{ {
let mut element = typed_html::elements::#typename::new(#args); let mut element = typed_html::elements::#typename::new(#args);
#body #body
Box::new(element) Box::new(element)
} }
) ))
} }
} }

View File

@ -1,7 +1,6 @@
#![recursion_limit = "128"] #![recursion_limit = "128"]
#![feature(proc_macro_hygiene)] #![feature(proc_macro_hygiene)]
#![feature(proc_macro_span)] #![feature(proc_macro_span)]
#![feature(proc_macro_diagnostic)]
extern crate ansi_term; extern crate ansi_term;
extern crate lalrpop_util; extern crate lalrpop_util;
@ -10,7 +9,6 @@ extern crate proc_macro2;
extern crate quote; extern crate quote;
use proc_macro::TokenStream; use proc_macro::TokenStream;
use quote::quote;
mod config; mod config;
mod declare; mod declare;
@ -32,11 +30,11 @@ pub fn html(input: TokenStream) -> TokenStream {
let stream = lexer::unroll_stream(input.into(), false); let stream = lexer::unroll_stream(input.into(), false);
let result = html::expand_html(&stream); let result = html::expand_html(&stream);
TokenStream::from(match result { TokenStream::from(match result {
Err(err) => { Err(err) => error::parse_error(&stream, &err),
error::parse_error(&stream, &err).emit(); Ok(node) => match node.into_token_stream() {
quote!(panic!()) Err(err) => err,
} Ok(success) => success,
Ok(node) => node.into_token_stream(), },
}) })
} }
@ -47,10 +45,7 @@ pub fn declare_elements(input: TokenStream) -> TokenStream {
let stream = lexer::keywordise(lexer::unroll_stream(input.into(), true)); let stream = lexer::keywordise(lexer::unroll_stream(input.into(), true));
let result = declare::expand_declare(&stream); let result = declare::expand_declare(&stream);
TokenStream::from(match result { TokenStream::from(match result {
Err(err) => { Err(err) => error::parse_error(&stream, &err),
error::parse_error(&stream, &err).emit();
quote!(panic!())
}
Ok(decls) => { Ok(decls) => {
let mut out = proc_macro2::TokenStream::new(); let mut out = proc_macro2::TokenStream::new();
for decl in decls { for decl in decls {