Meaningful span for attrs parsed from string literal

This commit is contained in:
David Tolnay 2018-01-10 19:59:49 -08:00
parent cc2558b0dc
commit 3d64df6e87
No known key found for this signature in database
GPG Key ID: F9BA143B95FF6D82

View File

@ -12,10 +12,10 @@ use syn::Ident;
use syn::Meta::{List, NameValue, Word}; use syn::Meta::{List, NameValue, Word};
use syn::NestedMeta::{Literal, Meta}; use syn::NestedMeta::{Literal, Meta};
use syn::punctuated::Punctuated; use syn::punctuated::Punctuated;
use syn::synom::Synom; use syn::synom::{Synom, ParseError};
use std::collections::BTreeSet; use std::collections::BTreeSet;
use std::str::FromStr; use std::str::FromStr;
use proc_macro2::Span; use proc_macro2::{Span, TokenStream, TokenNode, TokenTree};
// This module handles parsing of `#[serde(...)]` attributes. The entrypoints // This module handles parsing of `#[serde(...)]` attributes. The entrypoints
// are `attr::Container::from_ast`, `attr::Variant::from_ast`, and // are `attr::Container::from_ast`, `attr::Variant::from_ast`, and
@ -197,29 +197,29 @@ impl Container {
match meta_item { match meta_item {
// Parse `#[serde(rename = "foo")]` // Parse `#[serde(rename = "foo")]`
Meta(NameValue(ref m)) if m.ident == "rename" => { Meta(NameValue(ref m)) if m.ident == "rename" => {
if let Ok(s) = get_string_from_lit(cx, m.ident.as_ref(), m.ident.as_ref(), &m.lit) { if let Ok(s) = get_lit_str(cx, m.ident.as_ref(), m.ident.as_ref(), &m.lit) {
ser_name.set(s.clone()); ser_name.set(s.value());
de_name.set(s); de_name.set(s.value());
} }
} }
// Parse `#[serde(rename(serialize = "foo", deserialize = "bar"))]` // Parse `#[serde(rename(serialize = "foo", deserialize = "bar"))]`
Meta(List(ref m)) if m.ident == "rename" => { Meta(List(ref m)) if m.ident == "rename" => {
if let Ok((ser, de)) = get_renames(cx, &m.nested) { if let Ok((ser, de)) = get_renames(cx, &m.nested) {
ser_name.set_opt(ser); ser_name.set_opt(ser.map(syn::LitStr::value));
de_name.set_opt(de); de_name.set_opt(de.map(syn::LitStr::value));
} }
} }
// Parse `#[serde(rename_all = "foo")]` // Parse `#[serde(rename_all = "foo")]`
Meta(NameValue(ref m)) if m.ident == "rename_all" => { Meta(NameValue(ref m)) if m.ident == "rename_all" => {
if let Ok(s) = get_string_from_lit(cx, m.ident.as_ref(), m.ident.as_ref(), &m.lit) { if let Ok(s) = get_lit_str(cx, m.ident.as_ref(), m.ident.as_ref(), &m.lit) {
match RenameRule::from_str(&s) { match RenameRule::from_str(&s.value()) {
Ok(rename_rule) => rename_all.set(rename_rule), Ok(rename_rule) => rename_all.set(rename_rule),
Err(()) => cx.error(format!( Err(()) => cx.error(format!(
"unknown rename rule for #[serde(rename_all \ "unknown rename rule for #[serde(rename_all \
= {:?})]", = {:?})]",
s s.value()
)), )),
} }
} }
@ -286,10 +286,10 @@ impl Container {
// Parse `#[serde(tag = "type")]` // Parse `#[serde(tag = "type")]`
Meta(NameValue(ref m)) if m.ident == "tag" => { Meta(NameValue(ref m)) if m.ident == "tag" => {
if let Ok(s) = get_string_from_lit(cx, m.ident.as_ref(), m.ident.as_ref(), &m.lit) { if let Ok(s) = get_lit_str(cx, m.ident.as_ref(), m.ident.as_ref(), &m.lit) {
match item.data { match item.data {
syn::Data::Enum(_) => { syn::Data::Enum(_) => {
internal_tag.set(s); internal_tag.set(s.value());
} }
syn::Data::Struct(_) | syn::Data::Union(_) => { syn::Data::Struct(_) | syn::Data::Union(_) => {
cx.error("#[serde(tag = \"...\")] can only be used on enums") cx.error("#[serde(tag = \"...\")] can only be used on enums")
@ -300,10 +300,10 @@ impl Container {
// Parse `#[serde(content = "c")]` // Parse `#[serde(content = "c")]`
Meta(NameValue(ref m)) if m.ident == "content" => { Meta(NameValue(ref m)) if m.ident == "content" => {
if let Ok(s) = get_string_from_lit(cx, m.ident.as_ref(), m.ident.as_ref(), &m.lit) { if let Ok(s) = get_lit_str(cx, m.ident.as_ref(), m.ident.as_ref(), &m.lit) {
match item.data { match item.data {
syn::Data::Enum(_) => { syn::Data::Enum(_) => {
content.set(s); content.set(s.value());
} }
syn::Data::Struct(_) | syn::Data::Union(_) => cx.error( syn::Data::Struct(_) | syn::Data::Union(_) => cx.error(
"#[serde(content = \"...\")] can only be used on \ "#[serde(content = \"...\")] can only be used on \
@ -532,29 +532,29 @@ impl Variant {
match meta_item { match meta_item {
// Parse `#[serde(rename = "foo")]` // Parse `#[serde(rename = "foo")]`
Meta(NameValue(ref m)) if m.ident == "rename" => { Meta(NameValue(ref m)) if m.ident == "rename" => {
if let Ok(s) = get_string_from_lit(cx, m.ident.as_ref(), m.ident.as_ref(), &m.lit) { if let Ok(s) = get_lit_str(cx, m.ident.as_ref(), m.ident.as_ref(), &m.lit) {
ser_name.set(s.clone()); ser_name.set(s.value());
de_name.set(s); de_name.set(s.value());
} }
} }
// Parse `#[serde(rename(serialize = "foo", deserialize = "bar"))]` // Parse `#[serde(rename(serialize = "foo", deserialize = "bar"))]`
Meta(List(ref m)) if m.ident == "rename" => { Meta(List(ref m)) if m.ident == "rename" => {
if let Ok((ser, de)) = get_renames(cx, &m.nested) { if let Ok((ser, de)) = get_renames(cx, &m.nested) {
ser_name.set_opt(ser); ser_name.set_opt(ser.map(syn::LitStr::value));
de_name.set_opt(de); de_name.set_opt(de.map(syn::LitStr::value));
} }
} }
// Parse `#[serde(rename_all = "foo")]` // Parse `#[serde(rename_all = "foo")]`
Meta(NameValue(ref m)) if m.ident == "rename_all" => { Meta(NameValue(ref m)) if m.ident == "rename_all" => {
if let Ok(s) = get_string_from_lit(cx, m.ident.as_ref(), m.ident.as_ref(), &m.lit) { if let Ok(s) = get_lit_str(cx, m.ident.as_ref(), m.ident.as_ref(), &m.lit) {
match RenameRule::from_str(&s) { match RenameRule::from_str(&s.value()) {
Ok(rename_rule) => rename_all.set(rename_rule), Ok(rename_rule) => rename_all.set(rename_rule),
Err(()) => cx.error(format!( Err(()) => cx.error(format!(
"unknown rename rule for #[serde(rename_all \ "unknown rename rule for #[serde(rename_all \
= {:?})]", = {:?})]",
s s.value()
)), )),
} }
} }
@ -754,17 +754,17 @@ impl Field {
match meta_item { match meta_item {
// Parse `#[serde(rename = "foo")]` // Parse `#[serde(rename = "foo")]`
Meta(NameValue(ref m)) if m.ident == "rename" => { Meta(NameValue(ref m)) if m.ident == "rename" => {
if let Ok(s) = get_string_from_lit(cx, m.ident.as_ref(), m.ident.as_ref(), &m.lit) { if let Ok(s) = get_lit_str(cx, m.ident.as_ref(), m.ident.as_ref(), &m.lit) {
ser_name.set(s.clone()); ser_name.set(s.value());
de_name.set(s); de_name.set(s.value());
} }
} }
// Parse `#[serde(rename(serialize = "foo", deserialize = "bar"))]` // Parse `#[serde(rename(serialize = "foo", deserialize = "bar"))]`
Meta(List(ref m)) if m.ident == "rename" => { Meta(List(ref m)) if m.ident == "rename" => {
if let Ok((ser, de)) = get_renames(cx, &m.nested) { if let Ok((ser, de)) = get_renames(cx, &m.nested) {
ser_name.set_opt(ser); ser_name.set_opt(ser.map(syn::LitStr::value));
de_name.set_opt(de); de_name.set_opt(de.map(syn::LitStr::value));
} }
} }
@ -1019,14 +1019,15 @@ impl Field {
type SerAndDe<T> = (Option<T>, Option<T>); type SerAndDe<T> = (Option<T>, Option<T>);
fn get_ser_and_de<T, F>( fn get_ser_and_de<'a, T, F>(
cx: &Ctxt, cx: &Ctxt,
attr_name: &'static str, attr_name: &'static str,
metas: &Punctuated<syn::NestedMeta, Token![,]>, metas: &'a Punctuated<syn::NestedMeta, Token![,]>,
f: F, f: F,
) -> Result<SerAndDe<T>, ()> ) -> Result<SerAndDe<T>, ()>
where where
F: Fn(&Ctxt, &str, &str, &syn::Lit) -> Result<T, ()>, T: 'a,
F: Fn(&Ctxt, &str, &str, &'a syn::Lit) -> Result<T, ()>,
{ {
let mut ser_meta = Attr::none(cx, attr_name); let mut ser_meta = Attr::none(cx, attr_name);
let mut de_meta = Attr::none(cx, attr_name); let mut de_meta = Attr::none(cx, attr_name);
@ -1059,8 +1060,8 @@ where
Ok((ser_meta.get(), de_meta.get())) Ok((ser_meta.get(), de_meta.get()))
} }
fn get_renames(cx: &Ctxt, items: &Punctuated<syn::NestedMeta, Token![,]>) -> Result<SerAndDe<String>, ()> { fn get_renames<'a>(cx: &Ctxt, items: &'a Punctuated<syn::NestedMeta, Token![,]>) -> Result<SerAndDe<&'a syn::LitStr>, ()> {
get_ser_and_de(cx, "rename", items, get_string_from_lit) get_ser_and_de(cx, "rename", items, get_lit_str)
} }
fn get_where_predicates( fn get_where_predicates(
@ -1084,14 +1085,14 @@ pub fn get_serde_meta_items(attr: &syn::Attribute) -> Option<Vec<syn::NestedMeta
} }
} }
fn get_string_from_lit( fn get_lit_str<'a>(
cx: &Ctxt, cx: &Ctxt,
attr_name: &str, attr_name: &str,
meta_item_name: &str, meta_item_name: &str,
lit: &syn::Lit, lit: &'a syn::Lit,
) -> Result<String, ()> { ) -> Result<&'a syn::LitStr, ()> {
if let syn::Lit::Str(ref lit) = *lit { if let syn::Lit::Str(ref lit) = *lit {
Ok(lit.value()) Ok(lit)
} else { } else {
cx.error(format!( cx.error(format!(
"expected serde {} attribute to be a string: `{} = \"...\"`", "expected serde {} attribute to be a string: `{} = \"...\"`",
@ -1102,8 +1103,8 @@ fn get_string_from_lit(
} }
fn parse_lit_into_path(cx: &Ctxt, attr_name: &str, lit: &syn::Lit) -> Result<syn::Path, ()> { fn parse_lit_into_path(cx: &Ctxt, attr_name: &str, lit: &syn::Lit) -> Result<syn::Path, ()> {
let string = try!(get_string_from_lit(cx, attr_name, attr_name, lit)); let string = try!(get_lit_str(cx, attr_name, attr_name, lit));
syn::parse_str(&string).map_err(|_| cx.error(format!("failed to parse path: {:?}", string))) parse_lit_str(string).map_err(|_| cx.error(format!("failed to parse path: {:?}", string.value())))
} }
fn parse_lit_into_where( fn parse_lit_into_where(
@ -1112,25 +1113,25 @@ fn parse_lit_into_where(
meta_item_name: &str, meta_item_name: &str,
lit: &syn::Lit, lit: &syn::Lit,
) -> Result<Vec<syn::WherePredicate>, ()> { ) -> Result<Vec<syn::WherePredicate>, ()> {
let string = try!(get_string_from_lit(cx, attr_name, meta_item_name, lit)); let string = try!(get_lit_str(cx, attr_name, meta_item_name, lit));
if string.is_empty() { if string.value().is_empty() {
return Ok(Vec::new()); return Ok(Vec::new());
} }
let where_string = format!("where {}", string); let where_string = syn::LitStr::new(&format!("where {}", string.value()), string.span);
syn::parse_str::<syn::WhereClause>(&where_string) parse_lit_str::<syn::WhereClause>(&where_string)
.map(|wh| wh.predicates.into_iter().collect()) .map(|wh| wh.predicates.into_iter().collect())
.map_err(|err| cx.error(err)) .map_err(|err| cx.error(err))
} }
fn parse_lit_into_ty(cx: &Ctxt, attr_name: &str, lit: &syn::Lit) -> Result<syn::Type, ()> { fn parse_lit_into_ty(cx: &Ctxt, attr_name: &str, lit: &syn::Lit) -> Result<syn::Type, ()> {
let string = try!(get_string_from_lit(cx, attr_name, attr_name, lit)); let string = try!(get_lit_str(cx, attr_name, attr_name, lit));
syn::parse_str(&string).map_err(|_| { parse_lit_str(string).map_err(|_| {
cx.error(format!( cx.error(format!(
"failed to parse type: {} = {:?}", "failed to parse type: {} = {:?}",
attr_name, string attr_name, string.value()
)) ))
}) })
} }
@ -1142,8 +1143,8 @@ fn parse_lit_into_lifetimes(
attr_name: &str, attr_name: &str,
lit: &syn::Lit, lit: &syn::Lit,
) -> Result<BTreeSet<syn::Lifetime>, ()> { ) -> Result<BTreeSet<syn::Lifetime>, ()> {
let string = try!(get_string_from_lit(cx, attr_name, attr_name, lit)); let string = try!(get_lit_str(cx, attr_name, attr_name, lit));
if string.is_empty() { if string.value().is_empty() {
cx.error("at least one lifetime must be borrowed"); cx.error("at least one lifetime must be borrowed");
return Err(()); return Err(());
} }
@ -1157,7 +1158,7 @@ fn parse_lit_into_lifetimes(
)); ));
} }
if let Ok(BorrowedLifetimes(lifetimes)) = syn::parse_str(&string) { if let Ok(BorrowedLifetimes(lifetimes)) = parse_lit_str(string) {
let mut set = BTreeSet::new(); let mut set = BTreeSet::new();
for lifetime in lifetimes { for lifetime in lifetimes {
if !set.insert(lifetime) { if !set.insert(lifetime) {
@ -1166,7 +1167,7 @@ fn parse_lit_into_lifetimes(
} }
return Ok(set); return Ok(set);
} }
Err(cx.error(format!("failed to parse borrowed lifetimes: {:?}", string))) Err(cx.error(format!("failed to parse borrowed lifetimes: {:?}", string.value())))
} }
// Whether the type looks like it might be `std::borrow::Cow<T>` where elem="T". // Whether the type looks like it might be `std::borrow::Cow<T>` where elem="T".
@ -1350,3 +1351,32 @@ fn collect_lifetimes(ty: &syn::Type, out: &mut BTreeSet<syn::Lifetime>) {
| syn::Type::Verbatim(_) => {} | syn::Type::Verbatim(_) => {}
} }
} }
fn parse_lit_str<T>(s: &syn::LitStr) -> Result<T, ParseError>
where
T: Synom,
{
let tokens = try!(spanned_tokens(s));
syn::parse2(tokens)
}
fn spanned_tokens(s: &syn::LitStr) -> Result<TokenStream, ParseError> {
let stream = try!(syn::parse_str(&s.value()));
Ok(respan_token_stream(stream, s.span))
}
fn respan_token_stream(stream: TokenStream, span: Span) -> TokenStream {
stream.into_iter().map(|token| respan_token_tree(token, span)).collect()
}
fn respan_token_tree(token: TokenTree, span: Span) -> TokenTree {
TokenTree {
span: span,
kind: match token.kind {
TokenNode::Group(delimiter, nested) => {
TokenNode::Group(delimiter, respan_token_stream(nested, span))
}
other => other,
},
}
}