mirror of
https://github.com/pezkuwichain/serde.git
synced 2026-06-14 05:51:01 +00:00
feat(codegen): Improve error messages in annotations
This commit is contained in:
@@ -1,8 +1,12 @@
|
|||||||
use syntax::ast;
|
use std::rc::Rc;
|
||||||
|
use syntax::ast::{self, TokenTree};
|
||||||
use syntax::attr;
|
use syntax::attr;
|
||||||
|
use syntax::codemap::Span;
|
||||||
use syntax::ext::base::ExtCtxt;
|
use syntax::ext::base::ExtCtxt;
|
||||||
use syntax::print::pprust::{lit_to_string, meta_item_to_string};
|
use syntax::fold::Folder;
|
||||||
|
use syntax::parse::token;
|
||||||
use syntax::parse;
|
use syntax::parse;
|
||||||
|
use syntax::print::pprust::{lit_to_string, meta_item_to_string};
|
||||||
use syntax::ptr::P;
|
use syntax::ptr::P;
|
||||||
|
|
||||||
use aster::AstBuilder;
|
use aster::AstBuilder;
|
||||||
@@ -390,13 +394,60 @@ fn get_serde_meta_items(attr: &ast::Attribute) -> Option<&[P<ast::MetaItem>]> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This syntax folder rewrites tokens to say their spans are coming from a macro context.
|
||||||
|
struct Respanner<'a, 'b: 'a> {
|
||||||
|
cx: &'a ExtCtxt<'b>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> Folder for Respanner<'a, 'b> {
|
||||||
|
fn fold_tt(&mut self, tt: &TokenTree) -> TokenTree {
|
||||||
|
match *tt {
|
||||||
|
TokenTree::Token(span, ref tok) => {
|
||||||
|
TokenTree::Token(
|
||||||
|
self.new_span(span),
|
||||||
|
self.fold_token(tok.clone())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
TokenTree::Delimited(span, ref delimed) => {
|
||||||
|
TokenTree::Delimited(
|
||||||
|
self.new_span(span),
|
||||||
|
Rc::new(ast::Delimited {
|
||||||
|
delim: delimed.delim,
|
||||||
|
open_span: delimed.open_span,
|
||||||
|
tts: self.fold_tts(&delimed.tts),
|
||||||
|
close_span: delimed.close_span,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
TokenTree::Sequence(span, ref seq) => {
|
||||||
|
TokenTree::Sequence(
|
||||||
|
self.new_span(span),
|
||||||
|
Rc::new(ast::SequenceRepetition {
|
||||||
|
tts: self.fold_tts(&seq.tts),
|
||||||
|
separator: seq.separator.clone().map(|tok| self.fold_token(tok)),
|
||||||
|
..**seq
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_span(&mut self, span: Span) -> Span {
|
||||||
|
Span {
|
||||||
|
lo: span.lo,
|
||||||
|
hi: span.hi,
|
||||||
|
expn_id: self.cx.backtrace(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn parse_lit_into_expr(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result<P<ast::Expr>, Error> {
|
fn parse_lit_into_expr(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result<P<ast::Expr>, Error> {
|
||||||
let s: &str = match lit.node {
|
let source: &str = match lit.node {
|
||||||
ast::LitKind::Str(ref s, ast::StrStyle::Cooked) => &s,
|
ast::LitKind::Str(ref source, _) => &source,
|
||||||
_ => {
|
_ => {
|
||||||
cx.span_err(
|
cx.span_err(
|
||||||
lit.span,
|
lit.span,
|
||||||
&format!("{} literal `{}` must be a string",
|
&format!("serde annotation `{}` must be a string, not `{}`",
|
||||||
name,
|
name,
|
||||||
lit_to_string(lit)));
|
lit_to_string(lit)));
|
||||||
|
|
||||||
@@ -404,10 +455,40 @@ fn parse_lit_into_expr(cx: &ExtCtxt, name: &str, lit: &ast::Lit) -> Result<P<ast
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let expr = parse::parse_expr_from_source_str("<lit expansion>".to_string(),
|
// If we just parse the string into an expression, any syntax errors in the source will only
|
||||||
s.to_owned(),
|
// have spans that point inside the string, and not back to the attribute. So to have better
|
||||||
cx.cfg(),
|
// error reporting, we'll first parse the string into a token tree. Then we'll update those
|
||||||
cx.parse_sess());
|
// spans to say they're coming from a macro context that originally came from the attribute,
|
||||||
|
// and then finally parse them into an expression.
|
||||||
|
let tts = parse::parse_tts_from_source_str(
|
||||||
|
format!("<serde {} expansion>", name),
|
||||||
|
source.to_owned(),
|
||||||
|
cx.cfg(),
|
||||||
|
cx.parse_sess());
|
||||||
|
|
||||||
|
// Respan the spans to say they are all coming from this macro.
|
||||||
|
let tts = Respanner { cx: cx }.fold_tts(&tts);
|
||||||
|
|
||||||
|
let mut parser = parse::new_parser_from_tts(cx.parse_sess(), cx.cfg(), tts);
|
||||||
|
|
||||||
|
let expr = parser.parse_expr();
|
||||||
|
|
||||||
|
let expr = match expr {
|
||||||
|
Ok(expr) => expr,
|
||||||
|
Err(mut e) => {
|
||||||
|
e.emit();
|
||||||
|
return Err(Error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Make sure to error out if there are trailing characters in the stream.
|
||||||
|
match parser.expect(&token::Eof) {
|
||||||
|
Ok(()) => { }
|
||||||
|
Err(mut e) => {
|
||||||
|
e.emit();
|
||||||
|
return Err(Error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(expr)
|
Ok(expr)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user