Error on duplicate attributes

This commit is contained in:
David Tolnay 2016-06-14 11:22:49 -07:00
parent 8e4da7f36b
commit 28589620f6
No known key found for this signature in database
GPG Key ID: F9BA143B95FF6D82
5 changed files with 199 additions and 129 deletions

View File

@ -1,7 +1,7 @@
use std::rc::Rc;
use syntax::ast::{self, TokenTree};
use syntax::attr;
use syntax::codemap::Span;
use syntax::codemap::{Span, Spanned, respan};
use syntax::ext::base::ExtCtxt;
use syntax::fold::Folder;
use syntax::parse::parser::{Parser, PathStyle};
@ -11,9 +11,68 @@ use syntax::print::pprust::{lit_to_string, meta_item_to_string};
use syntax::ptr::P;
use aster::AstBuilder;
use aster::ident::ToIdent;
use error::Error;
struct Attr<'a, 'b: 'a, T> {
cx: &'a ExtCtxt<'b>,
name: &'static str,
value: Option<Spanned<T>>,
}
impl<'a, 'b, T> Attr<'a, 'b, T> {
fn none(cx: &'a ExtCtxt<'b>, name: &'static str) -> Self {
Attr {
cx: cx,
name: name,
value: None,
}
}
fn set(&mut self, span: Span, t: T) {
if let Some(Spanned { span: prev_span, .. }) = self.value {
let mut err = self.cx.struct_span_err(
span,
&format!("duplicate serde attribute `{}`", self.name));
err.span_help(prev_span, "previously set here");
err.emit();
} else {
self.value = Some(respan(span, t));
}
}
fn set_opt(&mut self, v: Option<Spanned<T>>) {
if let Some(v) = v {
self.set(v.span, v.node);
}
}
fn set_if_none(&mut self, span: Span, t: T) {
if self.value.is_none() {
self.value = Some(respan(span, t));
}
}
fn get(self) -> Option<T> {
self.value.map(|spanned| spanned.node)
}
}
struct BoolAttr<'a, 'b: 'a>(Attr<'a, 'b, ()>);
impl<'a, 'b> BoolAttr<'a, 'b> {
fn none(cx: &'a ExtCtxt<'b>, name: &'static str) -> Self {
BoolAttr(Attr::none(cx, name))
}
fn set_true(&mut self, span: Span) {
self.0.set(span, ());
}
fn get(&self) -> bool {
self.0.value.is_some()
}
}
#[derive(Debug)]
pub struct Name {
ident: ast::Ident,
@ -22,14 +81,6 @@ pub struct Name {
}
impl Name {
fn new(ident: ast::Ident) -> Self {
Name {
ident: ident,
serialize_name: None,
deserialize_name: None,
}
}
/// Return the container name for the container when serializing.
pub fn serialize_name(&self) -> InternedString {
match self.serialize_name {
@ -69,55 +120,47 @@ pub struct ContainerAttrs {
impl ContainerAttrs {
/// Extract out the `#[serde(...)]` attributes from an item.
pub fn from_item(cx: &ExtCtxt, item: &ast::Item) -> Result<Self, Error> {
let mut container_attrs = ContainerAttrs {
name: Name::new(item.ident),
deny_unknown_fields: false,
ser_bound: None,
de_bound: None,
};
let mut ser_name = Attr::none(cx, "rename");
let mut de_name = Attr::none(cx, "rename");
let mut deny_unknown_fields = BoolAttr::none(cx, "deny_unknown_fields");
let mut ser_bound = Attr::none(cx, "bound");
let mut de_bound = Attr::none(cx, "bound");
for meta_items in item.attrs().iter().filter_map(get_serde_meta_items) {
for meta_item in meta_items {
let span = meta_item.span;
match meta_item.node {
// Parse `#[serde(rename="foo")]`
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"rename" => {
let s = try!(get_str_from_lit(cx, name, lit));
container_attrs.name.serialize_name = Some(s.clone());
container_attrs.name.deserialize_name = Some(s);
ser_name.set(span, s.clone());
de_name.set(span, s);
}
// Parse `#[serde(rename(serialize="foo", deserialize="bar"))]`
ast::MetaItemKind::List(ref name, ref meta_items) if name == &"rename" => {
let (ser_name, de_name) = try!(get_renames(cx, meta_items));
if ser_name.is_some() {
container_attrs.name.serialize_name = ser_name;
}
if de_name.is_some() {
container_attrs.name.deserialize_name = de_name;
}
let (ser, de) = try!(get_renames(cx, meta_items));
ser_name.set_opt(ser);
de_name.set_opt(de);
}
// Parse `#[serde(deny_unknown_fields)]`
ast::MetaItemKind::Word(ref name) if name == &"deny_unknown_fields" => {
container_attrs.deny_unknown_fields = true;
deny_unknown_fields.set_true(span);
}
// Parse `#[serde(bound="D: Serialize")]`
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"bound" => {
let where_predicates = try!(parse_lit_into_where(cx, name, lit));
container_attrs.ser_bound = Some(where_predicates.clone());
container_attrs.de_bound = Some(where_predicates);
ser_bound.set(span, where_predicates.clone());
de_bound.set(span, where_predicates);
}
// Parse `#[serde(bound(serialize="D: Serialize", deserialize="D: Deserialize"))]`
ast::MetaItemKind::List(ref name, ref meta_items) if name == &"bound" => {
let (ser_bound, de_bound) = try!(get_where_predicates(cx, meta_items));
if ser_bound.is_some() {
container_attrs.ser_bound = ser_bound;
}
if de_bound.is_some() {
container_attrs.de_bound = de_bound;
}
let (ser, de) = try!(get_where_predicates(cx, meta_items));
ser_bound.set_opt(ser);
de_bound.set_opt(de);
}
_ => {
@ -132,7 +175,16 @@ impl ContainerAttrs {
}
}
Ok(container_attrs)
Ok(ContainerAttrs {
name: Name {
ident: item.ident,
serialize_name: ser_name.get(),
deserialize_name: de_name.get(),
},
deny_unknown_fields: deny_unknown_fields.get(),
ser_bound: ser_bound.get(),
de_bound: de_bound.get(),
})
}
pub fn name(&self) -> &Name {
@ -160,29 +212,25 @@ pub struct VariantAttrs {
impl VariantAttrs {
pub fn from_variant(cx: &ExtCtxt, variant: &ast::Variant) -> Result<Self, Error> {
let mut variant_attrs = VariantAttrs {
name: Name::new(variant.node.name),
};
let mut ser_name = Attr::none(cx, "rename");
let mut de_name = Attr::none(cx, "rename");
for meta_items in variant.node.attrs.iter().filter_map(get_serde_meta_items) {
for meta_item in meta_items {
let span = meta_item.span;
match meta_item.node {
// Parse `#[serde(rename="foo")]`
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"rename" => {
let s = try!(get_str_from_lit(cx, name, lit));
variant_attrs.name.serialize_name = Some(s.clone());
variant_attrs.name.deserialize_name = Some(s);
ser_name.set(span, s.clone());
de_name.set(span, s);
}
// Parse `#[serde(rename(serialize="foo", deserialize="bar"))]`
ast::MetaItemKind::List(ref name, ref meta_items) if name == &"rename" => {
let (ser_name, de_name) = try!(get_renames(cx, meta_items));
if ser_name.is_some() {
variant_attrs.name.serialize_name = ser_name;
}
if de_name.is_some() {
variant_attrs.name.deserialize_name = de_name;
}
let (ser, de) = try!(get_renames(cx, meta_items));
ser_name.set_opt(ser);
de_name.set_opt(de);
}
_ => {
@ -197,7 +245,13 @@ impl VariantAttrs {
}
}
Ok(variant_attrs)
Ok(VariantAttrs {
name: Name {
ident: variant.node.name,
serialize_name: ser_name.get(),
deserialize_name: de_name.get(),
},
})
}
pub fn name(&self) -> &Name {
@ -209,8 +263,8 @@ impl VariantAttrs {
#[derive(Debug)]
pub struct FieldAttrs {
name: Name,
skip_serializing_field: bool,
skip_deserializing_field: bool,
skip_serializing: bool,
skip_deserializing: bool,
skip_serializing_if: Option<ast::Path>,
default: FieldDefault,
serialize_with: Option<ast::Path>,
@ -235,107 +289,91 @@ impl FieldAttrs {
pub fn from_field(cx: &ExtCtxt,
index: usize,
field: &ast::StructField) -> Result<Self, Error> {
let builder = AstBuilder::new();
let mut ser_name = Attr::none(cx, "rename");
let mut de_name = Attr::none(cx, "rename");
let mut skip_serializing = BoolAttr::none(cx, "skip_serializing");
let mut skip_deserializing = BoolAttr::none(cx, "skip_deserializing");
let mut skip_serializing_if = Attr::none(cx, "skip_serializing_if");
let mut default = Attr::none(cx, "default");
let mut serialize_with = Attr::none(cx, "serialize_with");
let mut deserialize_with = Attr::none(cx, "deserialize_with");
let mut ser_bound = Attr::none(cx, "bound");
let mut de_bound = Attr::none(cx, "bound");
let field_ident = match field.ident {
Some(ident) => ident,
None => builder.id(index.to_string()),
};
let mut field_attrs = FieldAttrs {
name: Name::new(field_ident),
skip_serializing_field: false,
skip_deserializing_field: false,
skip_serializing_if: None,
default: FieldDefault::None,
serialize_with: None,
deserialize_with: None,
ser_bound: None,
de_bound: None,
None => index.to_string().to_ident(),
};
for meta_items in field.attrs.iter().filter_map(get_serde_meta_items) {
for meta_item in meta_items {
let span = meta_item.span;
match meta_item.node {
// Parse `#[serde(rename="foo")]`
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"rename" => {
let s = try!(get_str_from_lit(cx, name, lit));
field_attrs.name.serialize_name = Some(s.clone());
field_attrs.name.deserialize_name = Some(s);
ser_name.set(span, s.clone());
de_name.set(span, s);
}
// Parse `#[serde(rename(serialize="foo", deserialize="bar"))]`
ast::MetaItemKind::List(ref name, ref meta_items) if name == &"rename" => {
let (ser_name, de_name) = try!(get_renames(cx, meta_items));
if ser_name.is_some() {
field_attrs.name.serialize_name = ser_name;
}
if de_name.is_some() {
field_attrs.name.deserialize_name = de_name;
}
let (ser, de) = try!(get_renames(cx, meta_items));
ser_name.set_opt(ser);
de_name.set_opt(de);
}
// Parse `#[serde(default)]`
ast::MetaItemKind::Word(ref name) if name == &"default" => {
field_attrs.default = FieldDefault::Default;
default.set(span, FieldDefault::Default);
}
// Parse `#[serde(default="...")]`
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"default" => {
let path = try!(parse_lit_into_path(cx, name, lit));
field_attrs.default = FieldDefault::Path(path);
default.set(span, FieldDefault::Path(path));
}
// Parse `#[serde(skip_serializing)]`
ast::MetaItemKind::Word(ref name) if name == &"skip_serializing" => {
field_attrs.skip_serializing_field = true;
skip_serializing.set_true(span);
}
// Parse `#[serde(skip_deserializing)]`
ast::MetaItemKind::Word(ref name) if name == &"skip_deserializing" => {
field_attrs.skip_deserializing_field = true;
// Initialize field to Default::default() unless a different
// default is specified by `#[serde(default="...")]`
if field_attrs.default == FieldDefault::None {
field_attrs.default = FieldDefault::Default;
}
skip_deserializing.set_true(span);
}
// Parse `#[serde(skip_serializing_if="...")]`
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"skip_serializing_if" => {
let path = try!(parse_lit_into_path(cx, name, lit));
field_attrs.skip_serializing_if = Some(path);
skip_serializing_if.set(span, path);
}
// Parse `#[serde(serialize_with="...")]`
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"serialize_with" => {
let path = try!(parse_lit_into_path(cx, name, lit));
field_attrs.serialize_with = Some(path);
serialize_with.set(span, path);
}
// Parse `#[serde(deserialize_with="...")]`
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"deserialize_with" => {
let path = try!(parse_lit_into_path(cx, name, lit));
field_attrs.deserialize_with = Some(path);
deserialize_with.set(span, path);
}
// Parse `#[serde(bound="D: Serialize")]`
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"bound" => {
let where_predicates = try!(parse_lit_into_where(cx, name, lit));
field_attrs.ser_bound = Some(where_predicates.clone());
field_attrs.de_bound = Some(where_predicates);
ser_bound.set(span, where_predicates.clone());
de_bound.set(span, where_predicates);
}
// Parse `#[serde(bound(serialize="D: Serialize", deserialize="D: Deserialize"))]`
ast::MetaItemKind::List(ref name, ref meta_items) if name == &"bound" => {
let (ser_bound, de_bound) = try!(get_where_predicates(cx, meta_items));
if ser_bound.is_some() {
field_attrs.ser_bound = ser_bound;
}
if de_bound.is_some() {
field_attrs.de_bound = de_bound;
}
let (ser, de) = try!(get_where_predicates(cx, meta_items));
ser_bound.set_opt(ser);
de_bound.set_opt(de);
}
_ => {
@ -350,19 +388,39 @@ impl FieldAttrs {
}
}
Ok(field_attrs)
// Is skip_deserializing, initialize the field to Default::default()
// unless a different default is specified by `#[serde(default="...")]`
if let Some(Spanned { span, .. }) = skip_deserializing.0.value {
default.set_if_none(span, FieldDefault::Default);
}
Ok(FieldAttrs {
name: Name {
ident: field_ident,
serialize_name: ser_name.get(),
deserialize_name: de_name.get(),
},
skip_serializing: skip_serializing.get(),
skip_deserializing: skip_deserializing.get(),
skip_serializing_if: skip_serializing_if.get(),
default: default.get().unwrap_or(FieldDefault::None),
serialize_with: serialize_with.get(),
deserialize_with: deserialize_with.get(),
ser_bound: ser_bound.get(),
de_bound: de_bound.get(),
})
}
pub fn name(&self) -> &Name {
&self.name
}
pub fn skip_serializing_field(&self) -> bool {
self.skip_serializing_field
pub fn skip_serializing(&self) -> bool {
self.skip_serializing
}
pub fn skip_deserializing_field(&self) -> bool {
self.skip_deserializing_field
pub fn skip_deserializing(&self) -> bool {
self.skip_deserializing
}
pub fn skip_serializing_if(&self) -> Option<&ast::Path> {
@ -410,7 +468,7 @@ fn get_ser_and_de<T, F>(
attribute: &str,
items: &[P<ast::MetaItem>],
f: F
) -> Result<(Option<T>, Option<T>), Error>
) -> Result<(Option<Spanned<T>>, Option<Spanned<T>>), Error>
where F: Fn(&ExtCtxt, &str, &ast::Lit) -> Result<T, Error>,
{
let mut ser_item = None;
@ -420,12 +478,12 @@ fn get_ser_and_de<T, F>(
match item.node {
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"serialize" => {
let s = try!(f(cx, name, lit));
ser_item = Some(s);
ser_item = Some(respan(item.span, s));
}
ast::MetaItemKind::NameValue(ref name, ref lit) if name == &"deserialize" => {
let s = try!(f(cx, name, lit));
de_item = Some(s);
de_item = Some(respan(item.span, s));
}
_ => {
@ -446,14 +504,14 @@ fn get_ser_and_de<T, F>(
fn get_renames(
cx: &ExtCtxt,
items: &[P<ast::MetaItem>],
) -> Result<(Option<InternedString>, Option<InternedString>), Error> {
) -> Result<(Option<Spanned<InternedString>>, Option<Spanned<InternedString>>), Error> {
get_ser_and_de(cx, "rename", items, get_str_from_lit)
}
fn get_where_predicates(
cx: &ExtCtxt,
items: &[P<ast::MetaItem>],
) -> Result<(Option<Vec<ast::WherePredicate>>, Option<Vec<ast::WherePredicate>>), Error> {
) -> Result<(Option<Spanned<Vec<ast::WherePredicate>>>, Option<Spanned<Vec<ast::WherePredicate>>>), Error> {
get_ser_and_de(cx, "bound", items, parse_lit_into_where)
}

View File

@ -135,7 +135,7 @@ fn build_impl_generics(
// attribute specify their own bound so we do not generate one. All other fields
// may need a `T: Deserialize` bound where T is the type of the field.
fn needs_deserialize_bound(attrs: &attr::FieldAttrs) -> bool {
!attrs.skip_deserializing_field()
!attrs.skip_deserializing()
&& attrs.deserialize_with().is_none()
&& attrs.de_bound().is_none()
}
@ -450,7 +450,7 @@ fn deserialize_seq(
.enumerate()
.map(|(i, &(ref field, ref attrs))| {
let name = builder.id(format!("__field{}", i));
if attrs.skip_deserializing_field() {
if attrs.skip_deserializing() {
let default = expr_is_missing(cx, attrs);
quote_stmt!(cx,
let $name = $default;
@ -1049,7 +1049,7 @@ fn deserialize_map(
// Declare each field that will be deserialized.
let let_values: Vec<ast::Stmt> = fields_attrs_names.iter()
.filter(|&&(_, ref attrs, _)| !attrs.skip_deserializing_field())
.filter(|&&(_, ref attrs, _)| !attrs.skip_deserializing())
.map(|&(field, _, name)| {
let field_ty = &field.ty;
quote_stmt!(cx, let mut $name: Option<$field_ty> = None;).unwrap()
@ -1058,7 +1058,7 @@ fn deserialize_map(
// Match arms to extract a value for a field.
let value_arms = fields_attrs_names.iter()
.filter(|&&(_, ref attrs, _)| !attrs.skip_deserializing_field())
.filter(|&&(_, ref attrs, _)| !attrs.skip_deserializing())
.map(|&(ref field, ref attrs, name)| {
let deser_name = attrs.name().deserialize_name();
let name_str = builder.expr().lit().str(deser_name);
@ -1092,7 +1092,7 @@ fn deserialize_map(
// Match arms to ignore value for fields that have `skip_deserializing`.
// Ignored even if `deny_unknown_fields` is set.
let skipped_arms = fields_attrs_names.iter()
.filter(|&&(_, ref attrs, _)| attrs.skip_deserializing_field())
.filter(|&&(_, ref attrs, _)| attrs.skip_deserializing())
.map(|&(_, _, name)| {
quote_arm!(cx,
__Field::$name => {
@ -1112,7 +1112,7 @@ fn deserialize_map(
};
let extract_values = fields_attrs_names.iter()
.filter(|&&(_, ref attrs, _)| !attrs.skip_deserializing_field())
.filter(|&&(_, ref attrs, _)| !attrs.skip_deserializing())
.map(|&(_, ref attrs, name)| {
let missing_expr = expr_is_missing(cx, attrs);
@ -1138,7 +1138,7 @@ fn deserialize_map(
cx.span_bug(field.span, "struct contains unnamed fields")
}
},
if attrs.skip_deserializing_field() {
if attrs.skip_deserializing() {
expr_is_missing(cx, attrs)
} else {
builder.expr().id(name)
@ -1258,7 +1258,7 @@ fn check_no_str(
};
for &(ref field, ref attrs) in fields {
if attrs.skip_deserializing_field()
if attrs.skip_deserializing()
|| attrs.deserialize_with().is_some() { continue }
if let ast::TyKind::Rptr(_, ref inner) = field.ty.node {

View File

@ -129,7 +129,7 @@ fn build_impl_generics(
// attribute specify their own bound so we do not generate one. All other fields
// may need a `T: Serialize` bound where T is the type of the field.
fn needs_serialize_bound(attrs: &attr::FieldAttrs) -> bool {
!attrs.skip_serializing_field()
!attrs.skip_serializing()
&& attrs.serialize_with().is_none()
&& attrs.ser_bound().is_none()
}
@ -749,7 +749,7 @@ fn serialize_struct_visitor(
let fields_with_attrs = try!(attr::fields_with_attrs(cx, fields));
let arms: Vec<ast::Arm> = fields_with_attrs.iter()
.filter(|&&(_, ref attrs)| !attrs.skip_serializing_field())
.filter(|&&(_, ref attrs)| !attrs.skip_serializing())
.enumerate()
.map(|(i, &(ref field, ref attrs))| {
let ident = field.ident.expect("struct has unnamed field");
@ -794,7 +794,7 @@ fn serialize_struct_visitor(
.build();
let len = fields_with_attrs.iter()
.filter(|&&(_, ref attrs)| !attrs.skip_serializing_field())
.filter(|&&(_, ref attrs)| !attrs.skip_serializing())
.map(|&(ref field, ref attrs)| {
let ident = field.ident.expect("struct has unnamed fields");
let mut field_expr = quote_expr!(cx, self.value.$ident);

View File

@ -0,0 +1,25 @@
#![feature(custom_attribute, custom_derive, plugin)]
#![plugin(serde_macros)]
#[derive(Serialize)]
struct S {
#[serde(rename(serialize="x"))]
#[serde(rename(serialize="y"))] //~ ERROR: duplicate serde attribute `rename`
a: (), //~^ ERROR: duplicate serde attribute `rename`
//~^^ ERROR: duplicate serde attribute `rename`
#[serde(rename(serialize="x"))]
#[serde(rename="y")] //~ ERROR: duplicate serde attribute `rename`
b: (), //~^ ERROR: duplicate serde attribute `rename`
//~^^ ERROR: duplicate serde attribute `rename`
#[serde(rename(serialize="x"))]
#[serde(rename(deserialize="y"))] // ok
c: (),
#[serde(rename="x")]
#[serde(rename(deserialize="y"))] //~ ERROR: duplicate serde attribute `rename`
d: (), //~^ ERROR: duplicate serde attribute `rename`
} //~^^ ERROR: duplicate serde attribute `rename`
fn main() {}

View File

@ -1,13 +0,0 @@
#![feature(custom_attribute, custom_derive, plugin)]
#![plugin(serde_macros)]
#[derive(Serialize, Deserialize)]
struct S {
#[serde(rename(serialize="x"))]
#[serde(rename(serialize="y"))] //~ ERROR buldternua
#[serde(rename(deserialize="y"))] // ok
#[serde(rename="y")] // error
z: i32,
}
fn main() {}